Skip to content

Commit

Permalink
Merge pull request #65 from onesimus-wiafe/SCRUM-62-portfolio-control…
Browse files Browse the repository at this point in the history
…-and-access

Scrum 62 portfolio control and access
  • Loading branch information
onesimus-wiafe authored Aug 8, 2024
2 parents 0553be2 + 6931b5f commit 5003dab
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ public ResponseEntity<OpenOrderDTO> receiveUpdate(@RequestBody OpenOrderDTO payl

System.out.println("Received Subscription Update: " + payload);
if (payload.getExchange().equals("MAL1")){
ticker = payload.getProduct() + "_EX1";
ticker = payload.getProduct().toUpperCase() + "_EX1";
exchange = "Exchange1";
dataUpdate.put(ticker, newMarketData("exchange1", payload.getProduct()));
}
else {
ticker = payload.getProduct() + "_EX1";
ticker = payload.getProduct().toUpperCase() + "_EX1";
exchange = "Exchange2";
dataUpdate.put(ticker, newMarketData("exchange2", payload.getProduct()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.joe.trading.shared.events.Event;
import com.joe.trading.shared.nats.NatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
Expand All @@ -21,8 +22,12 @@ public class MarketDataServiceImpl implements MarketDataService {
private final RestTemplate restTemplate;
private final NatsService natsService;

private final String exchange1Url = "https://exchange.matraining.com";
private final String exchange2Url = "https://exchange2.matraining.com";
@Value("${exchange.exchange1.url}")
private String exchange1Url;
@Value("${exchange.exchange2.url}")
private String exchange2Url;
@Value("${service.market.data.url}")
private String serviceUrl;

@Autowired
public MarketDataServiceImpl(MarketDataRepo repo, RestTemplate restTemplate, NatsService natsService) {
Expand Down Expand Up @@ -112,13 +117,12 @@ public void initialSubscriptionCheck() {

List<String> subList = Arrays.asList(response.getBody());

String mdsUrl = "https://webhook.site/0aac56b5-d439-40ed-a29b-ec92896136e5";
boolean isSubbed = subList.contains(mdsUrl);
boolean isSubbed = subList.contains(serviceUrl);

if (!isSubbed) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(mdsUrl, headers);
HttpEntity<String> request = new HttpEntity<>(serviceUrl, headers);

restTemplate.exchange(
subscriptionUrl,
Expand All @@ -141,7 +145,7 @@ public void buildInitialCacheEntry() throws JsonProcessingException {
Map<String, MarketData> marketDataMap = new HashMap<>();

marketDataList.forEach(data -> {
String key = data.getTICKER() + "_" + data.getEXCHANGE();
String key = data.getTICKER().toUpperCase() + "_" + data.getEXCHANGE();
data.setTICKER(key);
marketDataMap.put(key, data);
});
Expand Down
6 changes: 6 additions & 0 deletions MarketDataService/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.endpoint.metrics.enabled=true
management.endpoint.prometheus.enabled=true


# EXCHANGE
exchange.exchange1.url=https://exchange.matraining.com
exchange.exchange2.url=https://exchange2.matraining.com
service.market.data.url=https://webhook.site/0aac56b5-d439-40ed-a29b-ec92896136e5
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,16 @@ public OrderController(OrderValidationService validationService, OrderService or
this.orderService = orderService;
}

private User authenticate(){
var auth = SecurityContextHolder.getContext().getAuthentication();
return (User) auth.getPrincipal();
}

@PostMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<OrderResponseDTO> sendOrder(@RequestBody OrderRequestDTO request) {
var auth = SecurityContextHolder.getContext().getAuthentication();
var principal = (User) auth.getPrincipal();

var principal = authenticate();

OrderResponseDTO response = new OrderResponseDTO();

Expand All @@ -51,13 +56,13 @@ public ResponseEntity<OrderResponseDTO> sendOrder(@RequestBody OrderRequestDTO r

Order order;
if (validatedRequest.getIsValidated()) {
order = buildValidatedOrder(request);
order = buildValidatedOrder(request, principal.getId());
} else {
response.setMessage("Order Validation Did Not Pass");
return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).body(response);
}

response = orderService.saveOrder(order);
response = orderService.saveOrder(order, request.getPortfolioId());

response.setMessage("New Order Created");

Expand All @@ -76,18 +81,24 @@ public ResponseEntity<Page<OrderResponseDTO>> getAllOrdersByUserId(
return ResponseEntity.ok(orderService.getAllOrdersPerUserId(userId, request));
}

@DeleteMapping("/{id}")
public ResponseEntity<OrderResponseDTO> cancelAnOrder(@PathVariable Long id) {
return ResponseEntity.ok(orderService.cancelOrder(id));
@DeleteMapping("/{orderId}")
public ResponseEntity<OrderResponseDTO> cancelAnOrder(@PathVariable Long orderId) {
var principal = authenticate();

return ResponseEntity.ok(orderService.cancelOrder(orderId, principal.getId()));
}

private Order buildValidatedOrder(OrderRequestDTO request) {
return new Order(
private Order buildValidatedOrder(OrderRequestDTO request, Long userId) {
Order order = new Order(
Ticker.valueOf(request.getTicker().toUpperCase()),
request.getQuantity(),
request.getUnitPrice(),
Side.valueOf(request.getSide().toUpperCase()),
AvailableExchanges.valueOf(request.getExchanges().toUpperCase()),
OrderType.valueOf(request.getOrderType()));

order.setUserId(userId);

return order;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class Order {
@JoinColumn(name = "order_id")
private Stock stock;

private Long userId;

public Order
(Ticker ticker, Integer quantity, Double unitPrice,
Side side, AvailableExchanges exchanges, OrderType type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.joe.trading.order_processing.entities.dto.PortfolioResponseDTO;
import com.joe.trading.order_processing.entities.enums.PortfolioState;
import com.joe.trading.order_processing.entities.enums.Ticker;
import com.joe.trading.shared.dtos.PortfolioEventDto;

import jakarta.persistence.*;
Expand All @@ -12,9 +13,7 @@
import org.hibernate.annotations.UpdateTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;

@Getter
@Setter
Expand All @@ -26,7 +25,7 @@ public class Portfolio {
private Long id;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "portfolio")
private List<Stock> stocks = new ArrayList<>();
private Set<Stock> stocks =new HashSet<>(Ticker.values().length);

@Column(name = "value", nullable = false)
private Double value = (double) 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import java.util.List;
import java.util.Map;

import com.joe.trading.order_processing.entities.Stock;
import com.joe.trading.order_processing.entities.Trade;
import com.joe.trading.order_processing.entities.enums.Side;
import org.springframework.stereotype.Service;

import com.joe.trading.order_processing.entities.OrderBook;
Expand Down Expand Up @@ -139,8 +142,8 @@ private void saveOrderBookUpdate(Map<Object, Object> message){
orderBookRepo.saveOrderBook(key, booksToCache);
}
else {
int keylen = key.length();
String cacheKey = key.substring(0, keylen-5)+"_OPEN";
int keyLen = key.length();
String cacheKey = key.substring(0, keyLen-5)+"_OPEN";
updateInternalOrders(cacheKey, key, actualMapData);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

public interface OrderService {

public OrderResponseDTO saveOrder(Order order);
OrderResponseDTO saveOrder(Order order, Long portfolioId);

public List<OrderResponseDTO> getAllOrders();
List<OrderResponseDTO> getAllOrders();

public OrderResponseDTO cancelOrder(Long id);
OrderResponseDTO cancelOrder(Long orderId, Long userId);

public Page<OrderResponseDTO> getAllOrdersPerUserId(Long userId, OrderRequestDTO request);
Page<OrderResponseDTO> getAllOrdersPerUserId(Long userId, OrderRequestDTO request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import com.joe.trading.order_processing.entities.Order;
import com.joe.trading.order_processing.entities.OrderBook;
import com.joe.trading.order_processing.entities.Stock;
import com.joe.trading.order_processing.entities.Trade;
import com.joe.trading.order_processing.entities.enums.TradeStatus;
import com.joe.trading.order_processing.repositories.jpa.OrderBookRepository;
import com.joe.trading.order_processing.repositories.jpa.OrderRepository;
import com.joe.trading.order_processing.repositories.jpa.StockRepository;
import com.joe.trading.order_processing.repositories.jpa.TradeRepository;
import com.joe.trading.order_processing.services.OrderBookService;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -18,12 +20,14 @@ public class OrderBookServiceImpl implements OrderBookService {
private final OrderBookRepository orderBookRepository;
private final TradeRepository tradeRepo;
private final OrderRepository orderRepo;
private final StockRepository stockRepo;

@Autowired
public OrderBookServiceImpl(OrderBookRepository orderBookRepository, TradeRepository tradeRepo, OrderRepository orderRepo) {
public OrderBookServiceImpl(OrderBookRepository orderBookRepository, TradeRepository tradeRepo, OrderRepository orderRepo, StockRepository stockRepo) {
this.orderBookRepository = orderBookRepository;
this.tradeRepo = tradeRepo;
this.orderRepo = orderRepo;
this.stockRepo = stockRepo;
}

@Override
Expand All @@ -45,10 +49,34 @@ public void updateOrderBooks(List<OrderBook> orderBooks) {
Trade trade = book.getTrade();
trade.setStatus(TradeStatus.CLOSED);
Order order = trade.getOrder();
order.setTradeStatus(TradeStatus.PARTIALLY_FILLED);

orderRepo.save(order);
List<Trade> trades = order.getTrades();
trades.remove(trade);

order.setTrades(trades);

long countOfClosedTrades = trades.stream().filter(trade1 -> trade1.getStatus().equals(TradeStatus.CLOSED))
.count();

if (countOfClosedTrades== trades.size()){
order.setTradeStatus(TradeStatus.CLOSED);

Stock stock = order.getStock();
Double value = stock.getStockValue();
switch (order.getSide()){
case BUY -> stock.setStockValue(value + (order.getUnitPrice()*order.getQuantity()));
case SELL -> stock.setStockValue(value - (order.getUnitPrice()*order.getQuantity()));
}
stockRepo.save(stock);
}
else {
order.setTradeStatus(TradeStatus.PARTIALLY_FILLED);
}
trades.add(trade);


tradeRepo.save(trade);
orderRepo.save(order);
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.joe.trading.order_processing.services.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.*;

import com.joe.trading.order_processing.entities.*;
import com.joe.trading.order_processing.repositories.jpa.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
Expand All @@ -20,22 +19,12 @@
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.joe.trading.order_processing.entities.Exchange;
import com.joe.trading.order_processing.entities.Order;
import com.joe.trading.order_processing.entities.OrderBook;
import com.joe.trading.order_processing.entities.Portfolio;
import com.joe.trading.order_processing.entities.Trade;
import com.joe.trading.order_processing.entities.User;
import com.joe.trading.order_processing.entities.dto.OrderRequestDTO;
import com.joe.trading.order_processing.entities.dto.OrderResponseDTO;
import com.joe.trading.order_processing.entities.enums.AvailableExchanges;
import com.joe.trading.order_processing.entities.enums.OrderType;
import com.joe.trading.order_processing.entities.enums.Side;
import com.joe.trading.order_processing.entities.enums.TradeStatus;
import com.joe.trading.order_processing.repositories.jpa.ExchangeRepository;
import com.joe.trading.order_processing.repositories.jpa.OrderRepository;
import com.joe.trading.order_processing.repositories.jpa.TradeRepository;
import com.joe.trading.order_processing.repositories.jpa.UserRepository;
import com.joe.trading.order_processing.repositories.redis.dao.InternalOpenOrderDAO;
import com.joe.trading.order_processing.repositories.redis.dao.OrderBookDAO;
import com.joe.trading.order_processing.services.OrderService;
Expand All @@ -53,21 +42,23 @@ public class OrderServiceImpl implements OrderService {
private final OrderBookDAO orderBookRepo;
private final InternalOpenOrderDAO internalOpenOrderRepo;
private final UserRepository userRepo;
private final PortfolioRepository portfolioRepo;

@Value("${exchange.private.api.key}")
String privateApiKey;

public OrderServiceImpl(RestTemplate restTemplate, OrderRepository orderRepo, ExchangeRepository exchangeRepo,
TradeRepository tradeRepo, OrderBookDAO orderBookRepo,
OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor,
InternalOpenOrderDAO internalOpenOrderRepo, UserRepository userRepo) {
TradeRepository tradeRepo, OrderBookDAO orderBookRepo,
OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor,
InternalOpenOrderDAO internalOpenOrderRepo, UserRepository userRepo, PortfolioRepository portfolioRepo) {
this.restTemplate = restTemplate;
this.orderRepo = orderRepo;
this.exchangeRepo = exchangeRepo;
this.tradeRepo = tradeRepo;
this.orderBookRepo = orderBookRepo;
this.internalOpenOrderRepo = internalOpenOrderRepo;
this.userRepo = userRepo;
this.portfolioRepo = portfolioRepo;
}

@Override
Expand All @@ -77,7 +68,11 @@ public List<OrderResponseDTO> getAllOrders() {
}

@Override
public OrderResponseDTO saveOrder(Order order) {
public OrderResponseDTO saveOrder(Order order, Long portfolioId) {

Portfolio portfolio = portfolioRepo.findById(portfolioId).orElseThrow();

Set<Stock> stocks = portfolio.getStocks();

List<Exchange> exchanges = exchangeRepo.findAll();

Expand All @@ -88,14 +83,39 @@ public OrderResponseDTO saveOrder(Order order) {

Order completedOrder = makeOrder(order, openOrders, exchanges);

return orderRepo.save(completedOrder).toOrderResponseDTO();
Stock stock = stocks.stream().filter(s -> s.getTicker().equals(order.getTicker()))
.findAny().orElse(new Stock(String.valueOf(order.getTicker()), 0));

completedOrder.setStock(stock);

Order orderMade = orderRepo.save(completedOrder);

List<Order> orders = stock.getOrders();

orders.add(orderMade);

stock.setOrders(orders);

stocks.add(stock);

portfolio.setStocks(stocks);

portfolioRepo.save(portfolio);

return orderMade.toOrderResponseDTO();
}

@Override
public OrderResponseDTO cancelOrder(Long id) {
public OrderResponseDTO cancelOrder(Long orderId, Long userId) {
OrderResponseDTO response = new OrderResponseDTO();

Order order = orderRepo.findById(id).orElseThrow();
Order order = orderRepo.findById(orderId).orElseThrow();

if (!userId.equals(order.getUserId())){
response.setMessage("User is not the owner of this order");
return response;
}


return switch (order.getTradeStatus()) {
case OPEN -> {
Expand Down
Loading

0 comments on commit 5003dab

Please sign in to comment.