package io.pivotal.portfolio.service; import java.math.BigDecimal; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import io.pivotal.portfolio.domain.Holding; import io.pivotal.portfolio.domain.Order; import io.pivotal.portfolio.domain.OrderType; import io.pivotal.portfolio.domain.Portfolio; import io.pivotal.portfolio.domain.Quote; import io.pivotal.portfolio.repository.OrderRepository; /** * Manages a portfolio of holdings of stock/shares. * * @author David Ferreira Pinto * */ @Service @RefreshScope public class PortfolioService { private static final Logger logger = LoggerFactory.getLogger(PortfolioService.class); /** * The order repository to store Order objects. */ @Autowired OrderRepository repository; /** * The service than handles the calls to get quotes. */ @Autowired QuoteRemoteCallService quoteService; @Autowired @LoadBalanced private RestTemplate restTemplate; // @Value("${pivotal.quotesService.name}") // protected String quotesService; @Value("${pivotal.accountsService.name}") protected String accountsService; /** * Retrieves the portfolio for the given accountId. * * @param accountId * The account id to retrieve for. * @return The portfolio. */ public Portfolio getPortfolio(String accountId) { /* * Retrieve all orders for accounts id and build portfolio. - for each * order create holding. - for each holding find current price. */ logger.debug("Getting portfolio for accountId: " + accountId); List<Order> orders = repository.findByAccountId(accountId); Portfolio folio = new Portfolio(); folio.setAccountId(accountId); return createPortfolio(folio, orders); } /** * Builds a portfolio object with the list of orders. * * @param portfolio * the portfolio object to build. * @param orders * the list of orders. * @return the portfolio object */ private Portfolio createPortfolio(Portfolio portfolio, List<Order> orders) { // TODO: change to forEach() and maybe in parallel? Set<String> symbols = new HashSet<>(); Holding holding = null; for (Order order : orders) { holding = portfolio.getHolding(order.getSymbol()); if (holding == null) { holding = new Holding(); holding.setSymbol(order.getSymbol()); portfolio.addHolding(holding); symbols.add(order.getSymbol()); } holding.addOrder(order); } List<Quote> quotes = quoteService.getQuotes(symbols); for (Quote quote : quotes) { portfolio.getHolding(quote.getSymbol()).setCurrentValue(quote.getLastPrice()); } portfolio.refreshTotalValue(); logger.debug("Portfolio: " + portfolio); return portfolio; } /** * Add an order to the repository and modify account balance. * * @param order * the order to add. * @return the saved order. */ @Transactional public Order addOrder(Order order) { logger.debug("Adding order: " + order); if (order.getOrderFee() == null) { order.setOrderFee(Order.DEFAULT_ORDER_FEE); logger.debug("Adding Fee to order: " + order); } if (order.getOrderType() == OrderType.BUY) { double amount = order.getQuantity() * order.getPrice().doubleValue() + order.getOrderFee().doubleValue(); ResponseEntity<Double> result = restTemplate.getForEntity("http://" + accountsService + "/accounts/{userid}/decreaseBalance/{amount}", Double.class, order.getAccountId(), amount); if (result.getStatusCode() == HttpStatus.OK) { logger.info(String .format("Account funds updated successfully for account: %s and new funds are: %s", order.getAccountId(), result.getBody())); return repository.save(order); } else { // TODO: throw exception - not enough funds! // SK - Whats the expected behaviour? logger.warn("PortfolioService:addOrder - decresing balance HTTP not ok: "); return null; } } else { double amount = order.getQuantity() * order.getPrice().doubleValue() - order.getOrderFee().doubleValue(); ResponseEntity<Double> result = restTemplate.getForEntity("http://" + accountsService + "/accounts/{userid}/increaseBalance/{amount}", Double.class, order.getAccountId(), amount); if (result.getStatusCode() == HttpStatus.OK) { logger.info(String .format("Account funds updated successfully for account: %s and new funds are: %s", order.getAccountId(), result.getBody())); return repository.save(order); } else { // TODO: throw exception - negative value??? logger.warn("PortfolioService:addOrder - increasing balance HTTP not ok: "); return null; } } } }