/// <summary> /// Creates an XML Element representing a broker in a execution document. /// </summary> /// <param name="xmlDocument">The destination XML document.</param> /// <param name="blockOrderRow">A broker record.</param> public BlockOrderElement(XmlDocument xmlDocument, ClientMarketData.BlockOrderRow blockOrderRow) : base("BlockOrder", xmlDocument) { // The block order id is occationally useful info at the root element. AddAttribute("BlockOrderId", blockOrderRow.BlockOrderId); // It's useful to have the transaction type so we know whether to add fees and commissions or subtract // them from the net calculations. AddAttribute("TransactionTypeCode", blockOrderRow.TransactionTypeCode); // Get a shortcut to the security. ClientMarketData.SecurityRow securityRow = blockOrderRow.SecurityRowByFKSecurityBlockOrderSecurityId; // Add Fixed Income Fundamentals where they exist. foreach (ClientMarketData.DebtRow debtRow in securityRow.GetDebtRowsByFKSecurityDebtDebtId()) { // Add the fundamental data for the debt. if (!debtRow.IsIssuerIdNull()) { AddAttribute("IssuerId", debtRow.IssuerId.ToString()); } AddAttribute("DebtTypeCode", debtRow.DebtTypeCode.ToString()); AddAttribute("Coupon", debtRow.Coupon.ToString()); AddAttribute("MaturityDate", debtRow.MaturityDate.ToShortDateString()); } }
/// <summary> /// Creates an XML Element representing a blockOrder in a execution document. /// </summary> /// <param name="xmlDocument">The destination XML document.</param> /// <param name="blockOrderRow">A blockOrder record.</param> public BlockOrderElement(XmlDocument xmlDocument, ClientMarketData.BlockOrderRow blockOrderRow) : base("BlockOrder", xmlDocument) { // Add the attributes of a blockOrder to this record. AddAttribute("BlockOrderId", blockOrderRow.BlockOrderId.ToString()); // If the security exists, add the basic data to the block information. ClientMarketData.SecurityRow securityRow = blockOrderRow.SecurityRowByFKSecurityBlockOrderSecurityId; AddAttribute("SecurityId", securityRow.SecurityId.ToString()); AddAttribute("SecuritySymbol", securityRow.Symbol); AddAttribute("SecurityName", securityRow.ObjectRow.Name); // It's useful to have the transaction type so we know whether to add fees and commissions or subtract // them from the net calculations. AddAttribute("TransactionTypeCode", blockOrderRow.TransactionTypeCode.ToString()); AddAttribute("TransactionTypeDescription", blockOrderRow.TransactionTypeRow.Description); decimal quantityOrdered = 0.0M; foreach (ClientMarketData.OrderRow orderRow in blockOrderRow.GetOrderRows()) { quantityOrdered += orderRow.Quantity; } AddAttribute("QuantityOrdered", quantityOrdered.ToString()); }
public BlockOrderElement(BlockOrderDocument blockOrderDocument, ClientMarketData.BlockOrderRow blockOrderRow) : base("BlockOrder", blockOrderDocument) { this.blockOrderRow = blockOrderRow; AddAttribute("BlockOrderId", this.blockOrderRow.BlockOrderId); AddAttribute("StatusCode", this.blockOrderRow.StatusCode); AddAttribute("StatusName", this.blockOrderRow.StatusRow.Mnemonic); }
/// <summary> /// Creates a block order. /// </summary> /// <param name="configurationId">Defines which external fields are used to identify an object.</param> /// <param name="blotterId">The destination blotter for the trade.</param> /// <param name="securityId">The security.</param> /// <param name="transactionType">The type of transaction.</param> public BlockOrder(Blotter blotter, Security security, TransactionType transactionType) { // Create a block order on the server. RemoteBatch remoteBatch = new RemoteBatch(); RemoteAssembly remoteAssembly = remoteBatch.Assemblies.Add("Service.Trading"); RemoteType remoteType = remoteAssembly.Types.Add("Shadows.WebService.Trading.BlockOrder"); RemoteMethod remoteMethod = remoteType.Methods.Add("Insert"); remoteMethod.Parameters.Add("blockOrderId", DataType.Int, Direction.ReturnValue); remoteMethod.Parameters.Add("blotterId", blotter.BlotterId); remoteMethod.Parameters.Add("securityId", security.SecurityId); remoteMethod.Parameters.Add("settlementId", security.SettlementId); remoteMethod.Parameters.Add("transactionTypeCode", (int)transactionType); ClientMarketData.Execute(remoteBatch); // Now that the block order is created, construct the in-memory version of the record. int blockOrderId = (int)remoteMethod.Parameters["blockOrderId"].Value; try { // Lock the tables. Debug.Assert(!ClientMarketData.AreLocksHeld); ClientMarketData.BlockOrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.BlockOrderRow blockOrderRow = ClientMarketData.BlockOrder.FindByBlockOrderId(blockOrderId); if (blockOrderRow == null) throw new Exception(String.Format("BlockOrder {0} doesn't exist", blockOrderId)); this.blockOrderId = blockOrderRow.BlockOrderId; this.security = Security.Make(blockOrderRow.SecurityRowByFKSecurityBlockOrderSecurityId.SecurityId); this.settlement = Security.Make(blockOrderRow.SecurityRowByFKSecurityBlockOrderSettlementId.SecurityId); this.transactionType = (TransactionType)blockOrderRow.TransactionTypeCode; } finally { // Release the table locks. if (ClientMarketData.BlockOrderLock.IsReaderLockHeld) ClientMarketData.BlockOrderLock.ReleaseReaderLock(); if (ClientMarketData.ObjectLock.IsReaderLockHeld) ClientMarketData.ObjectLock.ReleaseReaderLock(); if (ClientMarketData.SecurityLock.IsReaderLockHeld) ClientMarketData.SecurityLock.ReleaseReaderLock(); Debug.Assert(!ClientMarketData.AreLocksHeld); } }
/// <summary> /// Constructs a well formed Document Object Model for a group of executions on a block order. /// </summary> /// <param name="blockOrderId">The block order for which we want a execution document.</param> public ExecutionDocument(int blockOrderId, ExecutionSet executionSet) { // Create the root element for the document. XmlElement rootElement = this.CreateElement("Execution"); this.AppendChild(rootElement); // This list is used to sort the executions. ArrayList executionsList = new ArrayList(); // Start with the block order. All the executions flow from this record. ClientMarketData.BlockOrderRow blockOrderRow = ClientMarketData.BlockOrder.FindByBlockOrderId(blockOrderId); if (blockOrderRow != null) { // This element holds all the information related to the block order. XmlElement blockOrderElement = this.CreateBlockOrderElement(blockOrderRow); rootElement.AppendChild(blockOrderElement); // We are going to run through all the executions posted against the block order and construct a // hierarchical structure of brokers. Since they aren't arranged that way in the data model, we need to // scan the whole table looking for distinct brokers. foreach (ClientMarketData.ExecutionRow executionRow in blockOrderRow.GetExecutionRows()) { // Sort the list of executions by the created time. int index; for (index = 0; index < executionsList.Count; index++) { if (((ClientMarketData.ExecutionRow)executionsList[index]).CreatedTime > executionRow.CreatedTime) { executionsList.Insert(index, executionRow); break; } } // If the row wasn't sorted into the array above, it's added to the end of the list here. if (index == executionsList.Count) { executionsList.Add(executionRow); } } // Add all of the executions as children of the broker. foreach (ClientMarketData.ExecutionRow executionRow in executionsList) { blockOrderElement.AppendChild(this.CreateGlobalExecutionElement(executionRow)); } // Add the local executions. ExecutionSet.BlockOrderRow localBlockOrder = executionSet.BlockOrder.FindByBlockOrderId(blockOrderId); if (localBlockOrder != null) { foreach (ExecutionSet.ExecutionRow executionRow in localBlockOrder.GetExecutionRows()) { if (executionRow.RowState == DataRowState.Detached) { Console.WriteLine("{0} is Detached", executionRow["ExecutionId", DataRowVersion.Original]); } blockOrderElement.AppendChild(this.CreateLocalExecutionElement(executionRow)); } } } // The placeholder is a queue for the document manager to create a new execution. this.DocumentElement.AppendChild(this.CreatePlaceholderElement()); }
/// <summary> /// Creates a broker element for the ExecutionDocument /// </summary> /// <param name="blockOrderRow">A Broker record</param> /// <returns>An element that represents a broker.</returns> public BlockOrderElement CreateBlockOrderElement(ClientMarketData.BlockOrderRow blockOrderRow) { return(new BlockOrderElement(this, blockOrderRow)); }
public static void Distribute(int blockOrderId) { // This batch will be filled in with the allocations. RemoteBatch remoteBatch = null; try { // Lock all the tables that we'll reference while building a blotter document. Debug.Assert(!ClientMarketData.AreLocksHeld); ClientMarketData.AllocationLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.BlockOrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ExecutionLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.OrderLock.AcquireReaderLock(CommonTimeout.LockWait); // Find the block order that is to be allocated. ClientMarketData.BlockOrderRow blockOrderRow = ClientMarketData.BlockOrder.FindByBlockOrderId(blockOrderId); if (blockOrderRow == null) { throw new Exception(String.Format("Block Id {0} doesn't exist", blockOrderId)); } // Aggregate the total quantity ordered. This becomes the demoninator for the pro-rata calculation. decimal orderedQuantity = 0.0M; foreach (ClientMarketData.OrderRow orderRow in blockOrderRow.GetOrderRows()) { orderedQuantity += orderRow.Quantity; } // These values will total up all the executions posted against this block order. The pro-rata // allocation will divide all the executions up against the ratio of the quantity ordered against the // total quantity ordered. The price is an average price of all the executions. decimal executedQuantity = 0.0M; decimal executedPrice = 0.0M; decimal executedCommission = 0.0M; decimal executedAccruedInterest = 0.0M; decimal executedUserFee0 = 0.0M; decimal executedUserFee1 = 0.0M; decimal executedUserFee2 = 0.0M; decimal executedUserFee3 = 0.0M; DateTime tradeDate = DateTime.MinValue; DateTime settlementDate = DateTime.MinValue; // Total up all the executions against this block. foreach (ClientMarketData.ExecutionRow executionRow in blockOrderRow.GetExecutionRows()) { executedQuantity += executionRow.Quantity; executedPrice += executionRow.Price * executionRow.Quantity; executedCommission += executionRow.Commission; executedAccruedInterest += executionRow.AccruedInterest; executedUserFee0 += executionRow.UserFee0; executedUserFee1 += executionRow.UserFee1; executedUserFee2 += executionRow.UserFee2; executedUserFee3 += executionRow.UserFee3; tradeDate = executionRow.TradeDate; settlementDate = executionRow.SettlementDate; } // Calculate the average price. decimal averagePrice = Math.Round((executedQuantity > 0.0M) ? executedPrice / executedQuantity : 0.0M, 2); // These values are used to keep track of how much has been allocated. Because of the nature of a // pro-rata allocation, there will be an odd remainder after the allocation is finished. We will // arbitrarily pick the last order in the block order and give it the remainer of the order. To do that, // totals have to be kept of all the allocations that have been created before the last one. decimal allocatedQuantity = 0.0M; decimal allocatedCommission = 0.0M; decimal allocatedAccruedInterest = 0.0M; decimal allocatedUserFee0 = 0.0M; decimal allocatedUserFee1 = 0.0M; decimal allocatedUserFee2 = 0.0M; decimal allocatedUserFee3 = 0.0M; // Put all the allocations in a single batch. remoteBatch = new RemoteBatch(); RemoteAssembly remoteAssembly = remoteBatch.Assemblies.Add("Service.Core"); RemoteType remoteType = remoteAssembly.Types.Add("Shadows.WebService.Core.Allocation"); // Allocate the order back to the original accounts. Because odd values can be generated when dividing by the // pro-rata value, the last order will get the remaining portions of the execution. int orderCounter = blockOrderRow.GetOrderRows().Length; foreach (ClientMarketData.OrderRow orderRow in blockOrderRow.GetOrderRows()) { decimal quantity; decimal commission; decimal accruedInterest; decimal userFee0; decimal userFee1; decimal userFee2; decimal userFee3; // The last account in the order is arbitrarily given the remaining parts of the execution. if (--orderCounter == 0) { quantity = executedQuantity - allocatedQuantity; commission = executedCommission - allocatedCommission; accruedInterest = executedAccruedInterest = allocatedAccruedInterest; userFee0 = executedUserFee0 - allocatedUserFee0; userFee1 = executedUserFee1 - allocatedUserFee1; userFee2 = executedUserFee2 - allocatedUserFee2; userFee3 = executedUserFee3 - allocatedUserFee3; } else { // Calcuation the proportion of the trade destined for the current order. The proportion is // based on the amount of the original order against the block total. quantity = Math.Round(executedQuantity * orderRow.Quantity / orderedQuantity, 0); commission = Math.Round(executedCommission * orderRow.Quantity / orderedQuantity, 2); accruedInterest = Math.Round(executedAccruedInterest * orderRow.Quantity / orderedQuantity, 2); userFee0 = Math.Round(executedUserFee0 * orderRow.Quantity / orderedQuantity, 2); userFee1 = Math.Round(executedUserFee1 * orderRow.Quantity / orderedQuantity, 2); userFee2 = Math.Round(executedUserFee2 * orderRow.Quantity / orderedQuantity, 2); userFee3 = Math.Round(executedUserFee3 * orderRow.Quantity / orderedQuantity, 2); } // Keep a running total of the amount allocated so far. This will be used to calculate the last order's // portion when the loop has finished. allocatedQuantity += quantity; allocatedCommission += commission; allocatedAccruedInterest += accruedInterest; allocatedUserFee0 += userFee0; allocatedUserFee1 += userFee1; allocatedUserFee2 += userFee2; allocatedUserFee3 += userFee3; // If the allocation has a positive quantity to register, then post it to the server. if (quantity > 0.0M) { // Call the web service to add the new execution. RemoteMethod remoteMethod = remoteType.Methods.Add("Insert"); remoteMethod.Parameters.Add("blockOrderId", orderRow.BlockOrderId); remoteMethod.Parameters.Add("accountId", orderRow.AccountId); remoteMethod.Parameters.Add("securityId", orderRow.SecurityId); remoteMethod.Parameters.Add("settlementId", orderRow.SettlementId); remoteMethod.Parameters.Add("positionTypeCode", orderRow.PositionTypeCode); remoteMethod.Parameters.Add("transactionTypeCode", orderRow.TransactionTypeCode); remoteMethod.Parameters.Add("quantity", quantity); remoteMethod.Parameters.Add("price", averagePrice); remoteMethod.Parameters.Add("commission", commission); remoteMethod.Parameters.Add("accruedInterest", accruedInterest); remoteMethod.Parameters.Add("userFee0", userFee0); remoteMethod.Parameters.Add("userFee1", userFee1); remoteMethod.Parameters.Add("userFee2", userFee2); remoteMethod.Parameters.Add("userFee3", userFee3); remoteMethod.Parameters.Add("tradeDate", tradeDate); remoteMethod.Parameters.Add("settlementDate", settlementDate); remoteMethod.Parameters.Add("createdTime", DateTime.Now); remoteMethod.Parameters.Add("createdLoginId", ClientPreferences.LoginId); remoteMethod.Parameters.Add("modifiedTime", DateTime.Now); remoteMethod.Parameters.Add("modifiedLoginId", ClientPreferences.LoginId); } } } catch (Exception exception) { // This signals that the batch isn't valid and shouldn't be sent. remoteBatch = null; // This will catch all remaining exceptions. Debug.WriteLine(exception.Message); } finally { // Release the locks obtained to produce the blotter report. if (ClientMarketData.AllocationLock.IsReaderLockHeld) { ClientMarketData.AllocationLock.ReleaseReaderLock(); } if (ClientMarketData.BlockOrderLock.IsReaderLockHeld) { ClientMarketData.BlockOrderLock.ReleaseReaderLock(); } if (ClientMarketData.ExecutionLock.IsReaderLockHeld) { ClientMarketData.ExecutionLock.ReleaseReaderLock(); } if (ClientMarketData.OrderLock.IsReaderLockHeld) { ClientMarketData.OrderLock.ReleaseReaderLock(); } Debug.Assert(!ClientMarketData.AreLocksHeld); } // Once the locks are release, the batch can be sent to the server. if (remoteBatch != null) { ClientMarketData.Send(remoteBatch); } }