/// <summary> /// Simulation of brokers executing destination orders. /// </summary> static void SimulateBroker(Object state) { // This defines the operating properties of this broker simulation. BrokerContext brokerContext = state as BrokerContext; // Run this until shut down from an external thread. while (true) { // This will wait until there is an order to handle. BrokerSimulator.orderEvent.WaitOne(); try { // Access to the market data must be exclusive while we find and order and process it. Monitor.Enter(MarketData.SyncRoot); // This will put the thread to sleep until some event comes along (like a new order) that requires its attension. The main idea is that we // don't want to leech the CPU while waiting for orders to simulate. if (MarketData.Order.Rows.Count == 0) { BrokerSimulator.orderEvent.Reset(); continue; } // This will grab a random order and see if it can be claimed by this broker. If the order isn't available, then we'll allow other threads to // try processing the order. We'll keep this up until either an order is available (and not claimed by another simulated broker) or until we've // exhausted the order book. Int32 orderIndex = brokerContext.Random.Next(MarketData.Order.Rows.Count); MarketDataModel.OrderRow orderRow = MarketData.Order[orderIndex]; if (!orderRow.IsBrokerIdNull() && orderRow.BrokerId != brokerContext.Symbol) { Monitor.Exit(MarketData.SyncRoot); Thread.Sleep(0); Monitor.Enter(MarketData.SyncRoot); continue; } // This broker has now taken possession of the order. No one else will touch it. if (orderRow.IsBrokerIdNull()) { orderRow.BrokerId = brokerContext.Symbol; } // This will select a random action to perform on this order. Int32 orderActionIndex = brokerContext.Random.Next(Enum.GetValues(typeof(OrderAction)).Length); BrokerSimulator.actionMap[(OrderAction)Enum.GetValues(typeof(OrderAction)).GetValue(orderActionIndex)](brokerContext, orderRow); } finally { Monitor.Exit(MarketData.SyncRoot); } // This allows other threads the chance to run. Note that the broker simulation will run flat out until the order book has been exhausted. We // should eventually put a parameter on this to slow it down in order to demonstrate a more realistic feed. Thread.Sleep(Convert.ToInt32(BrokerSimulator.ExecutionFrequency)); } }
/// <summary> /// Simulates the execution of an order. /// </summary> /// <param name="orderRow">The order to be executed.</param> static void ExecuteOrder(BrokerContext brokerContext, MarketDataModel.OrderRow orderRow) { // Create an execution. MarketDataModel.ExecutionRow executionRow = MarketData.Execution.NewExecutionRow(); executionRow.ExecutionId = Guid.NewGuid(); executionRow.OrderId = orderRow.OrderId; // This execution needs a random number of shares to fill. We can't overcommit the order, so first we need to count up how many shares have already // been executed on this order. Then we can calculated the 'leaves' quantity (the amount remaining to be executed). Decimal executedQuantity = 0.0m; foreach (MarketDataModel.ExecutionRow childExecutionRow in orderRow.GetExecutionRows()) { executedQuantity += childExecutionRow.Quantity; } Decimal leavesQuantity = orderRow.QuantityOrdered - executedQuantity; // This will generate either a random fill or finish the order with an odd lot. executionRow.Quantity = Math.Min(leavesQuantity, brokerContext.Random.Next(1, 100) * 100); // The execution requires a price. The price simulator will provide a bid price or ask price depending on the side of the order. MarketDataModel.PriceRow priceRow = MarketData.Price.FindByFeedSymbol("US TICKER", orderRow.Symbol); SideCode sideCode = (SideCode)orderRow.SideCode; executionRow.Price = 0.0m; if (priceRow != null) { executionRow.Price = sideCode == SideCode.Buy || sideCode == SideCode.BuyCover ? priceRow.BidPrice : priceRow.AskPrice; } // We're record this execution in the broker's book. MarketData.Execution.AddExecutionRow(executionRow); // Now that the order is recorded, we can update the 'leaves' to reflect the execution. leavesQuantity -= executionRow.Quantity; // This message is sent back to the customer to record the execution. Note that we pick the web service to use from a mapping table. This table // associates the name of the firm with a preconfigured web service to communicate with that firm. Message message = new Message(); message.ExecBroker = brokerContext.Symbol; message.Symbol = orderRow.Symbol; message.SenderCompID = orderRow.Source; message.Price = executionRow.Price; message.OrderID = orderRow.OrderId.ToString(); message.OrderStatusCode = leavesQuantity == 0.0M ? OrderStatusCode.Filled : OrderStatusCode.PartiallyFilled; message.CumQty = executionRow.Quantity; message.ClOrdID = orderRow.CustomerOrderId; message.LeavesQty = leavesQuantity; // If this order is filled then remove it from the simulated order book. If the order book is empty, then put the broker threads to sleep. if (leavesQuantity == 0) { MarketData.Order.RemoveOrderRow(orderRow); if (MarketData.Order.Count == 0) { BrokerSimulator.orderEvent.Reset(); } } // This places the message in a queue of messages owned by the current broker that will route the message back to the source of the order. brokerContext.SendReport(message.SenderCompID, message); }