private static void CreateAccounts(IAccountServiceRpcClient client) { int numThreads = 10; int numAccounts = 877; ParallelOptions opt = new ParallelOptions() { MaxDegreeOfParallelism = numThreads }; Parallel.For(0, numAccounts, opt, i => { var req = new AccountRequest { FirstName = "Anthony " + i, LastName = "Chang" }; Task <AccountResponse> resp = null; try { resp = client.CreateAccountAsync(req).ResponseAsync; resp.Wait(); Account account = resp.Result.Data; Display(account.ToString()); } catch (Exception e) { Display("An error occurred: " + e.Message); } }); }
public OrderService(IDemoExchangeDbContextFactory <OrderContext> orderContextFactory, ISubscriber subscriber, IAccountServiceRpcClient accountService) { this.orderContextFactory = orderContextFactory; this.subscriber = subscriber; this.accountService = accountService; }
private static void AccountTester(IAccountServiceRpcClient client) { var req = new CanFillOrderRequest { Order = new Order { AccountId = "6056cfb6-a4ee-4a98-b71e-c082a732c473" }, FillQuantity = 100 }; Task <CanFillOrderResponse> resp = null; try { resp = client.CanFillOrderAsync(req).ResponseAsync; resp.Wait(); bool canFill = resp.Result.Value; Display("canFill: " + canFill); } catch (Exception e) { Display("An error occurred: " + e.Message); } Task <AccountList> listResp = null; try { listResp = client.ListAsync(new Empty()).ResponseAsync; listResp.Wait(); ICollection <Account> data = listResp.Result.Accounts; data.ToList().ForEach(account => Display(account.ToString())); } catch (Exception e) { Display("An error occurred: " + e.Message); } }
public ApiGateway(IAccountServiceRpcClient accountService, IOrderServiceRpcClient orderService, IQuoteServiceRpcClient quoteService) { this.accountService = accountService; this.orderService = orderService; this.quoteService = quoteService; }
public Simulator( //IOrderTestPerfService service, IAccountServiceRpcClient accountClient, IOrderServiceRpcClient orderClient) { // this.service = service; this.accountClient = accountClient; this.orderClient = orderClient; var httpHandler = new HttpClientHandler(); httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; var channel = GrpcChannel.ForAddress("https://hela:8090", new GrpcChannelOptions { HttpHandler = httpHandler }); this.apiClient = new ErxService.ErxServiceClient(channel); }
private OrderTransactionResponseBL TryFillOrderBook(IAccountServiceRpcClient accountService) { #if PERF_FINEST long start = Now; #endif OrderBL buyOrder = BuyBook.First; OrderBL sellOrder = SellBook.First; List <OrderBL> orders = new List <OrderBL>(); List <Transaction> transactions = new List <Transaction>(); // QUESTION: Execute the limit order at sell price benefits BUYER fat finger // What to do if SELLER fat finger? Shouldn't that give the SELLER // the higher BID price? decimal executionPrice = sellOrder.StrikePrice; int fillQuantity = Math.Min(buyOrder.OpenQuantity, sellOrder.OpenQuantity); Task <CanFillOrderResponse> buyerResponse = accountService .CanFillOrderAsync(new CanFillOrderRequest { Order = buyOrder.ToMessage(), FillQuantity = fillQuantity }).ResponseAsync; Task <CanFillOrderResponse> sellerResponse = accountService .CanFillOrderAsync(new CanFillOrderRequest { Order = sellOrder.ToMessage(), FillQuantity = fillQuantity }).ResponseAsync; Task.WaitAll(buyerResponse, sellerResponse); bool canExecute = true; if (!buyerResponse.Result.Value) { BuyBook.RemoveOrder(buyOrder); buyOrder.Cancel(); orders.Add(buyOrder); canExecute = false; // TODO: Publish order has been cancelled } if (!sellerResponse.Result.Value) { SellBook.RemoveOrder(sellOrder); sellOrder.Cancel(); orders.Add(sellOrder); canExecute = false; // TODO: Publish order has been cancelled } if (!canExecute) { return(new OrderTransactionResponseBL(new OrderTransactionBL(orders, transactions))); } buyOrder.OpenQuantity -= fillQuantity; sellOrder.OpenQuantity -= fillQuantity; Transaction transaction = new Transaction(buyOrder, sellOrder, buyOrder.Ticker, fillQuantity, executionPrice); transactions.Add(transaction); orders.Add(SellBook.First); if (SellBook.First.IsFilled) { OrderBL filledOrder = SellBook.First; SellBook.RemoveOrder(filledOrder); filledOrder.Complete(); } orders.Add(BuyBook.First); if (BuyBook.First.IsFilled) { OrderBL filledOrder = BuyBook.First; BuyBook.RemoveOrder(filledOrder); filledOrder.Complete(); } #if PERF_FINEST Logger.Here().Information(String.Format("TryFillOrderBook executed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(new OrderTransactionResponseBL(new OrderTransactionBL(orders, transactions))); }
private OrderTransactionResponseBL FillMarketOrder(IAccountServiceRpcClient accountService, OrderBL order, OrderBook book) { #if PERF_FINEST long start = Now; #endif CheckArgument(!order.Action.Equals(book.Type), "Error: Wrong book"); bool done = false; List <OrderBL> orders = new List <OrderBL>(); List <Transaction> transactions = new List <Transaction>(); while (!done) { if (book.IsEmpty) { throw new SystemException("Order book is empty"); // TODO: Handle order book is empty } OrderBL buyOrder = order.IsBuyOrder ? order : book.First; OrderBL sellOrder = order.IsSellOrder ? order : book.First; decimal executionPrice = order.IsBuyOrder ? sellOrder.StrikePrice : buyOrder.StrikePrice; int fillQuantity = Math.Min(buyOrder.OpenQuantity, sellOrder.OpenQuantity); // QUESTION: Many issues here, partial fill, etc Task <CanFillOrderResponse> buyerResponse = accountService .CanFillOrderAsync(new CanFillOrderRequest { Order = buyOrder.ToMessage(), FillQuantity = fillQuantity }).ResponseAsync; Task <CanFillOrderResponse> sellerResponse = accountService .CanFillOrderAsync(new CanFillOrderRequest { Order = sellOrder.ToMessage(), FillQuantity = fillQuantity }).ResponseAsync; Task.WaitAll(buyerResponse, sellerResponse); bool canExecute = true; if (!buyerResponse.Result.Value) { if (!order.IsBuyOrder) { BuyBook.RemoveOrder(buyOrder); } buyOrder.Cancel(); orders.Add(buyOrder); canExecute = false; // TODO: Publish order has been cancelled } if (!sellerResponse.Result.Value) { if (!order.IsSellOrder) { SellBook.RemoveOrder(sellOrder); } sellOrder.Cancel(); orders.Add(sellOrder); canExecute = false; // TODO: Publish order has been cancelled } if (!order.IsOpen) { return(new OrderTransactionResponseBL(new OrderTransactionBL(orders, transactions))); } if (!canExecute) { continue; } buyOrder.OpenQuantity -= fillQuantity; sellOrder.OpenQuantity -= fillQuantity; Transaction transaction = new Transaction(buyOrder, sellOrder, buyOrder.Ticker, fillQuantity, executionPrice); transactions.Add(transaction); orders.Add(book.First); if (book.First.IsFilled) { OrderBL filledOrder = book.First; book.RemoveOrder(filledOrder); filledOrder.Complete(); } if (order.IsFilled) { order.Complete(); orders.Add(order); done = true; } } #if PERF_FINEST Logger.Here().Information(String.Format("Market order executed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(new OrderTransactionResponseBL(new OrderTransactionBL(orders, transactions))); }
// QUESTION: Consider making async: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap public OrderResponseBL SubmitOrder(IOrderContext context, ISubscriber subscriber, IAccountServiceRpcClient accountService, OrderBL order) { #if (PERF || PERF_FINE || PERF_FINEST) long start = Now; #endif OrderResponseBL insertResponse = InsertOrder(context, subscriber, order); if (insertResponse.HasErrors) { return(insertResponse); } OrderBook book = order.IsMarketOrder ? order.IsBuyOrder ? SellBook : BuyBook : order.IsBuyOrder ? BuyBook : SellBook; if (order.IsMarketOrder) { // QUESTION: Could potentially optimize to not do an initial save of a Market order // but can we allow order entry during market close hours? What's MarketOnOpen? // The actual outcome of the match will be responded back asynchronously OrderTransactionResponseBL fillResponse = FillMarketOrder(accountService, order, book); if (fillResponse.HasErrors) { return(insertResponse); } fillResponse = SaveOrderTransaction(context, subscriber, fillResponse.Data); if (fillResponse.HasErrors) { return(insertResponse); } #if DIAGNOSTICS DiagnosticsWriteDetails(fillResponse.Data); #endif #if (PERF || PERF_FINE || PERF_FINEST) Logger.Here().Information(String.Format("SubmitOrder: Market order executed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(new OrderResponseBL(order)); } book.AddOrder(order); bool done = false; while (!done) { if (BuyBook.IsEmpty || SellBook.IsEmpty) { throw new SystemException("Order book is empty"); // TODO: Handle order book is empty } // The new order triggered a match // We'll try to match it here, but respond back with the insertResponse regardless // The actual outcome of the match will be responded back asynchronously if (BuyBook.First.StrikePrice >= SellBook.First.StrikePrice) { OrderTransactionResponseBL fillResponse = TryFillOrderBook(accountService); if (fillResponse.HasErrors) { return(insertResponse); } fillResponse = SaveOrderTransaction(context, subscriber, fillResponse.Data); if (fillResponse.HasErrors) { return(insertResponse); } #if DIAGNOSTICS DiagnosticsWriteDetails(fillResponse.Data); #endif } else { done = true; } } #if (PERF || PERF_FINE || PERF_FINEST) Logger.Here().Information(String.Format("SubmitOrder: Processed in {0} milliseconds", ((Now - start) / TimeSpan.TicksPerMillisecond))); #endif return(insertResponse); }