/** * Copyright 2011 Archfirst * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.archfirst.bfoms.domain.account.brokerage; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import org.archfirst.bfoms.domain.account.BaseAccountRepository; import org.archfirst.bfoms.domain.account.InvalidSymbolException; import org.archfirst.bfoms.domain.account.Transaction; import org.archfirst.bfoms.domain.account.TransactionCriteria; import org.archfirst.bfoms.domain.account.TransactionCriteriaInternal; import org.archfirst.bfoms.domain.account.TransactionSummaryV2; import org.archfirst.bfoms.domain.account.brokerage.order.ExecutionReport; import org.archfirst.bfoms.domain.account.brokerage.order.Order; import org.archfirst.bfoms.domain.account.brokerage.order.OrderCriteria; import org.archfirst.bfoms.domain.account.brokerage.order.OrderCriteriaInternal; import org.archfirst.bfoms.domain.account.brokerage.order.OrderEstimate; import org.archfirst.bfoms.domain.account.brokerage.order.OrderEventPublisher; import org.archfirst.bfoms.domain.account.brokerage.order.OrderParams; import org.archfirst.bfoms.domain.account.brokerage.order.OrderStatus; import org.archfirst.bfoms.domain.exchange.ExchangeTradingService; import org.archfirst.bfoms.domain.marketdata.MarketDataService; import org.archfirst.bfoms.domain.referencedata.ReferenceDataService; import org.archfirst.bfoms.domain.security.AuthorizationException; import org.archfirst.bfoms.domain.security.User; import org.archfirst.bfoms.domain.security.UserRepository; import org.archfirst.common.quantity.DecimalQuantity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * BrokerageAccountService * * @author Naresh Bhatia */ public class BrokerageAccountService { private static final Logger logger = LoggerFactory.getLogger(BrokerageAccountService.class); // ----- Commands ----- public Long openNewAccount(String username, String accountName) { User user = getUser(username); BrokerageAccount account = brokerageAccountFactory.createIndividualAccountWithFullAccess( accountName, user.getPerson(), user); return account.getId(); } public Long placeOrder(String username, Long accountId, OrderParams params) { logger.debug("Place order in account {}: {}", accountId, params); // Check authorization on account BrokerageAccount account = checkAccountAuthorization( getUser(username), accountId, BrokerageAccountPermission.Trade); // Check if the symbol is valid if (this.referenceDataService.lookup(params.getSymbol()) == null) { throw new InvalidSymbolException(); } // Place order Order order = account.placeOrder(params, marketDataService); exchangeTradingService.placeOrder(order); return order.getId(); } public void cancelOrder(String username, Long orderId) { // Find the order Order order = brokerageAccountRepository.findOrder(orderId); if (order == null) { throw new RuntimeException("Order " + orderId + " not found"); } // Check authorization on account checkAccountAuthorization( getUser(username), order.getAccount().getId(), BrokerageAccountPermission.Trade); // Cancel order order.pendingCancel(orderEventPublisher); exchangeTradingService.cancelOrder(order); } public void processExecutionReport(ExecutionReport executionReport) { // Send to account for processing BrokerageAccount account = brokerageAccountRepository.findAccountForOrder( executionReport.getClientOrderId()); account.processExecutionReport(executionReport); } public void processOrderCancelReject(Long orderId, OrderStatus newStatus) { // Get the order Order order = this.findOrder(orderId); if (order == null) { logger.error("OrderCancelReject: order {} not found", orderId); } // Send the new status to the order order.cancelRequestRejected(newStatus, orderEventPublisher); } // ----- Queries ----- public BrokerageAccount findAccount(Long id) { return brokerageAccountRepository.findAccount(id); } public List<Lot> findActiveLots(Long accountId) { return brokerageAccountRepository.findActiveLots(findAccount(accountId)); } public DecimalQuantity getNumberOfShares(Long accountId, String symbol) { return brokerageAccountRepository.getNumberOfShares( findAccount(accountId), symbol); } public Order findOrder(Long id) { return brokerageAccountRepository.findOrder(id); } public List<BrokerageAccountSummary> getAccountSummaries(String username) { User user = getUser(username); // Get a list of viewable accounts List<BrokerageAccount> viewableAccounts = brokerageAccountRepository.findAccountsWithPermission( user, BrokerageAccountPermission.View); // Calculate account summaries List<BrokerageAccountSummary> accountSummaries = new ArrayList<BrokerageAccountSummary>(); for (BrokerageAccount account : viewableAccounts) { accountSummaries.add(account.getAccountSummary( user, referenceDataService, marketDataService)); } return accountSummaries; } public BrokerageAccountSummary getAccountSummary(String username, Long accountId) { BrokerageAccount account = this.findAccount(accountId); return account.getAccountSummary( getUser(username), referenceDataService, marketDataService); } public List<Order> getOrders(String username, OrderCriteria criteria) { logger.debug("Get orders: {}", criteria); List<Long> accountIds = new ArrayList<Long>(); if (criteria.getAccountId() == null) { // Get a list of viewable accounts List<BrokerageAccount> viewableAccounts = brokerageAccountRepository.findAccountsWithPermission( getUser(username), BrokerageAccountPermission.View); for (BrokerageAccount account: viewableAccounts) { accountIds.add(account.getId()); } } else { // Check authorization on the specified account checkAccountAuthorization( getUser(username), criteria.getAccountId(), BrokerageAccountPermission.View); accountIds.add(criteria.getAccountId()); } OrderCriteriaInternal criteriaInternal = new OrderCriteriaInternal(criteria, accountIds); return brokerageAccountRepository.findOrders(criteriaInternal); } public OrderEstimate getOrderEstimate( String username, Long brokerageAccountId, OrderParams params) { // Check authorization on account BrokerageAccount account = checkAccountAuthorization( getUser(username), brokerageAccountId, BrokerageAccountPermission.Trade); return account.calculateOrderEstimate(params, marketDataService); } /** * This version returns transactions for brokerage accounts only, but allows * multiple brokerage accounts and has tighter security checks. */ public List<TransactionSummaryV2> getTransactionSummaries( String username, TransactionCriteria criteria) { logger.debug("Get transactions: {}", criteria); List<Long> accountIds = new ArrayList<Long>(); if (criteria.getAccountId() == null) { // Get a list of viewable accounts List<BrokerageAccount> viewableAccounts = brokerageAccountRepository.findAccountsWithPermission( getUser(username), BrokerageAccountPermission.View); for (BrokerageAccount account: viewableAccounts) { accountIds.add(account.getId()); } } else { // Check authorization on the specified account checkAccountAuthorization( getUser(username), criteria.getAccountId(), BrokerageAccountPermission.View); accountIds.add(criteria.getAccountId()); } TransactionCriteriaInternal criteriaInternal = new TransactionCriteriaInternal(criteria, accountIds); List<Transaction> transactions = baseAccountRepository.findTransactions(criteriaInternal); List<TransactionSummaryV2> summaries = new ArrayList<TransactionSummaryV2>(); for (Transaction transaction : transactions) { summaries.add(new TransactionSummaryV2(transaction)); } return summaries; } // ----- Helpers ----- private BrokerageAccount checkAccountAuthorization( User user, Long accountId, BrokerageAccountPermission requiredPermission) { if (accountId == null) throw new AuthorizationException(); BrokerageAccount account = brokerageAccountRepository.findAccount(accountId); checkAccountAuthorizationHelper(user, account, requiredPermission); return account; } private void checkAccountAuthorizationHelper( User user, BrokerageAccount account, BrokerageAccountPermission requiredPermission) { List<BrokerageAccountPermission> permissions = brokerageAccountRepository.findPermissionsForAccount(user, account); if (!permissions.contains(requiredPermission)) { throw new AuthorizationException(); } } private User getUser(String username) { return userRepository.findUser(username); } // ----- Attributes ----- @Inject private BrokerageAccountFactory brokerageAccountFactory; @Inject private BaseAccountRepository baseAccountRepository; @Inject private BrokerageAccountRepository brokerageAccountRepository; @Inject private MarketDataService marketDataService; @Inject private ReferenceDataService referenceDataService; @Inject private ExchangeTradingService exchangeTradingService; @Inject private UserRepository userRepository; @Inject private OrderEventPublisher orderEventPublisher; }