/// <summary> /// Changes the state of the Working Order to reflect a filledly filled state. /// </summary> private static void OnFilledAction(Object[] key, params Object[] parameters) { // A middle tier context is also required for a transacted update. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // It is possible that the Working Order that is the object of this status update operation may have been deleted since the action was // created. This is not an error condition. If there is no Working Order to update, then the operation is just terminated prematurely. WorkingOrderRow workingOrderRow = DataModel.WorkingOrder.WorkingOrderKey.Find(key); if (workingOrderRow == null) { return; } workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The error status on a Working Order cannot be cleared with a fill. if (workingOrderRow.StatusId != StatusMap.FromCode(Status.Error)) { // The Working Order is 'Filled' when the quantity executed is the same as the quantity ordered. Decimal quantityOrdered = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); Decimal quantityExecuted = WorkingOrder.GetExecutionQuantity(dataModelTransaction, workingOrderRow); if (quantityOrdered == quantityExecuted) { UpdateWorkingOrderStatus(workingOrderRow, Status.Filled); } } }
/// <summary> /// Handles a Destination Order entering the error state. /// </summary> private static void SetErrorAction(Object[] key, params Object[] parameters) { // A middle tier context is also required for a transacted update. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // It is possible that the Working Order that is the object of this status update operation may have been deleted since the action was // created. This is not an error condition. If there is no Working Order to update, then the operation is just terminated prematurely. WorkingOrderRow workingOrderRow = DataModel.WorkingOrder.WorkingOrderKey.Find(key); if (workingOrderRow == null) { return; } workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The Working Order reflects an 'Error' Status if any of its Destination Orders have an error. if (workingOrderRow.StatusId != StatusMap.FromCode(Status.Error)) { UpdateWorkingOrderStatus(workingOrderRow, Status.Error); } }
/// <summary> /// Changes the state of the Working Order to reflect a partially filled status. /// </summary> private static void OnPartialAction(Object[] key, params Object[] parameters) { // A middle tier context is also required for a transacted update. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // It is possible that the Working Order that is the object of this status update operation may have been deleted since the action was // created. This is not an error condition. If there is no Working Order to update, then the operation is just terminated prematurely. WorkingOrderRow workingOrderRow = DataModel.WorkingOrder.WorkingOrderKey.Find(key); if (workingOrderRow == null) { return; } workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The error status on a Working Order cannot be cleared with a partial fill. Otherwise, a partial fill on any of the Destination Orders is // reflected in the status of the parent Working Order. if (workingOrderRow.StatusId != StatusMap.FromCode(Status.Error)) { if (workingOrderRow.StatusId != StatusMap.FromCode(Status.PartiallyFilled)) { UpdateWorkingOrderStatus(workingOrderRow, Status.PartiallyFilled); } } }
/// <summary> /// Changes the state of the Working Order to reflect a filledly filled state. /// </summary> private static void ClearErrorAction(Object[] key, params Object[] parameters) { // A middle tier context is also required for a transacted update. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // It is possible that the Working Order that is the object of this status update operation may have been deleted since the action was // created. This is not an error condition. If there is no Working Order to update, then the operation is just terminated prematurely. WorkingOrderRow workingOrderRow = DataModel.WorkingOrder.WorkingOrderKey.Find(key); if (workingOrderRow == null) { return; } workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); dataModelTransaction.AddLock(workingOrderRow); if (workingOrderRow.RowState == DataRowState.Detached) { return; } // The 'Error' status is only cleared when all the Destination Orders are valid. Boolean isErrorStatus = false; foreach (DestinationOrderRow siblingOrderRow in workingOrderRow.GetDestinationOrderRows()) { siblingOrderRow.AcquireReaderLock(dataModelTransaction); if (siblingOrderRow.StatusId == StatusMap.FromCode(Status.Error)) { isErrorStatus = true; break; } } // The proper Working Order status must be evaluated when the error status is cleared. if (!isErrorStatus) { // The aggregates will determine the new state of the Working Order. Decimal quantityExecuted = WorkingOrder.GetExecutionQuantity(dataModelTransaction, workingOrderRow); Decimal quantityOrdered = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); // This restores the 'New' status when the canceled Destination Order was the only order with any fills. if (quantityExecuted == 0.0M && workingOrderRow.StatusId != StatusMap.FromCode(Status.New)) { UpdateWorkingOrderStatus(workingOrderRow, Status.New); } // This restores the 'Partially Filled' status when other executions remain. if (0.0M < quantityExecuted && quantityExecuted < quantityOrdered && workingOrderRow.StatusId != StatusMap.FromCode(Status.PartiallyFilled)) { UpdateWorkingOrderStatus(workingOrderRow, Status.PartiallyFilled); } // This restores the 'Filled' status when the quantity executed is the same as the quantity ordered. if (quantityExecuted == quantityOrdered && workingOrderRow.StatusId != StatusMap.FromCode(Status.Filled)) { UpdateWorkingOrderStatus(workingOrderRow, Status.Filled); } } }
/// <summary> /// The sum total of the quantities of all the destination orders in a given working order. /// </summary> /// <param name="dataModelTransaction"></param> /// <param name="workingOrderRow">A working order row.</param> /// <returns>The total quantity of all the destination orders associated with the working order.</returns> internal static Decimal GetExecutionQuantity(DataModelTransaction dataModelTransaction, WorkingOrderRow workingOrderRow) { // This will aggregate all the execution quantities. Note that the records are locked for the duration of the transaction to insure the integrity // of the aggregates. Decimal executionQuantity = 0.0m; foreach (DestinationOrderRow destinationOrderRow in workingOrderRow.GetDestinationOrderRows()) { destinationOrderRow.AcquireReaderLock(dataModelTransaction); if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.Canceled)) { foreach (ExecutionRow executionRow in destinationOrderRow.GetExecutionRows()) { executionRow.AcquireReaderLock(dataModelTransaction); executionQuantity += executionRow.ExecutionQuantity; } } } // This is the aggregate quantity executed for the given working order. return(executionQuantity); }
/// <summary> /// Handler for validating Execution records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> internal static void OnExecutionRowValidate(object sender, ExecutionRowChangeEventArgs e) { DestinationOrderRow destinationOrderRow; Decimal executedQuantity; DataModelTransaction dataModelTransaction; Decimal orderedQuantity; // The Business Rules will be enforced on this Execution. Note that it is locked at the point this handler is called. ExecutionRow executionRow = e.Row; // The action on the row determines which rule to evaluate. switch (e.Action) { case DataRowAction.Add: // This rule will update the status of the owning Destination Order based on the quantity executed. dataModelTransaction = DataModelTransaction.Current; // The aggregate of the quantities executed will determine whether the Destination Order requires a status change. destinationOrderRow = executionRow.DestinationOrderRow; destinationOrderRow.AcquireReaderLock(dataModelTransaction); if (destinationOrderRow.RowState == DataRowState.Detached) { return; } // This calculates the quantity outstanding on an order and the quantity executed on the order. orderedQuantity = destinationOrderRow.OrderedQuantity - destinationOrderRow.CanceledQuantity; executedQuantity = 0.0M; foreach (ExecutionRow siblingRow in destinationOrderRow.GetExecutionRows()) { siblingRow.AcquireReaderLock(dataModelTransaction); executedQuantity += siblingRow.ExecutionQuantity; } // This rule will set the status on the owning Destination Order based on the amont outstanding and the amount ordered. First, if the quantity // executed is greater than the quantity ordered, the Order goes into an error state. We can't reject executions as they come from an external // source, but the order can be flag and put into an error state. if (executedQuantity > orderedQuantity) { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.Error)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.Error); } } else { // When the total quantity executed is reduced to zero then the order goes back into the initial state. if (executedQuantity == 0.0m) { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.New)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.New); } } else { // While the executed quantity is still less than the outstanding quantity the order is consdered to be partially filled. Otherwise, // the order is completely filled. if (executedQuantity < orderedQuantity) { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.PartiallyFilled)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.PartiallyFilled); } } else { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.Filled)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.Filled); } } } } break; case DataRowAction.Delete: // A middle tier context is required to lock the rows so the quantities can be aggregated. dataModelTransaction = DataModelTransaction.Current; // The aggregate of the quantities executed will determine whether the Destination Order requires a status change. destinationOrderRow = (DestinationOrderRow)executionRow.GetParentRow( DataModel.Execution.DestinationOrderExecutionRelation, DataRowVersion.Original); destinationOrderRow.AcquireReaderLock(dataModelTransaction); if (destinationOrderRow.RowState == DataRowState.Detached) { return; } // This calculates the quantity outstanding on an order and the quantity executed on the order. orderedQuantity = destinationOrderRow.OrderedQuantity - destinationOrderRow.CanceledQuantity; executedQuantity = 0.0M; foreach (ExecutionRow siblingRow in destinationOrderRow.GetExecutionRows()) { siblingRow.AcquireReaderLock(dataModelTransaction); executedQuantity += siblingRow.ExecutionQuantity; } // When the total quantity executed is reduced to zero then the order goes back into the initial state. Note that it is impossible for the // order to transition into a error state by deleting an execution. if (executedQuantity == 0.0m) { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.New)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.New); } } else { // While the executed quantity is still less than the outstanding quantity the order is consdered to be partially filled. Note that it is // impossible for the order to transition to a filled state by deleting an execution. if (executedQuantity < orderedQuantity) { if (destinationOrderRow.StatusId != StatusMap.FromCode(Status.PartiallyFilled)) { UpdateDestinationOrderStatus(destinationOrderRow, Status.PartiallyFilled); } } } break; } }
/// <summary> /// Updates the status of the Destination Order. /// </summary> /// <param name="destinationOrderRow">The Working Order to be modified.</param> /// <param name="status">The new status of the Working Order.</param> private static void UpdateDestinationOrderStatus(DestinationOrderRow destinationOrderRow, Status status) { // It is a good idea to have a central method for updating the Destination Order in the event the parameter order changes. Execution.dataModel.UpdateDestinationOrder(null, null, null, null, null, null, null, new object[] { destinationOrderRow.DestinationOrderId }, null, null, null, null, null, null, null, null, null, destinationOrderRow.RowVersion, null, null, null, null, null, StatusMap.FromCode(status), null, null, null, null, null); }
internal static void Accept(Guid matchId, Int64 rowVersion) { DataModel dataModel = new DataModel(); try { DataModel.DataLock.EnterReadLock(); MatchRow matchRow = DataModel.Match.MatchKey.Find(matchId); if (matchRow != null) { // Time stamps and user stamps Guid createdUserId = TradingSupport.UserId; DateTime createdTime = DateTime.UtcNow; Guid modifiedUserId = createdUserId; DateTime modifiedTime = createdTime; // Call the internal method to complete the operation. dataModel.UpdateMatch(null, null, null, null, null, null, null, new Object[] { matchRow.MatchId }, rowVersion, StatusMap.FromCode(Status.Accepted), null); // This is the working order associated with the match. WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; // This will find the contra order. MatchRow contraMatchRow = DataModel.Match.MatchKeyWorkingOrderIdContraOrderId.Find( matchRow.ContraOrderId, matchRow.WorkingOrderId); if (contraMatchRow == null) { throw new Exception(string.Format("Corruption: the match record for {0}, {1} can't be found", matchRow.ContraOrderId, matchRow.WorkingOrderId)); } // When both sides have agreed to the match, the Destination Orders are generated. if (contraMatchRow.StatusId != StatusMap.FromCode(Status.Accepted)) { return; } // At this point, both sides have agreed to a trade. This is the working order of the contra side of the trade. WorkingOrderRow contraOrderRow = contraMatchRow.WorkingOrderRow; // The quantity of this transaction is the lesser of the two sides of the trade. decimal quantity = workingOrderRow.SubmittedQuantity < contraOrderRow.SubmittedQuantity ? workingOrderRow.SubmittedQuantity : contraOrderRow.SubmittedQuantity; PriceRow priceRow = DataModel.Price.PriceKey.Find(workingOrderRow.SecurityId, workingOrderRow.SettlementId); if (priceRow == null) { throw new Exception("The price of this trade can't be determined."); } Guid destinationOrderId = Guid.NewGuid(); dataModel.CreateDestinationOrder( workingOrderRow.BlotterId, null, null, createdTime, createdUserId, Match.destinationId, destinationOrderId, null, null, false, false, workingOrderRow[DataModel.WorkingOrder.LimitPriceColumn], modifiedTime, modifiedUserId, quantity, workingOrderRow.OrderTypeId, workingOrderRow.SecurityId, createdTime, workingOrderRow.SettlementId, workingOrderRow.SideId, StateMap.FromCode(State.Acknowledged), StatusMap.FromCode(Status.New), workingOrderRow[DataModel.WorkingOrder.StopPriceColumn], workingOrderRow.TimeInForceId, createdTime, createdUserId, workingOrderRow.WorkingOrderId); Guid executionId = Guid.NewGuid(); dataModel.CreateExecution( 0.0m, workingOrderRow.BlotterId, null, null, 0.0m, createdTime, createdUserId, destinationOrderId, StateMap.FromCode(State.Acknowledged), executionId, priceRow.LastPrice, quantity, null, null, false, modifiedTime, modifiedUserId, null, null, null, null, StateMap.FromCode(State.Sent), null, null, null, null); dataModel.UpdateMatch(null, null, null, null, null, null, null, new Object[] { matchId }, matchRow.RowVersion, StatusMap.FromCode(Status.Accepted), null); Guid contraDestinationOrderId = Guid.NewGuid(); dataModel.CreateDestinationOrder( contraOrderRow.BlotterId, 0.0m, null, createdTime, createdUserId, Match.destinationId, contraDestinationOrderId, null, null, false, false, contraOrderRow[DataModel.WorkingOrder.LimitPriceColumn], modifiedTime, modifiedUserId, quantity, contraOrderRow.OrderTypeId, contraOrderRow.SecurityId, createdTime, contraOrderRow.SettlementId, contraOrderRow.SideId, StateMap.FromCode(State.Acknowledged), StatusMap.FromCode(Status.New), contraOrderRow[DataModel.WorkingOrder.StopPriceColumn], contraOrderRow.TimeInForceId, createdTime, createdUserId, contraOrderRow.WorkingOrderId); Guid contraExecutionId = Guid.NewGuid(); dataModel.CreateExecution( 0.0m, contraOrderRow.BlotterId, null, null, 0.0m, createdTime, createdUserId, contraDestinationOrderId, StateMap.FromCode(State.Acknowledged), contraExecutionId, priceRow.LastPrice, quantity, null, null, false, modifiedTime, modifiedUserId, null, null, null, null, StateMap.FromCode(State.Sent), 0.0m, 0.0m, 0.0m, 0.0m); } } finally { // Release the tables. DataModel.DataLock.ExitReadLock(); } }
internal static void Decline(Guid matchId, Int64 rowVersion) { DataModel dataModel = new DataModel(); try { DataModel.DataLock.EnterReadLock(); // This context is used to keep track of the locks aquired for the ancillary data. TransactionScope transactionScope = new TransactionScope(); DataModelTransaction dataModelTransaction = DataModelTransaction.Current; MatchRow matchRow = DataModel.Match.MatchKey.Find(matchId); if (matchRow != null) { // Time stamps and user stamps Guid modifiedUserId = TradingSupport.UserId; DateTime modifiedTime = DateTime.UtcNow; // Call the internal method to complete the operation. dataModel.UpdateMatch(null, null, null, null, null, null, null, new Object[] { matchRow.MatchId }, rowVersion, StatusMap.FromCode(Status.Declined), null); // Call the internal method to decline the contra side of this match. foreach (MatchRow contraMatchRow in DataModel.Match.Rows) { if (contraMatchRow.WorkingOrderId == matchRow.ContraOrderId && contraMatchRow.ContraOrderId == matchRow.WorkingOrderId) { rowVersion = contraMatchRow.RowVersion; dataModel.UpdateMatch(null, null, null, null, null, null, null, new Object[] { contraMatchRow.MatchId }, rowVersion, StatusMap.FromCode(Status.Declined), null); } } } } finally { // Release the tables. DataModel.DataLock.ExitReadLock(); } }
/// <summary> /// Reset the negotiation to a "in negotiation" state. /// </summary> /// <param name="consumerTrustNegotiations">The the negotiations to reset.</param> internal static void Reject(ConsumerTrustNegotiationInfo[] consumerTrustNegotiations) { // An instance of the shared data model is required to use its methods. DataModel dataModel = new DataModel(); // The business logic requires the current time and the user identifier for auditing. Guid createUserId = TradingSupport.UserId; DateTime createDateTime = DateTime.UtcNow; DateTime modifiedTime = createDateTime; Guid modifiedUserId = createUserId; // This Web Method comes with an implicit transaction that is linked to its execution. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // This method can handle a batch of updates in a single transaction. foreach (ConsumerTrustNegotiationInfo consumerTrustNegotiationInfo in consumerTrustNegotiations) { List <ConsumerTrustNegotiationPaymentMethodTypeInfo> counterItems = new List <ConsumerTrustNegotiationPaymentMethodTypeInfo>(); // The blotter is not passed in from the client but is used Guid blotterId = Guid.Empty; Status negotiationStatus; TrustNegotiationInfo trustNegotiationInfo = null; // This is the next negotiation in the batch to be updated. ConsumerTrustNegotiationRow consumerTrustNegotiationRow = DataModel.ConsumerTrustNegotiation.ConsumerTrustNegotiationKey.Find(consumerTrustNegotiationInfo.ConsumerTrustNegotiationId); try { // Lock the current negotation record for reading. The data model doesn't support reader lock promotion, so the programming model is to // lock the database, collect the data, release the locks and then write. This model is especially important when iterating through a // large batch to prevent the number of locks from growing to large. consumerTrustNegotiationRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // The blotter identifier is used for access control and is not passed in by the client. blotterId = consumerTrustNegotiationRow.BlotterId; negotiationStatus = StatusMap.FromId(consumerTrustNegotiationRow.StatusId); trustNegotiationInfo = new TrustNegotiationInfo(consumerTrustNegotiationRow); // Determine whether the client has the right to modify this record. if (!TradingSupport.HasAccess(dataModelTransaction, blotterId, AccessRight.Write)) { throw new FaultException <FluidTrade.Core.SecurityFault>(new SecurityFault("You do not have write access to the selected object.")); } // The payment methods are maintained as a vector associated with the negotiation record. This will lock each of the records and read the // payment methods into a data structure so the locks don't need to be held when it is time to write foreach (var consumerTrustNegotiationOfferPaymentMethodRow in consumerTrustNegotiationRow.GetConsumerTrustNegotiationCounterPaymentMethodRows()) { try { // Temporarily lock the record containing the payment method. consumerTrustNegotiationOfferPaymentMethodRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // This list is used to delete the payment methods that are no longer part of this negotiation. counterItems.Add( new ConsumerTrustNegotiationPaymentMethodTypeInfo( consumerTrustNegotiationOfferPaymentMethodRow.PaymentMethodTypeId, consumerTrustNegotiationOfferPaymentMethodRow.ConsumerTrustNegotiationCounterPaymentMethodId, consumerTrustNegotiationOfferPaymentMethodRow.RowVersion)); } finally { // At this point the payment method isn't needed. consumerTrustNegotiationOfferPaymentMethodRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } MatchRow matchRow = DataModel.Match.MatchKey.Find(trustNegotiationInfo.MatchId); try { matchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); trustNegotiationInfo.MatchRowVersion = matchRow.RowVersion; trustNegotiationInfo.ContraMatchId = matchRow.ContraMatchId; } finally { matchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } MatchRow contraMatchRow = DataModel.Match.MatchKey.Find(trustNegotiationInfo.ContraMatchId); try { contraMatchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); trustNegotiationInfo.ContraMatchRowVersion = contraMatchRow.RowVersion; } finally { contraMatchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } finally { // At this point, the negotiation record isn't needed. It is critical to release the reader locks before attempting a write. consumerTrustNegotiationRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // At this point, all the data for this operation has been collected and the CRUD operations can be invoked to finish the update. Note that // the counter party information is not modified here, but is done through the Chinese wall. Guid newNegotiationId = Guid.NewGuid(); dataModel.CreateConsumerTrustNegotiation( trustNegotiationInfo.AccountBalance, trustNegotiationInfo.BlotterId, newNegotiationId, trustNegotiationInfo.CounterPaymentLength, trustNegotiationInfo.CounterPaymentStartDateLength, trustNegotiationInfo.CounterPaymentStartDateUnitId, trustNegotiationInfo.CounterSettlementUnitId, trustNegotiationInfo.CounterSettlementValue, createDateTime, createUserId, trustNegotiationInfo.CreditCardId, trustNegotiationInfo.IsRead, trustNegotiationInfo.IsReply, trustNegotiationInfo.MatchId, modifiedTime, modifiedUserId, consumerTrustNegotiationInfo.PaymentLength, consumerTrustNegotiationInfo.PaymentStartDateLength, consumerTrustNegotiationInfo.PaymentStartDateUnitId, consumerTrustNegotiationInfo.SettlementUnitId, consumerTrustNegotiationInfo.SettlementValue, StatusMap.FromCode(Status.Rejected), out trustNegotiationInfo.Version); // This will add the payment methods to the negotiation that are not already there. foreach (Guid paymentMethodTypeId in consumerTrustNegotiationInfo.PaymentMethodTypes) { dataModel.CreateConsumerTrustNegotiationOfferPaymentMethod( blotterId, newNegotiationId, Guid.NewGuid(), paymentMethodTypeId); } foreach (ConsumerTrustNegotiationPaymentMethodTypeInfo consumerTrustNegotiationPaymentMethodTypeInfo in counterItems) { dataModel.UpdateConsumerTrustNegotiationCounterPaymentMethod( blotterId, null, new Object[] { consumerTrustNegotiationPaymentMethodTypeInfo.ConsumerTrustNegotiationOfferPaymentMethodId }, newNegotiationId, consumerTrustNegotiationPaymentMethodTypeInfo.PaymentMethodInfoId, consumerTrustNegotiationPaymentMethodTypeInfo.RowVersion); } //Reset the Match Status Id. This is required so the match engine will redo the match. The //match engine does not recalculate if it is not in the initial three stages of - Valid, Partial, ValidwithFunds dataModel.UpdateMatch( null, null, null, null, null, null, null, new object[] { trustNegotiationInfo.MatchId }, trustNegotiationInfo.MatchRowVersion, StatusMap.FromCode(Status.ValidMatch), null); //Reset the Contra Match Status Id dataModel.UpdateMatch( null, null, null, null, null, null, null, new object[] { trustNegotiationInfo.ContraMatchId }, trustNegotiationInfo.ContraMatchRowVersion, StatusMap.FromCode(Status.ValidMatch), null); } }
internal static void Decline(Guid matchId) { Guid negotiationId = Guid.Empty; long rowVersion = long.MinValue; DataModel dataModel = new DataModel(); try { DataModel.DataLock.EnterReadLock(); // This context is used to keep track of the locks aquired for the ancillary data. TransactionScope transactionScope = new TransactionScope(); DataModelTransaction dataModelTransaction = DataModelTransaction.Current; MatchRow matchRow = DataModel.Match.MatchKey.Find(matchId); if (matchRow != null) { WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; // See if there is already a pending offer. bool isFound = false; foreach (NegotiationRow innerNegotiationRow in matchRow.GetNegotiationRows()) { if (innerNegotiationRow.StatusId == StatusMap.FromCode(Status.Declined)) { throw new Exception("This offer has previously been declined."); } if (innerNegotiationRow.StatusId == StatusMap.FromCode(Status.Pending)) { // Call the internal method to complete the operation. rowVersion = innerNegotiationRow.RowVersion; negotiationId = innerNegotiationRow.NegotiationId; dataModel.UpdateNegotiation( null, null, false, null, null, new Object[] { negotiationId }, null, rowVersion, StatusMap.FromCode(Status.Declined)); isFound = true; } } // Call the internal method to complete the operation. if (!isFound) { negotiationId = Guid.NewGuid(); dataModel.CreateNegotiation(workingOrderRow.BlotterId, null, false, matchId, negotiationId, 0.0m, StatusMap.FromCode(Status.Declined)); } // If there's a counter offer, then notify the couter part that the offer has been declined. This will find the contra matching record. MatchRow contraMatchRow = DataModel.Match.MatchKeyWorkingOrderIdContraOrderId.Find( new object[] { matchRow.ContraOrderId, matchRow.WorkingOrderId }); if (contraMatchRow == null) { throw new Exception(string.Format("Corruption: the match record for {0}, {1} can't be found", matchRow.ContraOrderId, matchRow.WorkingOrderId)); } // When both sides have agreed to the Negotiation, the Destination Orders are generated. foreach (NegotiationRow contraNegotiationRow in contraMatchRow.GetNegotiationRows()) { if (contraNegotiationRow.StatusId == StatusMap.FromCode(Status.Pending)) { dataModel.UpdateNegotiation( null, null, false, null, null, new Object[] { contraNegotiationRow.NegotiationId }, null, contraNegotiationRow.RowVersion, StatusMap.FromCode(Status.Declined)); } } } } finally { // Release the tables. DataModel.DataLock.ExitReadLock(); } }
/// <summary> /// Create a new Debt Holder Record /// </summary> /// <returns></returns> internal Guid Create() { DataModel dataModel = new DataModel(); DataModelTransaction dataModelTransaction = DataModelTransaction.Current; Guid userId = TradingSupport.UserId; Guid tenantId = PersistenceHelper.GetTenantForEntity(dataModelTransaction, this.Record.Blotter); Guid entityId = Guid.Empty; Guid consumerId; Guid creditCardId; Guid workingOrderId; CountryRow country; Guid countryId; Guid? provinceId = null; TypeRow type; Guid typeId; ImageRow image; Guid imageId; // These variables are used for auditing the changes to this record. DateTime createdTime = DateTime.UtcNow; Guid createdUserId = TradingSupport.UserId; DateTime modifiedTime = createdTime; Guid modifiedUserId = createdUserId; EntityRow dollars; Guid dollarsId; // We need write access to the containing blotter in order to add a record to it. if (!DataModelFilters.HasAccess(dataModelTransaction, userId, this.Record.Blotter, AccessRight.Write)) { throw new SecurityException("Current user does not have write access to the selected blotter"); } country = TradingSupport.FindCountryByKey( this.Record.ConfigurationId, "FK_Country_Security", new object[] { this.Record.CountryCode }); countryId = country.CountryId; country.ReleaseReaderLock(dataModelTransaction.TransactionId); if (this.Record.ProvinceCode != null) { ProvinceRow province = TradingSupport.FindProvinceByKey( this.Record.ConfigurationId, "FK_Province_Consumer", new object[] { this.Record.ProvinceCode }); provinceId = province.ProvinceId; province.ReleaseReaderLock(dataModelTransaction.TransactionId); } dollars = TradingSupport.FindEntityByKey( this.Record.ConfigurationId, "FK_Security_WorkingOrder_SettlementId", new object[] { this.Record.Currency }); dollarsId = dollars.EntityId; dollars.ReleaseReaderLock(dataModelTransaction.TransactionId); image = TradingSupport.FindImageByKey( this.Record.ConfigurationId, "FK_Image_Entity", new object[] { "OBJECT" }); imageId = image.ImageId; image.ReleaseReaderLock(dataModelTransaction.TransactionId); type = TradingSupport.FindTypeByKey( this.Record.ConfigurationId, "FK_Type_Entity", new object[] { "CONSUMER DEBT" }); typeId = type.TypeId; type.ReleaseReaderLock(dataModelTransaction.TransactionId); entityId = Guid.NewGuid(); consumerId = Guid.NewGuid(); creditCardId = Guid.NewGuid(); workingOrderId = Guid.NewGuid(); dataModel.CreateEntity( createdTime, null, entityId, null, null, null, null, null, null, null, this.Record.AccountCode, imageId, false, false, modifiedTime, this.Record.OriginalAccountNumber, tenantId, typeId); dataModel.CreateSecurity( null, countryId, null, null, null, 1, 1, entityId, this.Record.AccountCode, tenantId, VolumeCategoryMap.FromCode(VolumeCategory.Unknown)); dataModel.CreateConsumer( this.Record.Address1, this.Record.Address2, null, null, this.Record.City, consumerId, this.Record.DateOfBirth != null ? (object)this.Record.DateOfBirth.Value : null, null, null, this.Record.FirstName, null, this.Record.LastName, this.Record.MiddleName, StringUtilities.CleanUpAlphaNumericString(this.Record.PhoneNumber), this.Record.PostalCode, provinceId, null, StringUtilities.CleanUpAlphaNumericString(this.Record.SocialSecurityNumber), this.Record.Suffix); dataModel.CreateCreditCard( this.Record.AccountBalance, this.Record.AccountCode, consumerId, creditCardId, this.Record.DebtHolder, null, null, this.Record.AccountCode, StringUtilities.CleanUpAlphaNumericString(this.Record.OriginalAccountNumber), tenantId); dataModel.CreateConsumerDebt( this.Record.CollectionDate, entityId, consumerId, creditCardId, this.Record.DateOfDelinquency != null ? (object)this.Record.DateOfDelinquency.Value : null, null, null, this.Record.Representative, this.Record.Tag, tenantId, this.Record.VendorCode); dataModel.CreateWorkingOrder( null, this.Record.Blotter, createdTime, createdUserId, CrossingMap.FromCode(Crossing.AlwaysMatch), null, null, null, null, true, true, true, null, modifiedTime, modifiedUserId, OrderTypeMap.FromCode(OrderType.Market), entityId, createdTime, dollarsId, SideMap.FromCode(Side.Sell), createdTime, StatusMap.FromCode(Status.New), null, null, null, null, TimeInForceMap.FromCode(TimeInForce.GoodTillCancel), createdTime, createdTime, workingOrderId); // Create the access control record for this new entity. dataModel.CreateAccessControl( Guid.NewGuid(), AccessRightMap.FromCode(AccessRight.FullControl), entityId, userId, tenantId); return(entityId); }
/// <summary> /// Updates the status of the Working Order. /// </summary> /// <param name="workingOrderRow">The Working Order to be modified.</param> /// <param name="status">The new status of the Working Order.</param> private static void UpdateWorkingOrderStatus(WorkingOrderRow workingOrderRow, Status status) { // It is a good idea to have a central method for updating the Working Order in the event the parameter order changes. WorkingOrder.dataModel.UpdateWorkingOrder(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, workingOrderRow.RowVersion, null, null, null, null, null, StatusMap.FromCode(status), null, null, null, null, null, null, null, null, new object[] { workingOrderRow.WorkingOrderId }); }
/// <summary> /// Handler for validating Source Order records. /// </summary> /// <param name="sender">The object that originated the event.</param> /// <param name="e">The event arguments.</param> internal static void OnWorkingOrderRowValidate(object sender, WorkingOrderRowChangeEventArgs e) { // The Business Rules will be enforced on this Working Order. Note that it is locked at the point this handler is called. WorkingOrderRow workingOrderRow = e.Row; // The action on the row determines which rule to evaluate. switch (e.Action) { case DataRowAction.Add: case DataRowAction.Change: // This will evaluate whether the given Working Order is a candidate for matching. Boolean isSubmitted = false; // The 'Away' flag is used to inhibit matching without loosing the original preference for matching. It is used when the trader wants to walk // away from his blotter. When the trader returns, they want to flip a switch and have all the previous settings active for the orders on the // blotter. This skips the matching logic for any trade marked 'Away'. if ((CrossingMap.FromId(workingOrderRow.CrossingId) & Crossing.Away) == Crossing.None) { // This determines if the trade is eligible for crossing. switch (CrossingMap.FromId(workingOrderRow.CrossingId) & ~Crossing.Away) { case Crossing.AlwaysMatch: // The trade is eligible for crossing. isSubmitted = true; break; case Crossing.UsePreferences: // The trade is only eligible for crossing if it passes a set of preferences properties set by the user. isSubmitted = workingOrderRow.IsAwake; // The start and stop time must be extracted from the record using the base class. It is safe here because the triggers are only // invoked while the record is locked. DateTime startTime = workingOrderRow.StartTime; DateTime stopTime = workingOrderRow.StopTime; // The order is only eligible for crossing if the time of day is after the start time. if (startTime != null && DateTime.UtcNow.TimeOfDay < startTime.TimeOfDay) { isSubmitted = false; } // The order is only eligible for crossing if the time if day is before the end time. if (stopTime != null && DateTime.UtcNow.TimeOfDay > stopTime.TimeOfDay) { isSubmitted = false; } break; } } // This will submit the order for crossing when it isn't already submitted. if (isSubmitted && workingOrderRow.StatusId != StatusMap.FromCode(Status.Submitted)) { UpdateWorkingOrderStatus(workingOrderRow, Status.Submitted); } // This removes the order for consideration in the crossing pool when it has been submitted. if (!isSubmitted && workingOrderRow.StatusId == StatusMap.FromCode(Status.Submitted)) { UpdateWorkingOrderStatus(workingOrderRow, Status.New); } break; } }
/// <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> internal static void OnDestinationOrderRowValidate(object sender, DestinationOrderRowChangeEventArgs e) { Int32 currentStatusCode; Decimal destinationOrderQuantity; DataModelTransaction dataModelTransaction; Int32 previousStatusCode; Decimal sourceOrderQuantity; WorkingOrderRow workingOrderRow; // The Business Rules will be enforced on this Destination Order. Note that it is locked at the point this handler is called. DestinationOrderRow destinationOrderRow = e.Row; // The action on the row determines which rule to evaluate. switch (destinationOrderRow.RowState) { case DataRowState.Added: // This rule will reject the operation if the Working Order is overcommitted with a destination. dataModelTransaction = DataModelTransaction.Current; // This rule will throw an exception if the quantity sent to a destination is greater than the quantity ordered. The quantity ordered and // quantity sent can only be calcuated from the owning Working Order which must be locked in order to carry out the calculations. workingOrderRow = e.Row.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } break; case DataRowState.Modified: // Reject the operation if the Working Order is overcommitted with a destination. Note that the order of the evaluation is important for // efficiency. There is no need to sum up the source and Destination Orders if the quantity has been reduced as there's no chance it will be // over committed. Decimal originalQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Original]; Decimal currentQuantity = (Decimal)destinationOrderRow[DataModel.DestinationOrder.OrderedQuantityColumn, DataRowVersion.Current]; if (originalQuantity < currentQuantity) { // Once it's determined that the order can be overcommitted, a middle tier context is required to lock the rows so the quantities can be // aggregated. dataModelTransaction = DataModelTransaction.Current; // This rule will throw an exception if the quantity sent to a destination is greater than the quantity ordered. The quantity ordered and // quantity sent can only be calcuated from the owning Working Order which must be locked in order to carry out the calculations. workingOrderRow = e.Row.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction); sourceOrderQuantity = WorkingOrder.GetSourceOrderQuantity(dataModelTransaction, workingOrderRow); destinationOrderQuantity = WorkingOrder.GetDestinationOrderQuantity(dataModelTransaction, workingOrderRow); if (sourceOrderQuantity < destinationOrderQuantity) { throw new FaultException <DestinationQuantityFault>( new DestinationQuantityFault(workingOrderRow.WorkingOrderId, sourceOrderQuantity, destinationOrderQuantity)); } } // This rule will examine the effects of a state change of the Destination Order. The stateChangeMatrix contains delegates to methods that // will determine what, if any, stats change should be applied to the Working Order due to the change in state of this Destination Order. previousStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Original])); currentStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Current])); if (previousStatusCode != currentStatusCode) { DestinationOrder.statusChangeMatrix[previousStatusCode, currentStatusCode](new Object[] { destinationOrderRow.WorkingOrderId }); } break; case DataRowState.Deleted: // This rule will examine the effects of a state change of the Destination Order. The stateChangeMatrix contains delegates to methods that // will determine what, if any, stats change should be applied to the Working Order due to the change in state of this Destination Order. previousStatusCode = Convert.ToInt32(StatusMap.FromId((Guid)destinationOrderRow[DataModel.DestinationOrder.StatusIdColumn, DataRowVersion.Original])); currentStatusCode = (Int32)Status.Deleted; Guid workingOrderId = (Guid)destinationOrderRow[DataModel.DestinationOrder.WorkingOrderIdColumn, DataRowVersion.Original]; DestinationOrder.statusChangeMatrix[previousStatusCode, currentStatusCode](new Object[] { workingOrderId }); break; } }
/// <summary> /// Create a new consumer. /// </summary> /// <returns>The ConsumerId of the consumer.</returns> private Guid CreateConsumer() { DataModel dataModel = new DataModel(); DataModelTransaction dataModelTransaction = DataModelTransaction.Current; Guid userId = TradingSupport.UserId; Guid tenantId = PersistenceHelper.GetTenantForEntity(DataModelTransaction.Current, this.Record.Blotter); Guid entityId = Guid.NewGuid(); Guid consumerId = Guid.NewGuid(); Guid creditCardId = Guid.NewGuid(); Guid workingOrderId = Guid.NewGuid(); CountryRow country; Guid countryId; Guid? provinceId = null; EntityRow dollars; Guid dollarsId; TypeRow type; Guid typeId; ImageRow image; Guid imageId; DateTime currentUTCTime = DateTime.UtcNow; country = TradingSupport.FindCountryByKey( this.Record.ConfigurationId, "FK_Country_Security", new object[] { this.Record.CountryCode }); countryId = country.CountryId; country.ReleaseReaderLock(dataModelTransaction.TransactionId); if (this.Record.ProvinceCode != null) { ProvinceRow province = TradingSupport.FindProvinceByKey( this.Record.ConfigurationId, "FK_Province_Consumer", new object[] { this.Record.ProvinceCode }); provinceId = province.ProvinceId; province.ReleaseReaderLock(dataModelTransaction.TransactionId); } dollars = TradingSupport.FindEntityByKey( this.Record.ConfigurationId, "FK_Security_WorkingOrder_SettlementId", new object[] { this.Record.Currency }); dollarsId = dollars.EntityId; dollars.ReleaseReaderLock(dataModelTransaction.TransactionId); image = TradingSupport.FindImageByKey( this.Record.ConfigurationId, "FK_Image_Entity", new object[] { "OBJECT" }); imageId = image.ImageId; image.ReleaseReaderLock(dataModelTransaction.TransactionId); type = TradingSupport.FindTypeByKey( this.Record.ConfigurationId, "FK_Type_Entity", new object[] { "CONSUMER TRUST" }); typeId = type.TypeId; type.ReleaseReaderLock(dataModelTransaction.TransactionId); dataModel.CreateEntity( currentUTCTime, null, entityId, null, null, null, null, null, null, null, this.Record.CustomerCode, imageId, false, false, currentUTCTime, this.Record.SavingsEntityCode, tenantId, typeId); dataModel.CreateSecurity( null, countryId, null, null, null, 1, 1, entityId, this.Record.AccountCode, tenantId, VolumeCategoryMap.FromCode(VolumeCategory.Unknown)); dataModel.CreateConsumer( this.Record.Address1, this.Record.Address2, this.Record.BankAccountNumber, this.Record.BankRoutingNumber, this.Record.City, consumerId, this.Record.DateOfBirth != null ? (object)this.Record.DateOfBirth.Value : null, null, null, this.Record.FirstName, this.Record.IsEmployed != null ? (object)this.Record.IsEmployed.Value : null, this.Record.LastName, this.Record.MiddleName, StringUtilities.CleanUpAlphaNumericString(this.Record.PhoneNumber), this.Record.PostalCode, provinceId, this.Record.Salutation, StringUtilities.CleanUpAlphaNumericString(this.Record.SocialSecurityNumber), this.Record.Suffix); dataModel.CreateConsumerTrust( consumerId, entityId, null, null, this.Record.SavingsAccount, this.Record.SavingsBalance, this.Record.Tag, tenantId, this.Record.VendorCode); //If this not found, there will be an exception. Let the exception propagate up. dataModel.CreateWorkingOrder( null, this.Record.Blotter, currentUTCTime, userId, CrossingMap.FromCode(Crossing.AlwaysMatch), null, null, null, null, true, true, true, null, DateTime.UtcNow, TradingSupport.UserId, OrderTypeMap.FromCode(OrderType.Market), entityId, DateTime.UtcNow, dollarsId, SideMap.FromCode(Side.Sell), DateTime.UtcNow, StatusMap.FromCode(Status.New), null, null, null, null, TimeInForceMap.FromCode(TimeInForce.GoodTillCancel), DateTime.UtcNow, DateTime.UtcNow, workingOrderId); // Create the access control record for this new entity. dataModel.CreateAccessControl( Guid.NewGuid(), AccessRightMap.FromCode(AccessRight.FullControl), entityId, userId, tenantId); return(consumerId); }
/// <summary> /// Update a consumer. /// </summary> /// <param name="entity">The ConsumerTrust's Entity row.</param> /// <returns>The ConsumerId of the Consumer row.</returns> private Guid UpdateConsumer(EntityRow entity) { DataModel dataModel = new DataModel(); DataModelTransaction dataModelTransaction = DataModelTransaction.Current; DateTime modified = DateTime.UtcNow; CountryRow country; Guid countryId; Guid? provinceId = null; EntityRow dollars; Guid dollarsId; ConsumerRow consumer = null; ConsumerTrustRow consumerTrust = null; SecurityRow security = null; WorkingOrderRow workingOrder = null; MatchRow[] matches; Guid consumerId; Guid consumerTrustId; Guid entityId; Guid securityId; Guid workingOrderId; Int64 consumerVersion; Int64 consumerTrustVersion; Int64 entityVersion; Int64 securityVersion; Int64 workingOrderVersion; Boolean updateConsumer = false; Boolean updateConsumerTrust = false; Boolean updateEntity = false; Boolean updateSecurity = false; country = TradingSupport.FindCountryByKey( this.Record.ConfigurationId, "FK_Country_Security", new object[] { this.Record.CountryCode }); countryId = country.CountryId; country.ReleaseReaderLock(dataModelTransaction.TransactionId); if (this.Record.ProvinceCode != null) { ProvinceRow province = TradingSupport.FindProvinceByKey( this.Record.ConfigurationId, "FK_Province_Consumer", new object[] { this.Record.ProvinceCode }); provinceId = province.ProvinceId; province.ReleaseReaderLock(dataModelTransaction.TransactionId); } dollars = TradingSupport.FindEntityByKey( this.Record.ConfigurationId, "FK_Security_WorkingOrder_SettlementId", new object[] { this.Record.Currency }); dollarsId = dollars.EntityId; dollars.ReleaseReaderLock(dataModelTransaction.TransactionId); try { entity.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); entityId = entity.EntityId; entityVersion = entity.RowVersion; if (TradingSupport.IsColumnOld(entity, "Name", this.Record.OriginalAccountNumber)) { updateEntity = true; } } finally { entity.ReleaseLock(dataModelTransaction.TransactionId); } try { security = DataModel.Security.SecurityKey.Find(entityId); security.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); securityId = entityId; securityVersion = security.RowVersion; workingOrder = security.GetWorkingOrderRowsByFK_Security_WorkingOrder_SecurityId()[0]; if (TradingSupport.IsColumnOld(security, "CountryId", countryId)) { updateSecurity = true; } } finally { security.ReleaseLock(dataModelTransaction.TransactionId); } // Control the working order: workingOrder.AcquireWriterLock(dataModelTransaction); try { consumerTrust = DataModel.ConsumerTrust.ConsumerTrustKey.Find(entityId); consumerTrust.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); consumerTrustId = consumerTrust.ConsumerTrustId; consumerTrustVersion = consumerTrust.RowVersion; consumer = DataModel.Consumer.ConsumerKey.Find(consumerTrust.ConsumerId); if (TradingSupport.IsColumnOld(consumerTrust, "SavingsAccount", this.Record.SavingsAccount) || TradingSupport.IsColumnOld(consumerTrust, "SavingsBalance", this.Record.SavingsBalance) || TradingSupport.IsColumnOld(consumerTrust, "Tag", this.Record.Tag)) { updateConsumerTrust = true; } } finally { consumerTrust.ReleaseLock(dataModelTransaction.TransactionId); } try { consumer.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); consumerId = consumer.ConsumerId; consumerVersion = consumer.RowVersion; if (TradingSupport.IsColumnOld(consumer, "Address1", this.Record.Address1) || TradingSupport.IsColumnOld(consumer, "Address2", this.Record.Address2) || TradingSupport.IsColumnOld(consumer, "BankAccountNumber", this.Record.BankAccountNumber) || TradingSupport.IsColumnOld(consumer, "BankRoutingNumber", this.Record.BankRoutingNumber) || TradingSupport.IsColumnOld(consumer, "City", this.Record.City) || TradingSupport.IsColumnOld(consumer, "DateOfBirth", this.Record.DateOfBirth) || TradingSupport.IsColumnOld(consumer, "FirstName", this.Record.FirstName) || TradingSupport.IsColumnOld(consumer, "IsEmployed", this.Record.IsEmployed) || TradingSupport.IsColumnOld(consumer, "LastName", this.Record.LastName) || TradingSupport.IsColumnOld(consumer, "PostalCode", this.Record.PostalCode) || TradingSupport.IsColumnOld(consumer, "MiddleName", this.Record.MiddleName) || TradingSupport.IsColumnOld(consumer, "PhoneNumber", this.Record.PhoneNumber) || TradingSupport.IsColumnOld(consumer, "ProvinceId", provinceId) || TradingSupport.IsColumnOld(consumer, "SocialSecurityNumber", this.Record.SocialSecurityNumber) || TradingSupport.IsColumnOld(consumer, "Suffix", this.Record.Suffix)) { updateConsumer = true; } } finally { consumer.ReleaseLock(dataModelTransaction.TransactionId); } try { //workingOrder.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); workingOrderId = workingOrder.WorkingOrderId; workingOrderVersion = workingOrder.RowVersion; matches = workingOrder.GetMatchRows(); } finally { //workingOrder.ReleaseLock(dataModelTransaction.TransactionId); } foreach (MatchRow match in matches) { if (WorkingOrderPersistence.IsSettled(dataModelTransaction, match)) { throw new FaultException <SecurityFault>( new SecurityFault("Cannot update account that is settled") { FaultCode = ErrorCode.RecordExists }, "Cannot update account that is settled"); } } // We need write access to the containing blotter in order to add a record to it. if (!TradingSupport.HasAccess(dataModelTransaction, PersistenceHelper.GetBlotterForConsumer(dataModelTransaction, consumerId), AccessRight.Write)) { throw new SecurityException("Current user does not have write access to the original blotter"); } if (updateConsumer) { dataModel.UpdateConsumer( this.Record.Address1 != null ? (object)this.Record.Address1 : DBNull.Value, this.Record.Address2 != null ? (object)this.Record.Address2 : DBNull.Value, this.Record.BankAccountNumber != null ? (object)this.Record.BankAccountNumber : DBNull.Value, this.Record.BankRoutingNumber != null ? (object)this.Record.BankRoutingNumber : DBNull.Value, this.Record.City != null ? (object)this.Record.City : DBNull.Value, consumerId, new object[] { consumerId }, this.Record.DateOfBirth != null ? (object)this.Record.DateOfBirth.Value : DBNull.Value, null, null, this.Record.FirstName != null ? (object)this.Record.FirstName : DBNull.Value, this.Record.IsEmployed != null ? (object)this.Record.IsEmployed.Value : DBNull.Value, this.Record.LastName != null ? (object)this.Record.LastName : DBNull.Value, this.Record.MiddleName != null ? (object)this.Record.MiddleName : DBNull.Value, this.Record.PhoneNumber != null ? (object)StringUtilities.CleanUpAlphaNumericString(this.Record.PhoneNumber) : DBNull.Value, this.Record.PostalCode != null ? (object)this.Record.PostalCode : DBNull.Value, provinceId, consumerVersion, this.Record.Salutation, StringUtilities.CleanUpAlphaNumericString(this.Record.SocialSecurityNumber), this.Record.Suffix != null ? (object)this.Record.Suffix : DBNull.Value); } if (updateConsumerTrust) { dataModel.UpdateConsumerTrust( null, consumerTrustId, new object[] { consumerTrustId }, null, null, consumerTrustVersion, this.Record.SavingsAccount, this.Record.SavingsBalance, this.Record.Tag != null ? (object)this.Record.Tag : DBNull.Value, null, null); } if (updateEntity) { dataModel.UpdateEntity( null, null, entityId, new object[] { entityId }, null, null, null, null, null, null, null, null, null, null, null, modified, this.Record.SavingsEntityCode, entityVersion, null, null); } if (updateSecurity) { dataModel.UpdateSecurity( null, countryId, null, null, null, 1, 1, securityVersion, securityId, new object[] { securityId }, null, null, null); } dataModel.UpdateWorkingOrder( null, this.Record.Blotter, null, null, null, null, null, null, null, null, null, null, null, modified, TradingSupport.UserId, null, workingOrderVersion, null, null, dollarsId, null, null, StatusMap.FromCode(Status.New), null, null, null, null, null, null, modified, workingOrderId, new object[] { workingOrderId }); return(consumerId); }
/// <summary>Inserts a Negotiation record using Metadata Parameters.</summary> internal static void Offer(Guid matchId, Decimal quantity) { Guid negotiationId = Guid.Empty; DataModel dataModel = new DataModel(); try { DataModel.DataLock.EnterReadLock(); MatchRow matchRow = DataModel.Match.MatchKey.Find(matchId); if (matchRow != null) { // Rule #1: Insure that there are no pending offers. foreach (NegotiationRow innerNegotiationRow in matchRow.GetNegotiationRows()) { if (innerNegotiationRow.StatusId == StatusMap.FromCode(Status.Pending)) { throw new Exception("There is already an offer pending."); } if (innerNegotiationRow.StatusId == StatusMap.FromCode(Status.Declined)) { throw new Exception("This offer has previously been declined."); } } // Time stamps and user stamps Guid createdUserId = TradingSupport.UserId; DateTime createdTime = DateTime.UtcNow; Guid modifiedUserId = TradingSupport.UserId; DateTime modifiedTime = createdTime; // This will find the contra matching record. MatchRow contraMatchRow = DataModel.Match.MatchKeyWorkingOrderIdContraOrderId.Find( new Object[] { matchRow.ContraOrderId, matchRow.WorkingOrderId }); if (contraMatchRow == null) { throw new Exception( string.Format("Corruption: the match record for {0}, {1} can't be found", matchRow.ContraOrderId, matchRow.WorkingOrderId)); } // This is the order on the other side of the match. WorkingOrderRow contraWorkingOrderRow = contraMatchRow.WorkingOrderRow; // When both sides have agreed to the Negotiation, the Destination Orders are generated. NegotiationRow contraNegotiationRow = null; foreach (NegotiationRow innerNegotiationRow in contraMatchRow.GetNegotiationRows()) { if (innerNegotiationRow.StatusId == StatusMap.FromCode(Status.Pending)) { contraNegotiationRow = innerNegotiationRow; break; } } // This means that there's an offer on the other side. if (contraNegotiationRow == null) { // There is no opposite side of this transaction yet. It will be placed in the negotation table and wait there // until it times out, or the other side accepts the offer. negotiationId = Guid.NewGuid(); dataModel.CreateNegotiation( contraWorkingOrderRow.BlotterId, null, false, contraMatchRow.MatchId, negotiationId, quantity, StatusMap.FromCode(Status.Pending)); } else { // At this point, there is an offer on both sides of the match for a follow-on order. We'll create orders and // executions for both sides of the trade for the minimum agreed upon quantity. WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; WorkingOrderRow contraOrderRow = contraNegotiationRow.MatchRow.WorkingOrderRow; // The quantity of this negotiation will be the minimum of the two offers. decimal matchedQuantity = quantity < contraNegotiationRow.Quantity ? quantity : contraNegotiationRow.Quantity; // Create the order on this side of the trade. Guid destinationOrderId = Guid.NewGuid(); dataModel.CreateDestinationOrder( workingOrderRow.BlotterId, null, null, createdTime, createdUserId, destinationId, destinationOrderId, null, null, null, null, workingOrderRow[DataModel.WorkingOrder.LimitPriceColumn], modifiedTime, modifiedUserId, matchedQuantity, workingOrderRow.OrderTypeId, workingOrderRow.SecurityId, modifiedTime, workingOrderRow.SettlementId, workingOrderRow.SideId, StateMap.FromCode(State.Acknowledged), StatusMap.FromCode(Status.New), workingOrderRow[DataModel.WorkingOrder.StopPriceColumn], workingOrderRow.TimeInForceId, modifiedTime, null, workingOrderRow.WorkingOrderId); // The the trade is executed at the current price. PriceRow priceRow = DataModel.Price.PriceKey.Find(workingOrderRow.SecurityId, workingOrderRow.SettlementId); // Create the Execution for this side of the trade. Guid executionId = Guid.NewGuid(); dataModel.CreateExecution( null, workingOrderRow.BlotterId, null, null, null, createdTime, createdUserId, destinationOrderId, StateMap.FromCode(State.Acknowledged), executionId, priceRow.LastPrice, matchedQuantity, null, null, null, modifiedTime, modifiedUserId, null, null, null, null, StateMap.FromCode(State.Sent), null, null, null, null); // There is no opposite side of this transaction yet. It will be placed in the negotation table and wait there // until it times out, or the other side accepts the offer. negotiationId = Guid.NewGuid(); dataModel.CreateNegotiation( workingOrderRow.BlotterId, executionId, false, matchId, negotiationId, quantity, StatusMap.FromCode(Status.Accepted)); // Create an order for the agreed upon quantity. // Create the order on this side of the trade. Guid contraDestinationOrderId = Guid.NewGuid(); dataModel.CreateDestinationOrder( contraWorkingOrderRow.BlotterId, null, null, createdTime, createdUserId, destinationId, contraDestinationOrderId, null, null, null, null, contraWorkingOrderRow[DataModel.WorkingOrder.LimitPriceColumn], modifiedTime, modifiedUserId, matchedQuantity, contraWorkingOrderRow.OrderTypeId, contraWorkingOrderRow.SecurityId, modifiedTime, contraWorkingOrderRow.SettlementId, contraWorkingOrderRow.SideId, StateMap.FromCode(State.Acknowledged), StatusMap.FromCode(Status.New), contraWorkingOrderRow[DataModel.WorkingOrder.StopPriceColumn], contraWorkingOrderRow.TimeInForceId, modifiedTime, null, contraWorkingOrderRow.WorkingOrderId); // Create the Execution for this side of the trade. Guid contraExecutionId = Guid.NewGuid(); dataModel.CreateExecution( null, contraWorkingOrderRow.BlotterId, null, null, null, createdTime, createdUserId, contraDestinationOrderId, StateMap.FromCode(State.Acknowledged), contraExecutionId, priceRow.LastPrice, matchedQuantity, null, null, null, modifiedTime, modifiedUserId, null, null, null, null, StateMap.FromCode(State.Sent), null, null, null, null); // Update the contra offer. dataModel.UpdateNegotiation( contraWorkingOrderRow.BlotterId, contraExecutionId, false, null, null, new Object[] { contraNegotiationRow.NegotiationId }, null, contraNegotiationRow.RowVersion, StatusMap.FromCode(Status.Accepted)); } } } finally { // Release the tables. DataModel.DataLock.ExitReadLock(); } }