/// <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> /// 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> /// 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)); } }