/// <summary> /// Handles a change to a cancelled state. /// </summary> /// <param name="workingOrderRow">The parent working order.</param> static void OnCanceledAction(DataModel.WorkingOrderRow workingOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // This will lock the WorkingOrderRow while we examine it. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // When a destination order is canceled we will return the order to its previous state (as determined by aggregating the executed and destination // quantities). The only exception to this is when the working order is in the error state. if (workingOrderRow.StatusCode != StatusCode.Error) { Decimal quantityExecuted = WorkingOrderService.GetExecutionQuantity(dataModelTransaction, workingOrderRow); Decimal quantityOrdered = WorkingOrderService.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); if (quantityExecuted == 0.0M && workingOrderRow.StatusCode != StatusCode.New) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.New); } if (0.0M < quantityExecuted && quantityExecuted < quantityOrdered && workingOrderRow.StatusCode != StatusCode.PartiallyFilled) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.PartiallyFilled); } } }
/// <summary> /// Handles a change to a filled state. /// </summary> /// <param name="workingOrderRow">The parent working order.</param> static void OnFilledAction(DataModel.WorkingOrderRow workingOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // This will lock the WorkingOrderRow while we examine it. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // This will mark the working order as filled when the quantity executed is the same as the quantity ordered. The only exception to this is when ng // the workiorder is in an error state. if (workingOrderRow.StatusCode != StatusCode.Error) { Decimal quantityOrdered = WorkingOrderService.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); Decimal quantityExecuted = WorkingOrderService.GetExecutionQuantity(dataModelTransaction, workingOrderRow); if (quantityOrdered == quantityExecuted) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.Filled); } } }
/// <summary> /// Validates the source order row when it is deleted. /// </summary> /// <param name="sourceOrderRow">The source order row that was deleted.</param> static void OnSourceOrderDelete(DataModel.SourceOrderRow sourceOrderRow) { // We'll need to add several rows to the transaction as we validate the source order deletion. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // There is no implicit locking mechanism for deleted rows, so we need to lock the record manually as we access the parent working order of the deleted // source order. DataModel.WorkingOrderRow workingOrderRow = null; try { DataModel.DataLock.EnterReadLock(); workingOrderRow = ((DataModel.WorkingOrderRow)(sourceOrderRow.GetParentRow(DataModel.SourceOrder.WorkingOrderSourceOrderRelation, DataRowVersion.Original))); } finally { DataModel.DataLock.ExitReadLock(); } workingOrderRow.AcquireReaderLock(dataModelTransaction); // This will insure that the quantity ordered doesn't fall below the quantity placed with brokers and exchanges. 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> /// Validates the source order row when it changes. /// </summary> /// <param name="sourceOrderRow">The source order row that was changed.</param> static void OnSourceOrderChange(DataModel.SourceOrderRow sourceOrderRow) { // We can't allow the quantity of source order shares to drop below the quantity of destination order shares. We only need to check for this condition // when the quantity of the source order record has changed. Decimal originalQuantity = (Decimal)sourceOrderRow[DataModel.SourceOrder.OrderedQuantityColumn, DataRowVersion.Original]; Decimal currentQuantity = (Decimal)sourceOrderRow[DataModel.SourceOrder.OrderedQuantityColumn, DataRowVersion.Current]; if (originalQuantity < currentQuantity) { // We'll need to lock several records in order to check source order and destination order totals. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // Get the working order associated with this source order and lock it. DataModel.WorkingOrderRow workingOrderRow = sourceOrderRow.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); // Now aggregate the source order quantities and the destination order quantities. Throw an exception if the source orders are less than the // destination orders (we can't have less quantity ordered than we've placed with brokers and exchanges). 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 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> /// Handles a change to a error state. /// </summary> /// <param name="workingOrderRow">The parent working order.</param> static void OnClearErrorAction(DataModel.WorkingOrderRow workingOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // This will lock the WorkingOrderRow while we examine it. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The error status is cleared only when none of the sibling destination orders has an error. Boolean isErrorStatus = false; foreach (DataModel.DestinationOrderRow siblingOrderRow in workingOrderRow.GetDestinationOrderRows()) { siblingOrderRow.AcquireReaderLock(dataModelTransaction); if (siblingOrderRow.StatusCode == StatusCode.Error) { isErrorStatus = true; break; } } // If none of the siblings has an error, the we're going to set the working order's status to what it was before the error occurred. if (!isErrorStatus) { Decimal quantityExecuted = WorkingOrderService.GetExecutionQuantity(dataModelTransaction, workingOrderRow); Decimal quantityOrdered = WorkingOrderService.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); if (quantityExecuted == 0.0M && workingOrderRow.StatusCode != StatusCode.New) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.New); } if (0.0M < quantityExecuted && quantityExecuted < quantityOrdered && workingOrderRow.StatusCode != StatusCode.PartiallyFilled) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.PartiallyFilled); } if (quantityExecuted == quantityOrdered && workingOrderRow.StatusCode != StatusCode.Filled) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.Filled); } } }
/// <summary> /// Handles a change to a error state. /// </summary> /// <param name="workingOrderRow">The parent working order.</param> static void OnSetErrorAction(DataModel.WorkingOrderRow workingOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // This will lock the WorkingOrderRow while we examine it. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // This logic is simple enough, set to an error state if we're not already there. if (workingOrderRow.StatusCode != StatusCode.Error) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.Error); } }
/// <summary> /// Handles a change to a partially executed state. /// </summary> /// <param name="workingOrderRow">The parent working order.</param> static void OnPartialAction(DataModel.WorkingOrderRow workingOrderRow) { // A transaction is needed to handle the change. DataModelTransaction dataModelTransaction = DataModel.CurrentTransaction; // This will lock the WorkingOrderRow while we examine it. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The working order is considered partially filled if any of its destination orders are partially filled. The only exception to this is when the // working order is in an error state. if (workingOrderRow.StatusCode != StatusCode.Error && workingOrderRow.StatusCode != StatusCode.PartiallyFilled) { WorkingOrderService.UpdateWorkingOrderStatus(workingOrderRow, StatusCode.PartiallyFilled); } }
/// <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); } }