/// <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()); }
protected void Initialize(ClientMarketData.SecurityRow securityRow) { // Initialize the base class. base.Initialize(securityRow.ObjectRow); // Find the default settlement security for equities. if (securityRow.SecurityTypeCode == Common.SecurityType.Debt) { foreach (ClientMarketData.DebtRow debtRow in securityRow.GetDebtRowsByFKSecurityDebtDebtId()) { this.settlementId = debtRow.SettlementId; } } // Find the default settlement security for equities. if (securityRow.SecurityTypeCode == Common.SecurityType.Equity) { foreach (ClientMarketData.EquityRow equityRow in securityRow.GetEquityRowsByFKSecurityEquityEquityId()) { this.settlementId = equityRow.SettlementId; } } // Initialize the record from the data model. this.securityTypeCode = securityRow.SecurityTypeCode; this.quantityFactor = securityRow.QuantityFactor; this.priceFactor = securityRow.PriceFactor; this.symbol = securityRow.Symbol; }
internal static Security Make(ClientMarketData.SecurityRow securityRow) { Security security = new Security(); security.Initialize(securityRow); return(security); }
/// <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> /// Initializes a Security. /// </summary> /// <param name="configurationId">Defines which external fields are used to identify an object.</param> /// <param name="securityId">The security identifier.</param> /// <returns>A security record, null if the identifier doesn't exist.</returns> protected override void Initialize(int securityId) { // Use the specified configuration to find the internal security identifier. ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(securityId); if (securityRow == null) { throw new Exception(String.Format("Security {0} doesn't exist", securityId)); } Initialize(securityRow); }
/// <summary> /// Recursively creates instructions to delete proposed order of the given position. /// </summary> /// <param name="remoteBatch">The object type containing the method to delete the order relationship.</param> /// <param name="remoteTransaction">Groups several commands into a unit for execution.</param> /// <param name="accountRow">An account record, used to select proposed order records.</param> private static void Delete(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.AccountRow accountRow, ClientMarketData.SecurityRow securityRow, int positionTypeCode) { // Run through each of the proposed orders in this account and create a record to have them deleted. object[] key = new object[] { accountRow.AccountId, securityRow.SecurityId, positionTypeCode }; foreach (DataRowView dataRowView in MarketData.ProposedOrder.UKProposedOrderAccountIdSecurityIdPositionTypeCode.FindRows(key)) { // This is used to reference the current proposed order that matches the position criteria. ClientMarketData.ProposedOrderRow proposedOrderRow = (ClientMarketData.ProposedOrderRow)dataRowView.Row; // Child proposed orders aren't deleted directly, they can only be deleted when the parent is deleted. The best // example of this is cash. An account can have both child cash (related to an equity trade) or parent cash (cash // added directly to the account with no offsetting trade). If a reqest is made to delete cash, only the parent // cash should be deleted. The account will appear to have a cash balance until the equity attached to the child // cash is deleted. if (!Relationship.IsChildProposedOrder(proposedOrderRow)) { Delete(remoteBatch, remoteTransaction, proposedOrderRow); } } }
/// <summary> /// Notifies the user that a match opportunity exists. /// </summary> /// <param name="parameter">The thread initialization parameters.</param> private void NotifyUser(object parameter) { // Extract the thread parameters. int matchId = (int)parameter; // The symbol, title and the bitmap for the corporate logo will be retrieved from the data model in the code below. // They will be used to initialize the pop-up dialog after the locks on the data model have been released. string symbol = string.Empty; string title = string.Empty; Bitmap logo = null; try { // Lock the tables. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.MatchLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.NegotiationLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.OrderTypeLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.WorkingOrderLock.AcquireReaderLock(ClientTimeout.LockWait); // The match record, working order, order type and security records are used to construct the title, symbol and // logo used by the notification window. ClientMarketData.MatchRow matchRow = ClientMarketData.Match.FindByMatchId(matchId); ClientMarketData.WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; ClientMarketData.OrderTypeRow orderTypeRow = workingOrderRow.OrderTypeRow; ClientMarketData.SecurityRow securityRow = workingOrderRow.SecurityRowBySecurityWorkingOrderSecurityId; // Get the security symbol. symbol = securityRow.Symbol; // Create a logo bitmap. if (!securityRow.IsLogoNull()) { MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(securityRow.Logo)); logo = new Bitmap(memoryStream); } // Construct the title for the notification window. title = string.Format("{0} of {1}", orderTypeRow.Description, symbol); } finally { // Release the locks. if (ClientMarketData.MatchLock.IsReaderLockHeld) { ClientMarketData.MatchLock.ReleaseReaderLock(); } if (ClientMarketData.NegotiationLock.IsReaderLockHeld) { ClientMarketData.NegotiationLock.ReleaseReaderLock(); } if (ClientMarketData.OrderTypeLock.IsReaderLockHeld) { ClientMarketData.OrderTypeLock.ReleaseReaderLock(); } if (ClientMarketData.SecurityLock.IsReaderLockHeld) { ClientMarketData.SecurityLock.ReleaseReaderLock(); } if (ClientMarketData.WorkingOrderLock.IsReaderLockHeld) { ClientMarketData.WorkingOrderLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } // The notification window looks and acts like the Microsoft Instant Messaging window. It will pop up in the lower // right hand corner of the screen with a title, the corporate logo and a chance to either accept or decline the // opportunity for a match. NotificationWindow notificationWindow = new NotificationWindow(); notificationWindow.MatchId = matchId; notificationWindow.Symbol = symbol; notificationWindow.Message = title; notificationWindow.CompanyLogo = logo; notificationWindow.Accept += new MatchEventHandler(AcceptNegotiation); notificationWindow.Decline += new MatchEventHandler(DeclineNegotiation); notificationWindow.ChangeOptions += new EventHandler(ChangeOptions); notificationWindow.Show(); }
internal static Position Make(int accountId, int securityId, int positionTypeCode) { Position position = new Position(); ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(accountId); if (accountRow == null) { throw new Exception(string.Format("Account {0} doesn't exist", accountId)); } ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(securityId); if (securityRow == null) { throw new Exception(string.Format("Security {0} doesn't exist", securityId)); } // Look up the position record. ClientMarketData.PositionRow positionRow = ClientMarketData.Position.FindByAccountIdSecurityIdPositionTypeCode(accountId, securityId, positionTypeCode); // We can always create a position. There may be no information associated with it, but it can be created just the // same. position.account = Account.Make(accountId); position.security = Security.Make(securityId); position.positionType = (PositionType)positionTypeCode; // If we have a record for the price/currency combination, then update it with the new price. if (positionRow != null) { if (!positionRow.IsUserData0Null()) { position.userData0 = positionRow.UserData0; } if (!positionRow.IsUserData1Null()) { position.userData1 = positionRow.UserData1; } if (!positionRow.IsUserData2Null()) { position.userData2 = positionRow.UserData2; } if (!positionRow.IsUserData3Null()) { position.userData3 = positionRow.UserData3; } if (!positionRow.IsUserData4Null()) { position.userData4 = positionRow.UserData4; } if (!positionRow.IsUserData5Null()) { position.userData5 = positionRow.UserData5; } if (!positionRow.IsUserData6Null()) { position.userData6 = positionRow.UserData6; } if (!positionRow.IsUserData7Null()) { position.userData7 = positionRow.UserData7; } } return(position); }
public decimal GetMarketValue(Security security, int positionType) { try { // Lock the tables Debug.Assert(!ClientMarketData.AreLocksHeld); ClientMarketData.AccountLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.AllocationLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.CurrencyLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.DebtLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.EquityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.OrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.PriceLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ProposedOrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SectorLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TaxLotLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TransactionTypeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(this.AccountId); ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(security.SecurityId); return(Common.MarketValue.Calculate(accountRow.CurrencyRow, accountRow, securityRow, positionType, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts)); } finally { // Release the table locks. if (ClientMarketData.AccountLock.IsReaderLockHeld) { ClientMarketData.AccountLock.ReleaseReaderLock(); } if (ClientMarketData.AllocationLock.IsReaderLockHeld) { ClientMarketData.AllocationLock.ReleaseReaderLock(); } if (ClientMarketData.CurrencyLock.IsReaderLockHeld) { ClientMarketData.CurrencyLock.ReleaseReaderLock(); } if (ClientMarketData.DebtLock.IsReaderLockHeld) { ClientMarketData.DebtLock.ReleaseReaderLock(); } if (ClientMarketData.EquityLock.IsReaderLockHeld) { ClientMarketData.EquityLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } if (ClientMarketData.OrderLock.IsReaderLockHeld) { ClientMarketData.OrderLock.ReleaseReaderLock(); } if (ClientMarketData.PriceLock.IsReaderLockHeld) { ClientMarketData.PriceLock.ReleaseReaderLock(); } if (ClientMarketData.ProposedOrderLock.IsReaderLockHeld) { ClientMarketData.ProposedOrderLock.ReleaseReaderLock(); } if (ClientMarketData.SectorLock.IsReaderLockHeld) { ClientMarketData.SectorLock.ReleaseReaderLock(); } if (ClientMarketData.SecurityLock.IsReaderLockHeld) { ClientMarketData.SecurityLock.ReleaseReaderLock(); } if (ClientMarketData.TaxLotLock.IsReaderLockHeld) { ClientMarketData.TaxLotLock.ReleaseReaderLock(); } if (ClientMarketData.TransactionTypeLock.IsReaderLockHeld) { ClientMarketData.TransactionTypeLock.ReleaseReaderLock(); } Debug.Assert(!ClientMarketData.AreLocksHeld); } }
/// <summary> /// Indicates if a given security is part of the document's security classification scheme. /// </summary> /// <param name="securityRow">A security to be tested.</param> /// <returns>True if the security belongs to the document's classification scheme.</returns> /// <remarks> /// Table locks needed: /// Read: ObjectTree /// Read: Objects /// </remarks> private bool IsSecurityInSector(ClientMarketData.SectorRow sectorRow, ClientMarketData.SecurityRow securityRow) { // Call the generic method to determine the relationship between the sector and the security. return(Shadows.Quasar.Common.Object.IsParent(sectorRow.ObjectRow, securityRow.ObjectRow)); }
/// <summary> /// Creates a command in a RemoteBatch structure to insert a proposed order. /// </summary> /// <param name="remoteBatch"></param> /// <param name="remoteTransaction"></param> /// <param name="accountRow"></param> /// <param name="securityRow"></param> /// <param name="positionTypeCode"></param> /// <param name="quantityInstruction"></param> private static void Insert(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.AccountRow accountRow, ClientMarketData.SecurityRow securityRow, int positionTypeCode, decimal quantityInstruction) { // These define the assembly and the types within those assemblies that will be used to create the proposed orders on // the middle tier. RemoteAssembly remoteAssembly = remoteBatch.Assemblies.Add("Service.Core"); RemoteType proposedOrderType = remoteAssembly.Types.Add("Shadows.WebService.Core.ProposedOrder"); RemoteType proposedOrderTreeType = remoteAssembly.Types.Add("Shadows.WebService.Core.ProposedOrderTree"); // Find the default settlement for this order. int settlementId = Shadows.Quasar.Common.Security.GetDefaultSettlementId(securityRow); // As a convention between the rebalancing section and the order generation, the parentQuantity passed into this method // is a signed value where the negative values are treated as 'Sell' instructions and the positive values meaning // 'Buy'. This will adjust the parentQuantity so the trading methods can deal with an unsigned value, which is more // natural for trading. decimal parentQuantity = Math.Abs(quantityInstruction); // This will turn the signed parentQuantity into an absolute parentQuantity and a transaction code (e.g. -1000 is // turned into a SELL of 1000 shares). int parentTransactionTypeCode = TransactionType.Calculate(securityRow.SecurityTypeCode, positionTypeCode, quantityInstruction); // The time in force first comes from the user preferences, next, account settings and finally defaults to a day // orders. int timeInForceCode = !ClientPreferences.IsTimeInForceCodeNull() ? ClientPreferences.TimeInForceCode : !accountRow.IsTimeInForceCodeNull() ? accountRow.TimeInForceCode : TimeInForce.DAY; // The destination blotter comes first from the user preferences, second from the account preferences, and finally uses // the auto-routing logic. int parentBlotterId = ClientPreferences.IsBlotterIdNull() ? (accountRow.IsBlotterIdNull() ? TradingSupport.AutoRoute(securityRow, parentQuantity) : accountRow.BlotterId) : ClientPreferences.BlotterId; // Create a command to delete the relationship between the parent and child. RemoteMethod insertParent = proposedOrderType.Methods.Add("Insert"); insertParent.Transaction = remoteTransaction; insertParent.Parameters.Add("proposedOrderId", DataType.Int, Direction.ReturnValue); insertParent.Parameters.Add("blotterId", parentBlotterId); insertParent.Parameters.Add("accountId", accountRow.AccountId); insertParent.Parameters.Add("securityId", securityRow.SecurityId); insertParent.Parameters.Add("settlementId", settlementId); insertParent.Parameters.Add("positionTypeCode", positionTypeCode); insertParent.Parameters.Add("transactionTypeCode", parentTransactionTypeCode); insertParent.Parameters.Add("timeInForceCode", timeInForceCode); insertParent.Parameters.Add("orderTypeCode", OrderType.Market); insertParent.Parameters.Add("quantity", parentQuantity); // Now it's time to create an order for the settlement currency. if (securityRow.SecurityTypeCode == SecurityType.Equity || securityRow.SecurityTypeCode == SecurityType.Debt) { // The underlying currency is needed for the market value calculations. ClientMarketData.CurrencyRow currencyRow = MarketData.Currency.FindByCurrencyId(settlementId); decimal marketValue = parentQuantity * securityRow.QuantityFactor * Price.Security(currencyRow, securityRow) * securityRow.PriceFactor * TransactionType.GetCashSign(parentTransactionTypeCode); // The stragegy for handling the settlement currency changes is to calculate the old market value, calculate the // new market value, and add the difference to the running total for the settlement currency of this security. The // new market value is the impact of the trade that was just entered. int childTransactionTypeCode = TransactionType.Calculate(securityRow.SecurityTypeCode, positionTypeCode, marketValue); decimal childQuantity = Math.Abs(marketValue); // The destination blotter comes first from the user preferences, second from the account preferences, and finally // uses the auto-routing logic. int childBlotterId = ClientPreferences.IsBlotterIdNull() ? (accountRow.IsBlotterIdNull() ? TradingSupport.AutoRoute(currencyRow.SecurityRow, childQuantity) : accountRow.BlotterId) : ClientPreferences.BlotterId; // Fill in the rest of the fields and the defaulted fields for this order. Create a command to delete the // relationship between the parent and child. RemoteMethod insertChild = proposedOrderType.Methods.Add("Insert"); insertChild.Transaction = remoteTransaction; insertChild.Parameters.Add("proposedOrderId", DataType.Int, Direction.ReturnValue); insertChild.Parameters.Add("blotterId", childBlotterId); insertChild.Parameters.Add("accountId", accountRow.AccountId); insertChild.Parameters.Add("securityId", settlementId); insertChild.Parameters.Add("settlementId", settlementId); insertChild.Parameters.Add("transactionTypeCode", childTransactionTypeCode); insertChild.Parameters.Add("positionTypeCode", positionTypeCode); insertChild.Parameters.Add("timeInForceCode", timeInForceCode); insertChild.Parameters.Add("orderTypeCode", OrderType.Market); insertChild.Parameters.Add("quantity", childQuantity); RemoteMethod insertRelation = proposedOrderTreeType.Methods.Add("Insert"); insertRelation.Transaction = remoteTransaction; insertRelation.Parameters.Add("parentId", insertParent.Parameters["proposedOrderId"]); insertRelation.Parameters.Add("childId", insertChild.Parameters["proposedOrderId"]); } }
/// <summary> /// Initialize the data used in this application. /// </summary> private void InitializeData(object parameter) { string title = string.Empty; string symbol = string.Empty; string name = string.Empty; Bitmap logo = null; decimal leavesQuantity = 0.0m; decimal minimumQuantity = 0.0m; NegotiationState negotiationState = NegotiationState.None; try { // Lock the tables. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.MatchLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.NegotiationLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.OrderTypeLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.WorkingOrderLock.AcquireReaderLock(ClientTimeout.LockWait); // Find the Match record. ClientMarketData.MatchRow matchRow = ClientMarketData.Match.FindByMatchId(this.matchId); ClientMarketData.WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; ClientMarketData.OrderTypeRow orderTypeRow = workingOrderRow.OrderTypeRow; ClientMarketData.SecurityRow securityRow = workingOrderRow.SecurityRowBySecurityWorkingOrderSecurityId; symbol = securityRow.Symbol; name = securityRow.ObjectRow.Name; minimumQuantity = securityRow.MinimumQuantity; if (!securityRow.IsLogoNull()) { MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(securityRow.Logo)); logo = new Bitmap(memoryStream); } title = string.Format("{0} of {1}", orderTypeRow.Description, symbol); leavesQuantity = workingOrderRow.SubmittedQuantity; foreach (ClientMarketData.DestinationOrderRow destinationOrderRow in workingOrderRow.GetDestinationOrderRows()) { foreach (ClientMarketData.ExecutionRow executionRow in destinationOrderRow.GetExecutionRows()) { leavesQuantity -= executionRow.ExecutionQuantity; } } } finally { // Release the locks. if (ClientMarketData.MatchLock.IsReaderLockHeld) { ClientMarketData.MatchLock.ReleaseReaderLock(); } if (ClientMarketData.NegotiationLock.IsReaderLockHeld) { ClientMarketData.NegotiationLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.OrderTypeLock.IsReaderLockHeld) { ClientMarketData.OrderTypeLock.ReleaseReaderLock(); } if (ClientMarketData.SecurityLock.IsReaderLockHeld) { ClientMarketData.SecurityLock.ReleaseReaderLock(); } if (ClientMarketData.WorkingOrderLock.IsReaderLockHeld) { ClientMarketData.WorkingOrderLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } Invoke(new SetDialogAttributesDelegate(SetDialogAttributes), new object[] { title, symbol, name, logo, leavesQuantity, negotiationState }); }
/// <summary> /// Creates an Equity Element for an Appriasal Document. /// </summary> /// <param name="appraisalDocument">The parent Xml Document.</param> /// <param name="equityRow">An equity record from the data model.</param> /// <param name="PositionTypeCode">Whether a short or long position.</param> public EquityElement(AppraisalDocument appraisalDocument, ClientMarketData.EquityRow equityRow, AppraisalSet.PositionRow positionRow) : base("Equity", appraisalDocument) { // These records are used to access information held in the ancestors. ClientMarketData.SecurityRow securityRow = equityRow.SecurityRowByFKSecurityEquityEquityId; ClientMarketData.ObjectRow objectRow = securityRow.ObjectRow; // Add the essential attributes for this element. AddAttribute("SecurityId", equityRow.EquityId.ToString()); AddAttribute("PositionTypeCode", positionRow.PositionTypeCode.ToString()); AddAttribute("Name", objectRow.Name.ToString()); AddAttribute("PriceFactor", securityRow.PriceFactor.ToString()); AddAttribute("QuantityFactor", securityRow.QuantityFactor.ToString()); // Find the price based on the default currency found in the equity record. If the security master doesn't // have a price for this security, provide dummy values to insure a market value can be calculated. Zero is a // perfectly reasonable interpretation of a missing price. ClientMarketData.PriceRow securityPrice = ClientMarketData.Price.FindBySecurityIdCurrencyId( securityRow.SecurityId, equityRow.SettlementId); if (securityPrice == null) { AddAttribute("Price", "0.0"); } else { if (ClientPreferences.Pricing == Pricing.Close) { AddAttribute("Price", securityPrice.ClosePrice.ToString()); } if (ClientPreferences.Pricing == Pricing.Last) { AddAttribute("Price", securityPrice.LastPrice.ToString()); } } // Find the crossing price. This is used to convert any local price into the account's base currency. Provide // defaults if the crossing price can be found. While this will lead to wrong values, they will be obvious on // the appraisal. ClientMarketData.PriceRow currencyPrice = ClientMarketData.Price.FindBySecurityIdCurrencyId( equityRow.SettlementId, appraisalDocument.AccountRow.CurrencyRow.CurrencyId); if (currencyPrice == null) { AddAttribute("CloseCrossPrice", "0.0"); AddAttribute("LastCrossPrice", "0.0"); } else { AddAttribute("CloseCrossPrice", currencyPrice.ClosePrice.ToString()); AddAttribute("LastCrossPrice", currencyPrice.LastPrice.ToString()); } // Add a target percentage if one is associated with this security. ClientMarketData.PositionTargetRow positionTargetRow = ClientMarketData.PositionTarget.FindByModelIdSecurityIdPositionTypeCode( appraisalDocument.ModelRow.ModelId, equityRow.EquityId, positionRow.PositionTypeCode); if (positionTargetRow != null) { AddAttribute("ModelPercent", positionTargetRow.Percent.ToString()); } // If there is a position record associated with this, er.. position, then add the externally supplied data // to the record. Since the tax lots are always aggregated as we need them into a position, there's no static // table that keeps position data. This information is generally from an outside system that is related to // the position, such as risk metrics or quantitative calculations. ClientMarketData.PositionRow position = ClientMarketData.Position.FindByAccountIdSecurityIdPositionTypeCode( appraisalDocument.AccountRow.AccountId, positionRow.SecurityId, positionRow.PositionTypeCode); if (position != null) { if (!position.IsUserData0Null()) { AddAttribute("UserData0", position.UserData0.ToString()); } if (!position.IsUserData1Null()) { AddAttribute("UserData1", position.UserData1.ToString()); } if (!position.IsUserData2Null()) { AddAttribute("UserData2", position.UserData2.ToString()); } if (!position.IsUserData3Null()) { AddAttribute("UserData3", position.UserData3.ToString()); } if (!position.IsUserData4Null()) { AddAttribute("UserData4", position.UserData4.ToString()); } if (!position.IsUserData5Null()) { AddAttribute("UserData5", position.UserData5.ToString()); } if (!position.IsUserData6Null()) { AddAttribute("UserData6", position.UserData6.ToString()); } if (!position.IsUserData7Null()) { AddAttribute("UserData7", position.UserData7.ToString()); } } // Add the account level aggregates for this security/position type pair. foreach (AppraisalSet.AccountRow accountRow in positionRow.GetAccountRows()) { this.AppendChild(new AccountElement(appraisalDocument, accountRow)); } }
/// <summary> /// Creates a Currency Element for an Appraisal Document. /// </summary> /// <param name="appraisalDocument">The parent Xml Document.</param> /// <param name="currencyRow">A currency record in the data model.</param> /// <param name="PositionTypeCode">Whether a short or long position.</param> public CurrencyElement(AppraisalDocument appraisalDocument, ClientMarketData.CurrencyRow currencyRow, AppraisalSet.PositionRow positionRow) : base("Currency", appraisalDocument) { // These records are used to access information held in the ancestors. ClientMarketData.SecurityRow securityRow = currencyRow.SecurityRow; ClientMarketData.ObjectRow objectRow = securityRow.ObjectRow; // Add the essential attributes for this element. AddAttribute("SecurityId", currencyRow.CurrencyId.ToString()); AddAttribute("PositionTypeCode", positionRow.PositionTypeCode.ToString()); AddAttribute("Name", objectRow.Name.ToString()); // Add the price info. ClientMarketData.PriceRow priceRow = ClientMarketData.Price.FindBySecurityIdCurrencyId(securityRow.SecurityId, appraisalDocument.AccountRow.CurrencyRow.CurrencyId); if (priceRow == null) { AddAttribute("Price", "0.0"); } else { if (ClientPreferences.Pricing == Pricing.Close) { AddAttribute("Price", priceRow.ClosePrice.ToString()); } if (ClientPreferences.Pricing == Pricing.Last) { AddAttribute("Price", priceRow.LastPrice.ToString()); } } // Add a target percentage if one is associated with this security. ClientMarketData.PositionTargetRow positionTargetRow = ClientMarketData.PositionTarget.FindByModelIdSecurityIdPositionTypeCode( appraisalDocument.ModelRow.ModelId, currencyRow.CurrencyId, positionRow.PositionTypeCode); if (positionTargetRow != null) { AddAttribute("ModelPercent", positionTargetRow.Percent.ToString()); } // If there is a position record associated with this, er.. position, then add the externally supplied data // to the record. Since the tax lots are always aggregated as we need them into a position, there's no static // table that keeps position data. This information is generally from an outside system that is related to // the position, such as risk metrics or quantitative calculations. ClientMarketData.PositionRow position = ClientMarketData.Position.FindByAccountIdSecurityIdPositionTypeCode( appraisalDocument.AccountRow.AccountId, positionRow.SecurityId, positionRow.PositionTypeCode); if (position != null) { if (!position.IsUserData0Null()) { AddAttribute("UserData0", position.UserData0.ToString()); } if (!position.IsUserData1Null()) { AddAttribute("UserData1", position.UserData1.ToString()); } if (!position.IsUserData2Null()) { AddAttribute("UserData2", position.UserData2.ToString()); } if (!position.IsUserData3Null()) { AddAttribute("UserData3", position.UserData3.ToString()); } if (!position.IsUserData4Null()) { AddAttribute("UserData4", position.UserData4.ToString()); } if (!position.IsUserData5Null()) { AddAttribute("UserData5", position.UserData5.ToString()); } if (!position.IsUserData6Null()) { AddAttribute("UserData6", position.UserData6.ToString()); } if (!position.IsUserData7Null()) { AddAttribute("UserData7", position.UserData7.ToString()); } } // Append the account level aggregates to this security/position type element. foreach (AppraisalSet.AccountRow accountRow in positionRow.GetAccountRows()) { this.AppendChild(new AccountElement(appraisalDocument, accountRow)); } }
public void CalculateFields() { // The block order document has an operation that displays distinct values when the occur. For instance, if // all the orders in the block have the same time in force, then that time in force will appear in the // document at the block level. However, if any one of the elements is different from the others, the same // column would be blanked out to show that there is more than one value associated with this attribute of the // block. The first order is used as a seed for this operation. If any element in the block doesn't share // the first order's attribute, then that attribute is left blank. decimal quantityOrdered = 0.0M; OrdersElement firstOrder = (OrdersElement)this.SelectSingleNode(".//Order"); if (firstOrder != null) { // Aggregates by Orders. int orders = 0; int securityIdSum = 0; int accountIdSum = 0; int transactionTypeCodeSum = 0; int timeInForceCodeSum = 0; int orderTypeCodeSum = 0; decimal price1Sum = 0.0M; decimal price2Sum = 0.0M; foreach (OrdersElement ordersElement in this.SelectNodes("Order")) { orders++; securityIdSum += ordersElement.OrderRow.SecurityId; accountIdSum += ordersElement.OrderRow.AccountId; transactionTypeCodeSum += ordersElement.OrderRow.TransactionTypeCode; timeInForceCodeSum += ordersElement.OrderRow.TimeInForceCode; orderTypeCodeSum += ordersElement.OrderRow.OrderTypeCode; price1Sum += ordersElement.OrderRow.IsPrice1Null() ? 0.0M : ordersElement.OrderRow.Price1; price2Sum += ordersElement.OrderRow.IsPrice2Null() ? 0.0M : ordersElement.OrderRow.Price2; quantityOrdered += ordersElement.OrderRow.Quantity; } if (securityIdSum == firstOrder.OrderRow.SecurityId * orders) { AddAttribute("SecurityId", firstOrder.OrderRow.SecurityId); ClientMarketData.SecurityRow securityRow = firstOrder.OrderRow.SecurityRowByFKSecurityOrderSecurityId; AddAttribute("SecurityName", securityRow.ObjectRow.Name); AddAttribute("SecuritySymbol", securityRow.Symbol); ClientMarketData.PriceRow priceRow = ClientMarketData.Price.FindBySecurityIdCurrencyId( firstOrder.OrderRow.SecurityId, firstOrder.OrderRow.SettlementId); if (priceRow != null) { AddAttribute("LastPrice", priceRow.LastPrice); AddAttribute("BidPrice", priceRow.BidPrice); AddAttribute("BidSize", priceRow.BidSize); AddAttribute("AskPrice", priceRow.AskPrice); AddAttribute("AskSize", priceRow.AskSize); } // Add Fixed Income Fundamentals where they exist. foreach (ClientMarketData.DebtRow debtRow in securityRow.GetDebtRowsByFKSecurityDebtDebtId()) { if (!debtRow.IsIssuerIdNull()) { AddAttribute("IssuerId", debtRow.IssuerId); } AddAttribute("DebtTypeCode", debtRow.DebtTypeCode); AddAttribute("Coupon", debtRow.Coupon); AddAttribute("MaturityDate", debtRow.MaturityDate.ToShortDateString()); } } if (accountIdSum == firstOrder.OrderRow.AccountId * orders) { AddAttribute("AccountId", firstOrder.OrderRow.AccountId); ClientMarketData.AccountRow accountRow = firstOrder.OrderRow.AccountRow; AddAttribute("AccountName", accountRow.ObjectRow.Name); } if (transactionTypeCodeSum == firstOrder.OrderRow.TransactionTypeCode * orders) { AddAttribute("TransactionTypeCode", firstOrder.OrderRow.TransactionTypeCode); AddAttribute("TransactionTypeName", firstOrder.OrderRow.TransactionTypeRow.Mnemonic); } if (timeInForceCodeSum == firstOrder.OrderRow.TimeInForceCode * orders) { AddAttribute("TimeInForceCode", firstOrder.OrderRow.TimeInForceCode); AddAttribute("TimeInForceName", firstOrder.OrderRow.TimeInForceRow.Mnemonic); } if (orderTypeCodeSum == firstOrder.OrderRow.OrderTypeCode * orders) { AddAttribute("OrderTypeCode", firstOrder.OrderRow.OrderTypeCode); AddAttribute("OrderTypeName", firstOrder.OrderRow.OrderTypeRow.Mnemonic); } if (!firstOrder.OrderRow.IsPrice1Null() && price1Sum == firstOrder.OrderRow.Price1 * orders) { AddAttribute("Price1", firstOrder.OrderRow.Price1); } if (!firstOrder.OrderRow.IsPrice2Null() && price2Sum == firstOrder.OrderRow.Price2 * orders) { AddAttribute("Price2", firstOrder.OrderRow.Price2); } // HACK - Trade and Settlement are defaulted. These should be distinct elements from the executions. AddAttribute("TradeDate", DateTime.Now.ToShortDateString()); AddAttribute("SettlementDate", DateTime.Now.ToShortDateString()); } AddAttribute("QuantityOrdered", quantityOrdered); // Aggregate Placements decimal quantityPlaced = 0.0M; PlacementsElement firstPlacement = (PlacementsElement)this.SelectSingleNode(".//Placement"); if (firstPlacement != null) { // Aggregates by Placements. int placements = 0; int brokerIdSum = 0; foreach (PlacementsElement placementsElement in this.SelectNodes("Placement")) { placements++; brokerIdSum += placementsElement.PlacementRow.BrokerId; quantityPlaced += placementsElement.PlacementRow.Quantity; } if (brokerIdSum == firstPlacement.PlacementRow.BrokerId * placements) { AddAttribute("PlacementBrokerId", firstPlacement.PlacementRow.BrokerId); AddAttribute("PlacementBrokerSymbol", firstPlacement.PlacementRow.BrokerRow.Symbol); } } AddAttribute("QuantityPlaced", quantityPlaced); // Aggregate Execution decimal quantityExecuted = 0.0M; ExecutionElement firstExecution = (ExecutionElement)this.SelectSingleNode(".//Execution"); if (firstExecution != null) { // Aggregates by Execution. int placements = 0; int brokerIdSum = 0; foreach (ExecutionElement placementsElement in this.SelectNodes("Execution")) { placements++; brokerIdSum += placementsElement.ExecutionRow.BrokerId; quantityExecuted += placementsElement.ExecutionRow.Quantity; } if (brokerIdSum == firstExecution.ExecutionRow.BrokerId * placements) { AddAttribute("PlacementBrokerId", firstExecution.ExecutionRow.BrokerId); AddAttribute("PlacementBrokerSymbol", firstExecution.ExecutionRow.BrokerRow.Symbol); } } AddAttribute("QuantityExecuted", quantityExecuted); }
/// <summary> /// Fills the OrderForm table with instructions to create, delete or update proposed orders. /// </summary> /// <param name="accountId">Identifiers the destination account of the proposed order.</param> /// <param name="securityId">Identifies the security being trade.</param> /// <param name="positionTypeCode">Identifies the long or short position of the trade.</param> /// <param name="settlementId"></param> /// <param name="proposedQuantity">The signed (relative) quantity of the trade.</param> public static void Create(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.AccountRow accountRow, ClientMarketData.SecurityRow securityRow, int positionTypeCode, decimal proposedQuantity) { // If the proposed quantity is to be zero, we'll delete all proposed orders for this position in the parent and // descendant accounts. Otherwise, a command batch will be created to clear any child proposed orders and create or // update a proposed order for the parent account. if (proposedQuantity == 0.0M) { ProposedOrder.Delete(remoteBatch, remoteTransaction, accountRow, securityRow, positionTypeCode); } else { // The strategy here is to cycle through all the existing proposed orders looking for any that match the account // id, security id and position type of the new order. If none is found, we create a new order. If one is found, // we modify it for the new quantity. Any additional proposed orders are deleted. This flag lets us know if any // existing proposed orders match the position attributes. bool firstTime = true; // Cycle through each of the proposed orders in the given account looking for a matching position. object[] key = new object[] { accountRow.AccountId, securityRow.SecurityId, positionTypeCode }; foreach (DataRowView dataRowView in ClientMarketData.ProposedOrder.UKProposedOrderAccountIdSecurityIdPositionTypeCode.FindRows(key)) { // This is used to reference the current proposed order that matches the position criteria. ClientMarketData.ProposedOrderRow parentProposedOrderRow = (ClientMarketData.ProposedOrderRow)dataRowView.Row; // This check is provided for currency-like assets. There may be many proposed orders for currency // transactions that are used to settle other trades. The user can also enter currency orders directly into // the appraisal. Any manual deposits or withdrawls should not impact settlement orders. This check will skip // any trade that is linked to another order. if (Shadows.Quasar.Common.Relationship.IsChildProposedOrder(parentProposedOrderRow)) { continue; } // Recycle the first proposed order that matches the position criteria. Any additional proposed orders for the // same account, security, position type will be deleted. if (firstTime) { // Any proposed orders found after this one will be deleted. This variable will also indicate that an // existing proposed order was recycled. After the loop is run on this position, a new order will be // created if an existing order couldn't be recycled. firstTime = false; // Create the command to update this proposed order. Update(remoteBatch, remoteTransaction, parentProposedOrderRow, proposedQuantity); } else { // Any order that isn't recycled is considered to be redundant. That is, this order has been superceded by // the recycled order. Clearing any redundant orders makes the operation more intuitive: the user knows // that the only order on the books is the one they entered. They don't have to worry about artifacts from // other operations. Delete(remoteBatch, remoteTransaction, parentProposedOrderRow); } } // This will create a new proposed order if an existing one couldn't be found above for recycling. if (firstTime == true) { Insert(remoteBatch, remoteTransaction, accountRow, securityRow, positionTypeCode, proposedQuantity); } } }
private static void Update(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.ProposedOrderRow parentProposedOrder, decimal quantityInstruction) { // These define the assembly and the types within those assemblies that will be used to create the proposed orders on // the middle tier. RemoteAssembly remoteAssembly = remoteBatch.Assemblies.Add("Service.Core"); RemoteType proposedOrderType = remoteAssembly.Types.Add("Shadows.WebService.Core.ProposedOrder"); ClientMarketData.AccountRow accountRow = parentProposedOrder.AccountRow; ClientMarketData.SecurityRow securityRow = parentProposedOrder.SecurityRowByFKSecurityProposedOrderSecurityId; // This will turn the signed quantity into an absolute quantity and a transaction code (e.g. -1000 is turned into a // SELL of 1000 shares). decimal parentQuantity = Math.Abs(quantityInstruction); int parentTransactionTypeCode = TransactionType.Calculate(securityRow.SecurityTypeCode, parentProposedOrder.PositionTypeCode, quantityInstruction); // The time in force first comes from the user preferences, next, account settings and finally defaults to a day // orders. int timeInForceCode = !ClientPreferences.IsTimeInForceCodeNull() ? ClientPreferences.TimeInForceCode : !accountRow.IsTimeInForceCodeNull() ? accountRow.TimeInForceCode : TimeInForce.DAY; // The destination blotter comes first from the user preferences, second from the account preferences, and finally uses // the auto-routing logic. int blotterId = !ClientPreferences.IsBlotterIdNull() ? ClientPreferences.BlotterId : !accountRow.IsBlotterIdNull() ? accountRow.BlotterId : TradingSupport.AutoRoute(securityRow, parentQuantity); // Create a command to update the proposed order. RemoteMethod updateParent = proposedOrderType.Methods.Add("Update"); updateParent.Transaction = remoteTransaction; updateParent.Parameters.Add("rowVersion", parentProposedOrder.RowVersion); updateParent.Parameters.Add("proposedOrderId", parentProposedOrder.ProposedOrderId); updateParent.Parameters.Add("accountId", parentProposedOrder.AccountId); updateParent.Parameters.Add("securityId", parentProposedOrder.SecurityId); updateParent.Parameters.Add("settlementId", parentProposedOrder.SettlementId); updateParent.Parameters.Add("blotterId", blotterId); updateParent.Parameters.Add("positionTypeCode", parentProposedOrder.PositionTypeCode); updateParent.Parameters.Add("transactionTypeCode", parentTransactionTypeCode); updateParent.Parameters.Add("timeInForceCode", timeInForceCode); updateParent.Parameters.Add("orderTypeCode", OrderType.Market); updateParent.Parameters.Add("quantity", parentQuantity); foreach (ClientMarketData.ProposedOrderTreeRow proposedOrderTree in parentProposedOrder.GetProposedOrderTreeRowsByFKProposedOrderProposedOrderTreeParentId()) { ClientMarketData.ProposedOrderRow childProposedOrder = proposedOrderTree.ProposedOrderRowByFKProposedOrderProposedOrderTreeChildId; // If this is the settlement part of the order, then adjust the quantity. if (childProposedOrder.SecurityId == parentProposedOrder.SettlementId) { // The settlement security is needed for the calculation of the cash impact of this trade. ClientMarketData.CurrencyRow currencyRow = MarketData.Currency.FindByCurrencyId(childProposedOrder.SettlementId); decimal marketValue = parentQuantity * securityRow.QuantityFactor * Price.Security(currencyRow, securityRow) * securityRow.PriceFactor * TransactionType.GetCashSign(parentTransactionTypeCode); decimal childQuantity = Math.Abs(marketValue); int childTransactionTypeCode = TransactionType.Calculate(securityRow.SecurityTypeCode, parentProposedOrder.PositionTypeCode, marketValue); // Create a command to update the proposed order. RemoteMethod updateChild = proposedOrderType.Methods.Add("Update"); updateChild.Transaction = remoteTransaction; updateChild.Parameters.Add("rowVersion", childProposedOrder.RowVersion); updateChild.Parameters.Add("proposedOrderId", childProposedOrder.ProposedOrderId); updateChild.Parameters.Add("accountId", childProposedOrder.AccountId); updateChild.Parameters.Add("securityId", childProposedOrder.SecurityId); updateChild.Parameters.Add("settlementId", childProposedOrder.SettlementId); updateChild.Parameters.Add("blotterId", blotterId); updateChild.Parameters.Add("positionTypeCode", parentProposedOrder.PositionTypeCode); updateChild.Parameters.Add("transactionTypeCode", childTransactionTypeCode); updateChild.Parameters.Add("timeInForceCode", timeInForceCode); updateChild.Parameters.Add("orderTypeCode", OrderType.Market); updateChild.Parameters.Add("quantity", childQuantity); } } }
/// <summary> /// Creates an XML Element representing a placment in the order document. /// </summary> /// <param name="xmlDocument">The destination XML document.</param> /// <param name="orderRow">A order record.</param> public LocalOrderElement(XmlDocument xmlDocument, LocalOrderSet.OrderRow orderRow) : base("LocalOrder", xmlDocument) { // Add the attributes of a order to this record. AddAttribute("OrderId", orderRow.OrderId.ToString()); // Account field if (!orderRow.IsAccountIdNull()) { AddAttribute("AccountId", orderRow.AccountId.ToString()); ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(orderRow.AccountId); if (accountRow != null) { AddAttribute("AccountId", accountRow.AccountId.ToString()); AddAttribute("AccountName", accountRow.ObjectRow.Name); AddAttribute("AccountMnemonic", accountRow.Mnemonic); } } // Security field if (!orderRow.IsSecurityIdNull()) { AddAttribute("SecurityId", orderRow.SecurityId.ToString()); ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(orderRow.SecurityId); if (securityRow != null) { AddAttribute("SecurityId", securityRow.SecurityId.ToString()); AddAttribute("SecurityName", securityRow.ObjectRow.Name); AddAttribute("SecuritySymbol", securityRow.Symbol); } } // Broker field if (!orderRow.IsBrokerIdNull()) { AddAttribute("BrokerId", orderRow.BrokerId.ToString()); ClientMarketData.BrokerRow brokerRow = ClientMarketData.Broker.FindByBrokerId(orderRow.BrokerId); if (brokerRow != null) { AddAttribute("BrokerId", brokerRow.BrokerId.ToString()); AddAttribute("BrokerName", brokerRow.ObjectRow.Name); AddAttribute("BrokerSymbol", brokerRow.Symbol); if (!brokerRow.IsPhoneNull()) { AddAttribute("BrokerPhone", brokerRow.Phone); } } } // TransactionType field if (!orderRow.IsTransactionTypeCodeNull()) { AddAttribute("TransactionTypeCode", orderRow.TransactionTypeCode.ToString()); ClientMarketData.TransactionTypeRow transactionTypeRow = ClientMarketData.TransactionType.FindByTransactionTypeCode(orderRow.TransactionTypeCode); if (transactionTypeRow != null) { AddAttribute("TransactionTypeMnemonic", transactionTypeRow.Mnemonic); } } // TimeInForce field if (!orderRow.IsTimeInForceCodeNull()) { AddAttribute("TimeInForceCode", orderRow.TimeInForceCode.ToString()); ClientMarketData.TimeInForceRow timeInForceRow = ClientMarketData.TimeInForce.FindByTimeInForceCode(orderRow.TimeInForceCode); if (timeInForceRow != null) { AddAttribute("TimeInForceMnemonic", timeInForceRow.Mnemonic); } } // OrderType field if (!orderRow.IsOrderTypeCodeNull()) { AddAttribute("OrderTypeCode", orderRow.OrderTypeCode.ToString()); ClientMarketData.OrderTypeRow orderTypeRow = ClientMarketData.OrderType.FindByOrderTypeCode(orderRow.OrderTypeCode); if (orderTypeRow != null) { AddAttribute("OrderTypeMnemonic", orderTypeRow.Mnemonic); } } if (!orderRow.IsQuantityNull()) { AddAttribute("Quantity", orderRow.Quantity.ToString()); } if (!orderRow.IsPrice1Null()) { AddAttribute("Price1", orderRow.Price1.ToString()); } if (!orderRow.IsPrice2Null()) { AddAttribute("Price2", orderRow.Price2.ToString()); } }
/// <summary> /// Creates a denormalized order record from a local record. /// </summary> /// <param name="localOrder">A local order record.</param> /// <returns>A order record that is independant of the global data set for all the anscillary data.</returns> public static OrderSet.OrderRow Create(LocalOrderSet.OrderRow localOrder) { // Create a new, empty order record. OrderSet.OrderRow orderRow = orderSet.Order.NewOrderRow(); // This new record is a copy of a local record and uses the local system of identifiers. orderRow.IsLocal = true; // Copy each field that has an analog in the local record set into the new record. foreach (DataColumn dataColumn in localOrder.Table.Columns) { orderRow[dataColumn.ColumnName] = localOrder[dataColumn]; } // AccountId cross-referenced data is filled in here. if (!localOrder.IsAccountIdNull()) { ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(orderRow.AccountId); if (accountRow != null) { orderRow.AccountMnemonic = accountRow.Mnemonic; orderRow.AccountName = accountRow.ObjectRow.Name; } } // SecurityId cross-referenced data is filled in here. if (!localOrder.IsSecurityIdNull()) { ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(orderRow.SecurityId); if (securityRow != null) { orderRow.SecuritySymbol = securityRow.Symbol; orderRow.SecurityName = securityRow.ObjectRow.Name; } } // CurrencyId cross-referenced data is filled in here. if (!localOrder.IsSettlementIdNull()) { ClientMarketData.CurrencyRow currencyRow = ClientMarketData.Currency.FindByCurrencyId(orderRow.SettlementId); if (currencyRow != null) { orderRow.SettlementSymbol = currencyRow.SecurityRow.Symbol; orderRow.SettlementName = currencyRow.SecurityRow.ObjectRow.Name; } } // BrokerId cross-referenced data is filled in here. if (!localOrder.IsBrokerIdNull()) { ClientMarketData.BrokerRow brokerRow = ClientMarketData.Broker.FindByBrokerId(orderRow.BrokerId); if (brokerRow != null) { orderRow.BrokerSymbol = brokerRow.Symbol; orderRow.BrokerName = brokerRow.ObjectRow.Name; } } // TransactionType cross-referenced data is filled in here. if (!localOrder.IsTransactionTypeCodeNull()) { ClientMarketData.TransactionTypeRow transactionTypeRow = ClientMarketData.TransactionType.FindByTransactionTypeCode(orderRow.TransactionTypeCode); if (transactionTypeRow != null) { orderRow.TransactionTypeMnemonic = transactionTypeRow.Mnemonic; } } // TimeInForce cross-referenced data is filled in here. if (!localOrder.IsTimeInForceCodeNull()) { ClientMarketData.TimeInForceRow timeInForceRow = ClientMarketData.TimeInForce.FindByTimeInForceCode(orderRow.TimeInForceCode); if (timeInForceRow != null) { orderRow.TimeInForceMnemonic = timeInForceRow.Mnemonic; } } // TimeInForce cross-referenced data is filled in here. if (!localOrder.IsOrderTypeCodeNull()) { ClientMarketData.OrderTypeRow orderTypeRow = ClientMarketData.OrderType.FindByOrderTypeCode(orderRow.OrderTypeCode); if (orderTypeRow != null) { orderRow.OrderTypeMnemonic = orderTypeRow.Mnemonic; } } // This is a complete record of the order, including the referenced data. return(orderRow); }
/// <summary> /// Recursively rebalances an account and all it's children. /// </summary> /// <param name="accountRow">The parent account to be rebalanced.</param> private static void RecurseAccounts(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, AppraisalSet appraisalSet, ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow) { // The base currency of the account is used to cacluate market values. ClientMarketData.CurrencyRow currencyRow = ClientMarketData.Currency.FindByCurrencyId(accountRow.CurrencyId); // Calculate the total market value for the appraisal. This will be the denominator in all calculations involving // portfolio percentages. decimal accountMarketValue = MarketValue.Calculate(currencyRow, accountRow, MarketValueFlags.EntirePosition); // Cycle through all the positions of the appraisal using the current account and calculate the size and direction of // the trade needed to bring it to the model's target percent. foreach (AppraisalSet.SecurityRow driverSecurity in appraisalSet.Security) { // We need to reference the security row in the ClientMarketData to price this item. ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(driverSecurity.SecurityId); // In this rebalancing operation, the cash balance is dependant on the securities bought and sold. The assumption // is made that we won't implicitly add or remove cash to accomplish the reblancing operation. When stocks are // bought or sold below, they will impact the underlying currency. A cash target can be reached by setting all the // other percentages up properly. As long as the total percentage in a model is 100%, the proper cash target will // be calculated. We don't have to do anything with this asset type. if (securityRow.SecurityTypeCode == SecurityType.Currency) { continue; } // This section will calculate the difference in between the actual and target market values for each // position and create orders that will bring the account to the targeted percentages. foreach (AppraisalSet.PositionRow driverPosition in driverSecurity.GetPositionRows()) { // Calculate the proposed quantity needed to bring this asset/account combination to the percentage given by // the model. First, find the target percent. If it's not there, we assume a target of zero (meaning sell all // holdings). ClientMarketData.PositionTargetRow positionTargetRow = ClientMarketData.PositionTarget.FindByModelIdSecurityIdPositionTypeCode(modelRow.ModelId, securityRow.SecurityId, driverPosition.PositionTypeCode); decimal targetPositionPercent = positionTargetRow == null ? 0.0M : positionTargetRow.Percent; // The market value of this trade will be the target market value less the current market value of // this position (without including the existing proposed orders in the current market value // calculation). decimal targetPositionMarketValue = targetPositionPercent * accountMarketValue; decimal actualPositionMarketValue = MarketValue.Calculate(currencyRow, accountRow, securityRow, driverPosition.PositionTypeCode, MarketValueFlags.ExcludeProposedOrder); decimal proposedMarketValue = targetPositionMarketValue - actualPositionMarketValue; // Calculate the quantity needed to hit the target market value and round it according to the model. Note that // the market values and prices are all denominated in the currency of the parent account. Also note the // quantityFactor is needed for the proper quantity calculation. decimal price = Price.Security(currencyRow, securityRow); decimal proposedQuantity = price == 0.0M ? 0.0M : proposedMarketValue / (price * securityRow.QuantityFactor); // If we have an equity, round to the model's lot size. Common values are 100 and 1. if (securityRow.SecurityTypeCode == SecurityType.Equity) { proposedQuantity = Math.Round(proposedQuantity / modelRow.EquityRounding, 0) * modelRow.EquityRounding; } // A debt generally needs to be rounded to face. if (securityRow.SecurityTypeCode == SecurityType.Debt) { proposedQuantity = Math.Round(proposedQuantity / modelRow.DebtRounding, 0) * modelRow.DebtRounding; } // Have the Order Form Builder object construct an order based on the new proposed quantity. This method will // fill in the defaults needed for a complete Proposed Order. It will also create an deposit or widthdrawal // from an account to cover the transaction. ProposedOrder.Create(remoteBatch, remoteTransaction, accountRow, securityRow, driverPosition.PositionTypeCode, proposedQuantity); } } // Now that we've rebalanced the parent account, cycle through all the children accounts and rebalance them. foreach (ClientMarketData.ObjectTreeRow objectTreeRow in accountRow.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { foreach (ClientMarketData.AccountRow childAccount in objectTreeRow.ObjectRowByFKObjectObjectTreeChildId.GetAccountRows()) { Security.RecurseAccounts(remoteBatch, remoteTransaction, appraisalSet, childAccount, modelRow); } } }
/// <summary> /// Method called to retrieve the securityId for the security selected in the match viewere. /// </summary> /// <param name="parameter"></param> private void InitializeData(object parameter) { int id = Int32.MinValue; try { // Lock the tables. System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.MatchLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.NegotiationLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.OrderTypeLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(ClientTimeout.LockWait); ClientMarketData.WorkingOrderLock.AcquireReaderLock(ClientTimeout.LockWait); // Find the Match record. ClientMarketData.MatchRow matchRow = ClientMarketData.Match.FindByMatchId(this.matchId); ClientMarketData.WorkingOrderRow workingOrderRow = matchRow.WorkingOrderRow; ClientMarketData.OrderTypeRow orderTypeRow = workingOrderRow.OrderTypeRow; ClientMarketData.SecurityRow securityRow = workingOrderRow.SecurityRowBySecurityWorkingOrderSecurityId; if (securityRow != null) { id = securityRow.SecurityId; } } catch (Exception e) { // Write the error and stack trace out to the debug listener EventLog.Error("{0}, {1}", e.Message, e.StackTrace); } finally { // Release the locks. if (ClientMarketData.MatchLock.IsReaderLockHeld) { ClientMarketData.MatchLock.ReleaseReaderLock(); } if (ClientMarketData.NegotiationLock.IsReaderLockHeld) { ClientMarketData.NegotiationLock.ReleaseReaderLock(); } if (ClientMarketData.OrderTypeLock.IsReaderLockHeld) { ClientMarketData.OrderTypeLock.ReleaseReaderLock(); } if (ClientMarketData.SecurityLock.IsReaderLockHeld) { ClientMarketData.SecurityLock.ReleaseReaderLock(); } if (ClientMarketData.WorkingOrderLock.IsReaderLockHeld) { ClientMarketData.WorkingOrderLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } if (id != Int32.MinValue) { this.Open(id); } }
/// <summary> /// Creates a temporary model based on the current position level targets. /// </summary> /// <param name="accountRow">An account used as a basis for the targets.</param> /// <param name="schemeRow">The scheme used to select sector targets.</param> /// <returns>A batch of commands that will create a model containing the current position weights of the account.</returns> private static ModelBatch CreatePositionSelfModel(ClientMarketData.AccountRow accountRow, ClientMarketData.SchemeRow schemeRow) { // Create the batch and fill it in with the assembly and type needed for this function. ModelBatch modelBatch = new ModelBatch(); RemoteTransaction remoteTransaction = modelBatch.Transactions.Add(); RemoteAssembly remoteAssembly = modelBatch.Assemblies.Add("Service.Core"); RemoteType remoteType = remoteAssembly.Types.Add("Shadows.WebService.Core.Model"); // Create the temporary, position model based on the scheme used by the original account. RemoteMethod insertModel = remoteType.Methods.Add("Insert"); insertModel.Parameters.Add("modelId", DataType.Int, Direction.ReturnValue); insertModel.Parameters.Add("rowVersion", DataType.Long, Direction.Output); insertModel.Parameters.Add("modelTypeCode", ModelType.Security); insertModel.Parameters.Add("name", "Untitled"); insertModel.Parameters.Add("schemeId", schemeRow.SchemeId); insertModel.Parameters.Add("algorithmId", Algorithm.SecurityRebalancer); insertModel.Parameters.Add("temporary", true); // The 'Self Security' model uses the market value of all the positions, regardless of account or sub-account, when // calculating the denominator for the percentages. decimal accountMarketValue = MarketValue.Calculate(accountRow.CurrencyRow, accountRow, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts); // If the account market value is zero, we can't do much more to create a model. if (accountMarketValue != 0.0M) { // Create a new outline for the model to follow. This will collect the tax lots, proposed orders orders and // allocations into positions that can be used for calculating percentages. Common.Appraisal appraisal = new Common.Appraisal(accountRow, true); // Run through each of the positions, starting with the security. foreach (AppraisalSet.SecurityRow driverSecurity in appraisal.Security) { // This is a position is the destination for the market value calculation. ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(driverSecurity.SecurityId); // The object Type for this operation. RemoteType positionTargetType = remoteAssembly.Types.Add("Shadows.WebService.Core.PositionTarget"); // Run through each of the positions in the appraisal calculating the market value of each position. The ratio // of this market value to the account's market value is the model percentage. foreach (AppraisalSet.PositionRow positionRow in driverSecurity.GetPositionRows()) { // Calculate the market value of the given position. decimal securityMarketValue = MarketValue.Calculate(accountRow.CurrencyRow, accountRow, securityRow, positionRow.PositionTypeCode, MarketValueFlags.EntirePosition | MarketValueFlags.EntirePosition); // Add the position level target to the model. RemoteMethod insertPosition = positionTargetType.Methods.Add("Insert"); insertPosition.Parameters.Add("modelId", insertModel.Parameters["modelId"]); insertPosition.Parameters.Add("securityId", securityRow.SecurityId); insertPosition.Parameters.Add("positionTypeCode", positionRow.PositionTypeCode); insertPosition.Parameters.Add("percent", securityMarketValue / accountMarketValue); } } } // Save the reference to the 'modelId' return parameter. modelBatch.ModelIdParameter = insertModel.Parameters["modelId"]; // This batch will create a temporary model based on the position totals of the original account. return(modelBatch); }
/// <summary> /// Recursively calculates proposed orders for a sector. /// </summary> /// <param name="sector">Gives the current sector (sector) for the calculation.</param> private static void RecurseSectors(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.CurrencyRow currencyRow, ClientMarketData.ModelRow modelRow, AppraisalSet.ObjectRow driverObject, decimal actualSectorMarketValue, decimal targetSectorMarketValue) { // Run through each of the positions in the sector and calculate the current percentage of the position within // the sector. We're going to keep this percentage as we rebalance to the new sector market value. foreach (AppraisalSet.SecurityRow driverSecurity in driverObject.GetSecurityRows()) { foreach (AppraisalSet.PositionRow driverPosition in driverSecurity.GetPositionRows()) { // We need to know what kind of security we're dealing with when calculating market values and quantities // below. ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(driverSecurity.SecurityId); // In this rebalancing operation, the cash balance is dependant on the securities bought and sold. When // stocks are bought or sold below, they will impact the underlying currency. We can not balance to a // currency target directly. if (securityRow.SecurityTypeCode == SecurityType.Currency) { continue; } // Calculate the proposed orders for each account. The fraction of the security within the sector will // stay the same, even though the sector may increase or decrease with respect to the total market value. foreach (AppraisalSet.AccountRow driverAccount in driverPosition.GetAccountRows()) { // The underlying currency is needed for the market value calculations. ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(driverAccount.AccountId); // Sector rebalancing keeps the percentage of a security within the sector constant. Only the overall // percentage of the sector with respect to the NAV changes. To accomplish this, we first calculate // the percentage of the security within the sector before we rebalance the sector. decimal actualPositionMarketValue = MarketValue.Calculate(currencyRow, accountRow, securityRow, driverPosition.PositionTypeCode, MarketValueFlags.EntirePosition); // Calculate the target market value as a percentage of the entire sector (use zero if the sector has // no market value to prevent divide by zero errors). decimal targetPositionMarketValue = (actualSectorMarketValue == 0) ? 0.0M : actualPositionMarketValue * targetSectorMarketValue / actualSectorMarketValue; // The target proposed orders market value keeps the percentage of the position constant while // changing the overall sector percentage. decimal proposedMarketValue = targetPositionMarketValue - MarketValue.Calculate(currencyRow, accountRow, securityRow, driverPosition.PositionTypeCode, MarketValueFlags.ExcludeProposedOrder); // Calculate the quantity needed to hit the target market value and round it according to the // model. Note that the market values and prices are all denominated in the currency of the // parent account. Also note the quantityFactor is needed for the proper quantity calculation. decimal proposedQuantity = proposedMarketValue / (Price.Security(currencyRow, securityRow) * securityRow.PriceFactor * securityRow.QuantityFactor); // If we have an equity, round to the model's lot size. if (securityRow.SecurityTypeCode == SecurityType.Equity) { proposedQuantity = Math.Round(proposedQuantity / modelRow.EquityRounding, 0) * modelRow.EquityRounding; } // A debt generally needs to be rounded to face. if (securityRow.SecurityTypeCode == SecurityType.Debt) { proposedQuantity = Math.Round(proposedQuantity / modelRow.DebtRounding, 0) * modelRow.DebtRounding; } // Have the Order Form Builder object construct an order based on the quantity we've calcuated from // the market value. This method will fill in the defaults needed for a complete proposed order. ProposedOrder.Create(remoteBatch, remoteTransaction, accountRow, securityRow, driverAccount.PositionTypeCode, proposedQuantity); } } } // Recurse into each of the sub-sectors. This allows us to rebalance with any number of levels to the // hierarchy. Eventually, we will run across a sector with security positions in it and end up doing some // real work. foreach (AppraisalSet.ObjectTreeRow driverTree in driverObject.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { SectorMerge.RecurseSectors(remoteBatch, remoteTransaction, currencyRow, modelRow, driverTree.ObjectRowByFKObjectObjectTreeChildId, actualSectorMarketValue, targetSectorMarketValue); } }
/// <summary> /// Recursively calculates proposed orders for a sector. /// </summary> /// <param name="sector">Gives the current sector (sector) for the calculation.</param> private static void RecurseSectors(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.ModelRow modelRow, AppraisalSet.SectorRow driverSector, decimal actualSectorMarketValue, decimal targetSectorMarketValue) { // The main idea here is to keep the ratio of the security to the sector constant, while changing the market // value of the sector. Scan each of the securities belonging to this sector. foreach (AppraisalSet.ObjectTreeRow objectTreeRow in driverSector.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { // Cycle through each of the securities in the sector. We're going to keep the ratio of the security the // same as we target a different sector total. foreach (AppraisalSet.SecurityRow driverSecurity in objectTreeRow.ObjectRowByFKObjectObjectTreeChildId.GetSecurityRows()) { foreach (AppraisalSet.PositionRow driverPosition in driverSecurity.GetPositionRows()) { // We need to reference the security record for calculating proposed orders and the market value // of the trade. ClientMarketData.SecurityRow securityRow = ClientMarketData.Security.FindBySecurityId(driverSecurity.SecurityId); // In this rebalancing operation, the cash balance is dependant on the securities bought and // sold. When stocks are bought or sold below, they will impact the underlying currency. A cash // target can be reached by setting all the other percentages up properly. As long as the total // percentage in a model is 100%, the proper cash target will be calculated. We don't have to do // anything with this asset type. if (securityRow.SecurityTypeCode == SecurityType.Currency) { continue; } // The ratio of the security within the sector will stay constant, even though the sector may // increase or decrease with the target in the model. Note that there's only one account in the // 'Accounts' table of the driver because this is a 'Wrap' operation. foreach (AppraisalSet.AccountRow driverAccount in driverPosition.GetAccountRows()) { // Find the account associated with the driver record. ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(driverAccount.AccountId); // The market value of all the securities are normalized to the base currency of the account // so they can be aggregated. ClientMarketData.CurrencyRow currencyRow = ClientMarketData.Currency.FindByCurrencyId(accountRow.CurrencyId); // Sector rebalancing keeps the percentage of a security within the sector constant. Only the // overall percentage of the sector with respect to the NAV changes. The first step in this // rebalancing operation is to calculate the market value of the given position. decimal actualPositionMarketValue = MarketValue.Calculate(currencyRow, accountRow, securityRow, driverPosition.PositionTypeCode, MarketValueFlags.EntirePosition); // The target market value operation keeps the percentage of the position constant while // changing the overall sector percentage. decimal targetPositionMarketValue = (actualSectorMarketValue == 0) ? 0.0M : actualPositionMarketValue * targetSectorMarketValue / actualSectorMarketValue; // Calculate the market value of an order that will achieve the target. Note that we're not // including the existing proposed orders in the market value, but we did include them when // calculating the account's market value. This allows us to put in what-if orders that will // impact the market value before we do the rebalancing. decimal proposedMarketValue = targetPositionMarketValue - MarketValue.Calculate(currencyRow, accountRow, securityRow, driverPosition.PositionTypeCode, MarketValueFlags.ExcludeProposedOrder); // Calculate the quantity needed to hit the target market value and round it according to the // model. Note that the market values and prices are all denominated in the currency of the // parent account. Also note the quantityFactor is needed for the proper quantity // calculation. decimal proposedQuantity = proposedMarketValue / (Price.Security(currencyRow, securityRow) * securityRow.QuantityFactor); // If we have an equity, round to the model's lot size. if (securityRow.SecurityTypeCode == SecurityType.Equity) { proposedQuantity = Math.Round(proposedQuantity / modelRow.EquityRounding, 0) * modelRow.EquityRounding; } // A debt generally needs to be rounded to face. if (securityRow.SecurityTypeCode == SecurityType.Debt) { proposedQuantity = Math.Round(proposedQuantity / modelRow.DebtRounding, 0) * modelRow.DebtRounding; } // Have the OrderForm object construct an order based on the quantity we've calcuated // from the market value. This will fill in the defaults for the order and translate the // signed quantities into transaction codes. ProposedOrder.Create(remoteBatch, remoteTransaction, accountRow, securityRow, driverAccount.PositionTypeCode, proposedQuantity); } } } // Recurse into each of the sub-sectors. This allows us to rebalance with any number of levels to the // hierarchy. Eventually, we will run across a sector with security positions in it and end up doing some // real work. foreach (AppraisalSet.SectorRow childSector in objectTreeRow.ObjectRowByFKObjectObjectTreeChildId.GetSectorRows()) { SectorWrap.RecurseSectors(remoteBatch, remoteTransaction, modelRow, childSector, actualSectorMarketValue, targetSectorMarketValue); } } }
/// <summary> /// Opens the document for the given object and it's argument. /// </summary> /// <param name="matchId">The primary identifier of the object to open.</param> /// <param name="argument">Options that can be used to further specify the document's properties.</param> public void OpenMatchCommand(object parameter) { // Extract the command argument. int matchId = (int)parameter; // This will force an empty placement and placement document to appear until we get some data. It's the same // as forcing a drawing of the headers only. this.matchId = matchId; try { // Lock the tables System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); ClientMarketData.EquityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.MatchLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.PriceLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.WorkingOrderLock.AcquireReaderLock(CommonTimeout.LockWait); // Install the event handlers. The ClientMarketData component will advise us when the data has changed. ClientMarketData.MatchRow matchRow = ClientMarketData.Match.FindByMatchId(matchId); if (matchRow == null) { return; } ClientMarketData.SecurityRow securityRow = matchRow.WorkingOrderRow.SecurityRowBySecurityWorkingOrderSecurityId; ClientMarketData.EquityRow equityRow = ClientMarketData.Equity.FindByEquityId(securityRow.SecurityId); if (equityRow == null) { return; } string securityName = securityRow.ObjectRow.Name; ClientMarketData.PriceRow priceRow = ClientMarketData.Price.FindBySecurityId(securityRow.SecurityId); decimal price = priceRow.LastPrice; Invoke(displayDelegate, new object[] { securityName, price }); } finally { if (ClientMarketData.EquityLock.IsReaderLockHeld) { ClientMarketData.EquityLock.ReleaseReaderLock(); } if (ClientMarketData.MatchLock.IsReaderLockHeld) { ClientMarketData.MatchLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.PriceLock.IsReaderLockHeld) { ClientMarketData.PriceLock.ReleaseReaderLock(); } if (ClientMarketData.SecurityLock.IsReaderLockHeld) { ClientMarketData.SecurityLock.ReleaseReaderLock(); } if (ClientMarketData.WorkingOrderLock.IsReaderLockHeld) { ClientMarketData.WorkingOrderLock.ReleaseReaderLock(); } System.Diagnostics.Debug.Assert(!ClientMarketData.IsLocked); } }