/// <summary> /// Updates the status of the Destination Order. /// </summary> /// <param name="destinationOrderRow">The Working Order to be modified.</param> /// <param name="statusCode">The new status of the Working Order.</param> internal static void UpdateDestinationOrderStatus(DataModel.DestinationOrderRow destinationOrderRow, StatusCode statusCode) { // This will update only the status of the given destination order. DataModel.TenantDataModel.UpdateDestinationOrder( null, null, null, null, null, null, new Object[] { destinationOrderRow.DestinationOrderId }, null, null, null, null, null, null, null, null, destinationOrderRow.RowVersion, null, null, null, null, null, statusCode, null, null, null, null, null); }
/// <summary> /// Handler for validating Destination Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> static void OnDestinationOrderAdd(DataModel.DestinationOrderRow destinationOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; DataModel.WorkingOrderRow workingOrderRow = destinationOrderRow.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); Decimal sourceOrderQuantity = WorkingOrderService.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); Decimal destinationOrderQuantity = WorkingOrderService.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } }
/// <summary> /// Handler for validating Destination Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> static void OnDestinationOrderDelete(DataModel.DestinationOrderRow destinationOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // If the StatusCode of the destination order has changed then find the right handler for the state change and go execute the business logic for this // change. The 'statusChangeMap' is a two dimensional Dictionary (hash table) using the previous and current states to find the right handler. Note // that on a deletion, we don't have easy access to the original working order, so we need to extract it from the deleted record. StatusCode previousStatusCode = (StatusCode)destinationOrderRow[DataModel.DestinationOrder.StatusCodeColumn, DataRowVersion.Original]; StatusCode currentStatusCode = StatusCode.Deleted; Guid workingOrderId = (Guid)destinationOrderRow[DataModel.DestinationOrder.WorkingOrderIdColumn, DataRowVersion.Original]; DataModel.WorkingOrderRow workingOrderRow = DataModel.WorkingOrder.WorkingOrderKey.Find(new Object[] { workingOrderId }); if (workingOrderRow != null) { statusChangeMap[previousStatusCode][currentStatusCode](workingOrderRow); } }
/// <summary> /// Handles a change to the DestinationOrder table. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event data.</param> static void OnDestinationOrderRowChanged(object sender, DataModel.DestinationOrderRowChangeEventArgs e) { // This will turn new DestinationOrder records into orders that can be sent to a destination for execution. if (e.Action == DataRowAction.Add) { // The current transaction is going to be needed to lock records. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // Create a new message for the order we're going to build from the DestinationOrder. Message message = new Message(); // The market execution engine will need to know the source firm so it knows how to route the order back. OrganizationPrincipal organizationPrincipal = Thread.CurrentPrincipal as OrganizationPrincipal; message.SenderCompID = organizationPrincipal.Organization; // Copy the basic properties of the DestinationOrder into the message. DataModel.DestinationOrderRow destinationOrderRow = e.Row; message.ClOrdID = destinationOrderRow.DestinationOrderId.ToString(); message.OrderQty = destinationOrderRow.OrderedQuantity; message.OrdType = destinationOrderRow.OrderTypeCode; message.SideCode = destinationOrderRow.SideCode; message.TimeInForceCode = destinationOrderRow.TimeInForceCode; // Get the symbol to use as a security identifier. DataModel.SecurityRow securityRow = destinationOrderRow.SecurityRowByFK_Security_DestinationOrder_SecurityId; securityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(securityRow); if (securityRow.RowState == DataRowState.Detached) { throw new FaultException <RecordNotFoundFault>(new RecordNotFoundFault("Security", new Object[] { destinationOrderRow.SecurityId })); } message.Symbol = securityRow.Symbol; // This will put the new order in a queue. The DestinatonThread will pull it out, batch it up and send it to the destination to be executed. lock (MarketEngine.syncRoot) { MarketEngine.messageQueue.Enqueue(message); if (messageQueue.Count == 1) { MarketEngine.orderEvent.Set(); } } } }
/// <summary> /// Handles the deletion of a Execution row. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="executionRowChangeEventArgs">The event arguments.</param> void OnExecutionRowDeleted(Object sender, DataModel.ExecutionRowChangeEventArgs executionRowChangeEventArgs) { // We're only interested in deletes that affect the WorkingOrder records in this blotter. if (executionRowChangeEventArgs.Action == DataRowAction.Delete) { // Filtering requires a little more work with a deleted record. We need to first find the original record and then look up the destination and // working order to which it belonged. We don't need to check for the existence of the destination order or working order as we know that this // execution was just deleted and, for it to have existed in the first place, there must have been a parent destination and working order. DataModel.ExecutionRow executionRow = executionRowChangeEventArgs.Row; Guid destinationOrderId = (Guid)executionRow[DataModel.Execution.DestinationOrderIdColumn, DataRowVersion.Original]; DataModel.DestinationOrderRow destinationOrderRow = DataModel.DestinationOrder.DestinationOrderKey.Find(destinationOrderId); DataModel.WorkingOrderRow workingOrderRow = destinationOrderRow.WorkingOrderRow; if (this.blotterIdSet.Contains(workingOrderRow.BlotterId)) { // Once the quantity has been removed from the totals, the percent executed can be updated. this.ExecutionQuantity -= (Decimal)executionRow[DataModel.Execution.ExecutionQuantityColumn, DataRowVersion.Original]; this.ExecutedPercent = this.DestinationOrderQuantity == 0.0m ? 0.0 : Convert.ToDouble(this.ExecutionQuantity / this.DestinationOrderQuantity); } } }
/// <summary> /// Handles a change to the Execution row. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="executionRowChangeEventArgs">The event arguments.</param> void OnExecutionRowChanged(Object sender, DataModel.ExecutionRowChangeEventArgs executionRowChangeEventArgs) { // We're only interested in additions and changes that affect the WorkingOrder records in this blotter. if (executionRowChangeEventArgs.Action == DataRowAction.Add || executionRowChangeEventArgs.Action == DataRowAction.Change) { // This is designed to filter out all events that don't pertain to this blotter. DataModel.ExecutionRow executionRow = executionRowChangeEventArgs.Row; DataModel.DestinationOrderRow destinationOrderRow = executionRow.DestinationOrderRow; DataModel.WorkingOrderRow workingOrderRow = destinationOrderRow.WorkingOrderRow; if (this.blotterIdSet.Contains(workingOrderRow.BlotterId)) { // Once the previous execution is subtracted and the current execution added to the totals we can calculate the percent executed. if (executionRow.HasVersion(DataRowVersion.Original)) { this.executionQuantityField -= (Decimal)executionRow[DataModel.Execution.ExecutionQuantityColumn, DataRowVersion.Original]; } this.ExecutionQuantity += executionRow.ExecutionQuantity; this.ExecutedPercent = this.DestinationOrderQuantity == 0.0m ? 0.0 : Convert.ToDouble(this.ExecutionQuantity / this.DestinationOrderQuantity); } } }
/// <summary> /// Handler for validating Destination Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> static void OnDestinationOrderChange(DataModel.DestinationOrderRow destinationOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // If the quantity has changed then we need to make sure that the quantity sent to a destination (broker, exchange, etc.) is not less than the amount // ordered. That is, we can't accept a change that leaves us overcommited with a destination. Decimal originalQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Original]; Decimal currentQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Current]; if (originalQuantity < currentQuantity) { // We need to aggregate at the working order, so we need to lock the working order while we do our sums. DataModel.WorkingOrderRow workingOrderRow = destinationOrderRow.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); // This will aggregate the source and destination orders and throw an exception if this transaction would leave us overcommitted with the // destination. Decimal sourceOrderQuantity = WorkingOrderService.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); Decimal destinationOrderQuantity = WorkingOrderService.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } } // If the StatusCode of the destination order has changed then find the right handler for the state change and go execute the business logic for this // change. The 'statusChangeMap' is a two dimensional Dictionary (hash table) using the previous and current states to find the right handler. StatusCode previousStatusCode = (StatusCode)destinationOrderRow[DataModel.DestinationOrder.StatusCodeColumn, DataRowVersion.Original]; StatusCode currentStatusCode = destinationOrderRow.StatusCode; if (previousStatusCode != currentStatusCode) { statusChangeMap[previousStatusCode][currentStatusCode](destinationOrderRow.WorkingOrderRow); } }
/// <summary> /// Do nothing. /// </summary> /// <param name="workingOrderRow">The source order row that was changed.</param> static void OnDoNothing(DataModel.DestinationOrderRow destinationOrderRow) { }
/// <summary> /// Creates orders in the simulated order book. /// </summary> private static void OrderHandler() { // This thread will create orders in the simulated order book from destination orders placed in the queue. This thread // is necessary due to the locking architecture that prevents accessing the data model during a commit operation (which // is where the event indicating a new or changed destination order originates). while (MarketSimulator.IsBrokerSimulatorThreadRunning) { // This thread will wait here until a destination order is available in the queue. DataModel.DestinationOrderRow destinationOrderRow = MarketSimulator.orderQueue.Dequeue(); // The code that adds an order to the simulated book must have exclusive access to the simulated data model. lock (MarketSimulator.syncRoot) { // The primary data model needs to be accessed also for ancillary data associated with the new order. using (TransactionScope transactionScope = new TransactionScope()) { try { // This context is used to keep track of the locks aquired for the ancillary data. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // Lock the destination order. destinationOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(destinationOrderRow); // Lock the Security. DataModel.SecurityRow securityRow = destinationOrderRow.SecurityRowByFK_Security_DestinationOrder_SecurityId; securityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(securityRow); // The working order row must be locked to examine the flags DataModel.WorkingOrderRow workingOrderRow = destinationOrderRow.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); // Lock the Entity. DataModel.EntityRow entityRow = securityRow.EntityRow; entityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(entityRow); // Calculate the quantity executed on this order (some orders are created with executions, such as crossed orders.) Decimal quantityExecuted = 0.0M; foreach (DataModel.ExecutionRow executionRow in destinationOrderRow.GetExecutionRows()) { executionRow.AcquireReaderLock(dataModelTransaction); dataModelTransaction.AddLock(executionRow); quantityExecuted += executionRow.ExecutionQuantity; } // The simulated order is added to the book. This collects all the information required to simulate the execution of this order. DataSetMarket.OrderRow orderRow = MarketSimulator.dataSetMarket.Order.NewOrderRow(); orderRow.BlotterId = destinationOrderRow.BlotterId; orderRow.DestinationOrderId = destinationOrderRow.DestinationOrderId; orderRow.OrderTypeCode = OrderTypeMap.FromId(destinationOrderRow.OrderTypeId); orderRow.QuantityOrdered = destinationOrderRow.OrderedQuantity; orderRow.QuantityExecuted = quantityExecuted; orderRow.SideCode = SideMap.FromId(destinationOrderRow.SideId); orderRow.Symbol = securityRow.Symbol; orderRow.TimeInForceCode = TimeInForceMap.FromId(destinationOrderRow.TimeInForceId); MarketSimulator.dataSetMarket.Order.AddOrderRow(orderRow); // Adding the first order to the simulated order book will enable the simulation thread to begin processing the orders. The order // simulation thread will continue until the book is filled. if (MarketSimulator.dataSetMarket.Order.Count == 1) { MarketSimulator.orderEvent.Set(); } } catch { } // The locks are no longer required. transactionScope.Complete(); } } } }