/// <summary>Archives a Placement record.</summary> /// <param name="transaction">Commits or rejects a set of commands as a unit</param> /// <param name="placementId">The value for the PlacementId column.</param> /// <param name="rowVersion">The value for the RowVersion column.</param> /// <param name="archive">true to archive the object, false to unarchive it.</param> public static void Archive(Transaction transaction, int placementId, long rowVersion) { // Accessor for the Placement Table. ServerMarketData.PlacementDataTable placementTable = ServerMarketData.Placement; // Rule #1: Make sure the record exists before updating it. ServerMarketData.PlacementRow placementRow = placementTable.FindByPlacementId(placementId); if ((placementRow == null)) { throw new Exception(string.Format("The Placement table does not have an element identified by {0}", placementId)); } // Rule #2: Optimistic Concurrency Check if ((placementRow.RowVersion != rowVersion)) { throw new System.Exception("This record is busy. Please try again later."); } // Archive the child records. // Delete the record in the ADO database. transaction.DataRows.Add(placementRow); placementRow.Delete(); // Archive the record in the SQL database. SqlCommand sqlCommand = new SqlCommand("update Placement set Archived = 1 where \"PlacementId\"=@placementId"); sqlCommand.Connection = transaction.SqlConnection; sqlCommand.Transaction = transaction.SqlTransaction; sqlCommand.Parameters.Add("@placementId", @placementId); sqlCommand.ExecuteNonQuery(); }
/// <summary>Updates a Placement record.</summary> /// <param name="transaction">Commits or rejects a set of commands as a unit</param> /// <param name="placementId">The value for the PlacementId column.</param> /// <param name="blockOrderId">The value for the BlockOrderId column.</param> /// <param name="brokerId">The value for the BrokerId column.</param> /// <param name="timeInForceCode">The value for the TimeInForceCode column.</param> /// <param name="orderTypeCode">The value for the OrderTypeCode column.</param> /// <param name="rowVersion">The value for the RowVersion column.</param> /// <param name="isRouted">The value for the IsRouted column.</param> /// <param name="quantity">The value for the Quantity column.</param> /// <param name="price1">The value for the Price1 column.</param> /// <param name="price2">The value for the Price2 column.</param> public static void Update(Transaction transaction, int placementId, object blockOrderId, object brokerId, object timeInForceCode, object orderTypeCode, ref long rowVersion, object isRouted, object quantity, object price1, object price2) { // These values are provided by the server. DateTime modifiedTime = DateTime.Now; int modifiedLoginId = ServerMarketData.LoginId; // Rule #1: Make sure the record exists before updating it. ServerMarketData.PlacementRow placementRow = ServerMarketData.Placement.FindByPlacementId(placementId); if ((placementRow == null)) { throw new Exception("This Placement has been deleted by someone else"); } // Rule #2: The block order exist. ServerMarketData.BlockOrderRow blockOrderRow = ServerMarketData.BlockOrder.FindByBlockOrderId((int)blockOrderId); if (blockOrderRow == null) { throw new Exception("This block order has been deleted by someone else"); } // Rule #3: The Block Order is active if (blockOrderRow.StatusCode == Shadows.Quasar.Common.Status.Closed || blockOrderRow.StatusCode == Shadows.Quasar.Common.Status.Confirmed || blockOrderRow.StatusCode == Shadows.Quasar.Common.Status.Pending) { throw new Exception("This order isn't active"); } // Rule #4: Quantity executed doesn't exceed the quantity ordered. decimal quantityOrdered = 0.0M; foreach (ServerMarketData.OrderRow orderSumRow in blockOrderRow.GetOrderRows()) { quantityOrdered += orderSumRow.Quantity; } decimal quantityPlaced = 0.0M; foreach (ServerMarketData.PlacementRow placementSumRow in blockOrderRow.GetPlacementRows()) { quantityPlaced += placementSumRow.Quantity; } if (quantityPlaced - placementRow.Quantity + (decimal)quantity > quantityOrdered) { throw new Exception("The quantity placed is more than the quantity ordered."); } // Call the primitive library to handle the rest of the operation. Shadows.WebService.Core.Placement.Update(transaction, placementId, blockOrderId, brokerId, timeInForceCode, orderTypeCode, ref rowVersion, null, isRouted, quantity, price1, price2, null, null, modifiedTime, modifiedLoginId); }
/// <summary> /// This thread takes placements off the queue and creates orders in the local order book for them. /// </summary> private static void PlacementHandler() { while (true) { // Wait here for the next tick to come in. Broker.placementQueue.WaitOne(); // Get the next placement from the queue. int placementId = Broker.placementQueue.Dequeue(); try { // Lock the tables. Debug.Assert(!ServerMarketData.AreLocksHeld); ServerMarketData.BlockOrderLock.AcquireReaderLock(Timeout.Infinite); ServerMarketData.PlacementLock.AcquireReaderLock(Timeout.Infinite); // Find the placement row. ServerMarketData.PlacementRow placementRow = ServerMarketData.Placement.FindByPlacementId(placementId); if (placementRow == null) { return; } // Information about the security is found in the block. It isn't part of the placement so a BlockOrder // record is needed to get some of the ancillary data. ServerMarketData.BlockOrderRow blockOrderRow = placementRow.BlockOrderRow; // The execution quantity needs to be calculated to prevent over execution when the simulator is // restarted. Count up the quantity executed against this placement. While this calcuation isn't // perfect -- it will miss when there are multiple placements to the same broker -- it is good enough for // the simulator to restart itself. decimal quantityExecuted = 0.0M; foreach (ServerMarketData.ExecutionRow executionRow in blockOrderRow.GetExecutionRows()) { if (executionRow.BrokerId == placementRow.BrokerId) { quantityExecuted += executionRow.Quantity; } } } catch (Exception exception) { // Write the error and stack trace out to the debug listener Debug.WriteLine(String.Format("{0}, {1}", exception.Message, exception.StackTrace)); } finally { // Release the global tables. if (ServerMarketData.BlockOrderLock.IsReaderLockHeld) { ServerMarketData.BlockOrderLock.ReleaseReaderLock(); } if (ServerMarketData.PlacementLock.IsReaderLockHeld) { ServerMarketData.PlacementLock.ReleaseReaderLock(); } Debug.Assert(!ServerMarketData.AreLocksHeld); } } }
/// <summary> /// This thread takes placements off the queue and creates orders in the local order book for them. /// </summary> private static void PlacementHandler() { while (true) { // Wait here for the next tick to come in. Broker.placementQueue.WaitOne(); // Get the next placement from the queue. int placementId = Broker.placementQueue.Dequeue(); try { // Lock the Order Book. Broker.orderBookLock.AcquireWriterLock(CommonTimeout.LockWait); // Lock the tables. Debug.Assert(!ServerMarketData.AreLocksHeld); ServerMarketData.BlockOrderLock.AcquireReaderLock(Timeout.Infinite); ServerMarketData.PlacementLock.AcquireReaderLock(Timeout.Infinite); // Find the placement row. ServerMarketData.PlacementRow placementRow = ServerMarketData.Placement.FindByPlacementId(placementId); if (placementRow == null) { return; } // Information about the security is found in the block. It isn't part of the placement so a BlockOrder // record is needed to get some of the ancillary data. ServerMarketData.BlockOrderRow blockOrderRow = placementRow.BlockOrderRow; // The execution quantity needs to be calculated to prevent over execution when the simulator is // restarted. Count up the quantity executed against this placement. While this calcuation isn't // perfect -- it will miss when there are multiple placements to the same broker -- it is good enough for // the simulator to restart itself. decimal quantityExecuted = 0.0M; foreach (ServerMarketData.ExecutionRow executionRow in blockOrderRow.GetExecutionRows()) { if (executionRow.BrokerId == placementRow.BrokerId) { quantityExecuted += executionRow.Quantity; } } // Create a new order book record and copy the data from the placement into the order. OrderBook.OrderRow orderRow = Broker.orderBook.Order.NewOrderRow(); orderRow.BlockOrderId = placementRow.BlockOrderId; orderRow.BrokerId = placementRow.BrokerId; orderRow.SecurityId = blockOrderRow.SecurityId; orderRow.QuantityOrdered = placementRow.Quantity; orderRow.QuantityExecuted = quantityExecuted; orderRow.TimeInForceCode = placementRow.TimeInForceCode; orderRow.OrderTypeCode = placementRow.OrderTypeCode; if (!placementRow.IsPrice1Null()) { orderRow.Price1 = placementRow.Price1; } if (!placementRow.IsPrice2Null()) { orderRow.Price2 = placementRow.Price2; } // Add the new record to the book and accept the changes. Broker.orderBook.Order.AddOrderRow(orderRow); orderRow.AcceptChanges(); // Signal to the execution thread that there are orders in the local book to be filled. Broker.orderBookEvent.Set(); } catch (Exception exception) { // Write the error and stack trace out to the debug listener Debug.WriteLine(String.Format("{0}, {1}", exception.Message, exception.StackTrace)); } finally { // Release the order book. if (Broker.orderBookLock.IsWriterLockHeld) { Broker.orderBookLock.ReleaseWriterLock(); } // Release the global tables. if (ServerMarketData.BlockOrderLock.IsReaderLockHeld) { ServerMarketData.BlockOrderLock.ReleaseReaderLock(); } if (ServerMarketData.PlacementLock.IsReaderLockHeld) { ServerMarketData.PlacementLock.ReleaseReaderLock(); } Debug.Assert(!ServerMarketData.AreLocksHeld); } } }