/// <summary> /// Creates a sector element. /// </summary> /// <param name="appraisalDocument">The parent document.</param> /// <param name="sectorRow">The sector record used to create the Xml element.</param> public SectorElement(AppraisalDocument appraisalDocument, ClientMarketData.SectorRow sectorRow) : base("Sector", appraisalDocument) { // The sector id and name are taken directly from the SectorRow record. AddAttribute("SectorId", sectorRow.SectorId.ToString()); AddAttribute("SortOrder", sectorRow.SortOrder.ToString()); AddAttribute("Name", sectorRow.ObjectRow.Name.ToString()); // If there is a target percentage associated with this sector, add it to the attribute list. ClientMarketData.SectorTargetRow sectorTargetRow = ClientMarketData.SectorTarget.FindByModelIdSectorId(appraisalDocument.ModelRow.ModelId, sectorRow.SectorId); if (sectorTargetRow != null) { AddAttribute("ModelPercent", sectorTargetRow.Percent.ToString()); } // If there is a position record associated with this sector, 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, sectorRow.SectorId, Shadows.Quasar.Common.PositionType.Long); 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()); } } }
/// <summary> /// Initializes a Sector. /// </summary> /// <param name="configurationId">Defines which external fields are used to identify an object.</param> /// <param name="sectorId">The sector identifier.</param> /// <returns>A sector record, null if the identifier doesn't exist.</returns> protected override void Initialize(int sectorId) { // Use the specified configuration to find the internal sector identifier. ClientMarketData.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(sectorId); if (sectorRow == null) { throw new Exception(String.Format("Sector {0} doesn't exist", sectorId)); } // Initialize the base class. base.Initialize(sectorId); }
/// <summary> /// Rebalances an account to the sector targets, then recursively rebalances the children accounts. /// </summary> /// <param name="orderFormBuilder">A collection of orders.</param> /// <param name="accountRow">The parent account to be rebalanced.</param> /// <param name="modelRow">The model containing the sector targets.</param> /// <param name="schemeRow">The outline scheme used to define the sector contents.</param> private static void RecurseAccounts(RemoteBatch remoteBatch, RemoteTransaction remoteTransaction, ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow, ClientMarketData.SchemeRow schemeRow) { // All the market values of all the securities in this account are normalized to a single currency so they can // be aggregated. ClientMarketData.CurrencyRow currencyRow = ClientMarketData.Currency.FindByCurrencyId(accountRow.CurrencyId); // Calculate the total market value for the appraisal without including child accounts. This is a 'Wrap' // rebalancing, so we're only concerned with what's in this account. The account's market value will be the // denominator in all calculations involving sector percentages. decimal accountMarketValue = MarketValue.Calculate(currencyRow, accountRow, MarketValueFlags.EntirePosition); // The outline of the appraisal will be needed to make market value calculations based on a sector. Note that // we're not including the child accounts in the outline. Wrap rebalancing works only on a single account at // a time. AppraisalSet appraisalSet = new Appraisal(accountRow, schemeRow, false); // By cycling through all the immediate children of the scheme record, we'll have covered the top-level // sectors in this appraisal. foreach (AppraisalSet.SchemeRow driverScheme in appraisalSet.Scheme) { foreach (AppraisalSet.ObjectTreeRow driverTree in driverScheme.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { foreach (AppraisalSet.SectorRow driverSector in driverTree.ObjectRowByFKObjectObjectTreeChildId.GetSectorRows()) { // Find the sectors row record that corresponds to the current sector in the appraisal set. ClientMarketData.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(driverSector.SectorId); // Get the market value of the top-level sector, including all sub-sectors and all positions // belonging to only the current account. decimal actualSectorMarketValue = MarketValue.Calculate(currencyRow, accountRow, sectorRow, MarketValueFlags.EntirePosition); // This will find the model percentage of the current top-level sector. If the sector wasn't // specified in the model, assume a value of zero, which would indicate that we're to sell the // entire sector. ClientMarketData.SectorTargetRow sectorTargetRow = ClientMarketData.SectorTarget.FindByModelIdSectorId(modelRow.ModelId, driverSector.SectorId); decimal targetPercent = (sectorTargetRow == null) ? 0.0M : sectorTargetRow.Percent; // The sector's target market value is calculated from the model percentage and the current // account market value. This is placed in a member variable so it's available to the methods // when we recurse. decimal targetSectorMarketValue = accountMarketValue * targetPercent; // Now that we have a sector target to shoot for, recursively descend into the structure // calculating proposed orders. SectorWrap.RecurseSectors(remoteBatch, remoteTransaction, modelRow, driverSector, actualSectorMarketValue, targetSectorMarketValue); } } } // 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()) { SectorWrap.RecurseAccounts(remoteBatch, remoteTransaction, childAccount, modelRow, schemeRow); } } }
public decimal GetMarketValue(Sector sector) { 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.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(sector.SectorId); return(Common.MarketValue.Calculate(accountRow.CurrencyRow, accountRow, sectorRow, 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)); }
public PositionList GetPositions(Sector sector) { PositionList positionList = new PositionList(); try { // Lock the tables. Debug.Assert(!ClientMarketData.AreLocksHeld); ClientMarketData.AccountLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.AllocationLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.OrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.PositionLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ProposedOrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SectorLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TaxLotLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(this.AccountId); ClientMarketData.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(sector.SectorId); // Aggregate the tax lot quantities. foreach (ClientMarketData.TaxLotRow taxLotRow in accountRow.GetTaxLotRows()) { if (IsSecurityInSector(sectorRow, taxLotRow.SecurityRow)) { positionList.Add(Position.Make(this.AccountId, taxLotRow.SecurityId, taxLotRow.PositionTypeCode)); } } // Aggregate the proposed ordered quantities foreach (ClientMarketData.ProposedOrderRow proposedOrderRow in accountRow.GetProposedOrderRows()) { if (IsSecurityInSector(sectorRow, proposedOrderRow.SecurityRowByFKSecurityProposedOrderSecurityId)) { positionList.Add(Position.Make(this.AccountId, proposedOrderRow.SecurityId, proposedOrderRow.PositionTypeCode)); } } // Aggregate the ordered quantities foreach (ClientMarketData.OrderRow orderRow in accountRow.GetOrderRows()) { if (IsSecurityInSector(sectorRow, orderRow.SecurityRowByFKSecurityOrderSecurityId)) { positionList.Add(Position.Make(this.AccountId, orderRow.SecurityId, orderRow.PositionTypeCode)); } } // Aggregate the allocated quantities foreach (ClientMarketData.AllocationRow allocationRow in accountRow.GetAllocationRows()) { if (IsSecurityInSector(sectorRow, allocationRow.SecurityRowByFKSecurityAllocationSecurityId)) { positionList.Add(Position.Make(this.AccountId, allocationRow.SecurityId, allocationRow.PositionTypeCode)); } } } finally { // Locks are no longer needed on the price table. if (ClientMarketData.AccountLock.IsReaderLockHeld) { ClientMarketData.AccountLock.ReleaseReaderLock(); } if (ClientMarketData.AllocationLock.IsReaderLockHeld) { ClientMarketData.AllocationLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectLock.IsReaderLockHeld) { ClientMarketData.ObjectLock.ReleaseReaderLock(); } if (ClientMarketData.ObjectTreeLock.IsReaderLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseReaderLock(); } if (ClientMarketData.OrderLock.IsReaderLockHeld) { ClientMarketData.OrderLock.ReleaseReaderLock(); } if (ClientMarketData.PositionLock.IsReaderLockHeld) { ClientMarketData.PositionLock.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(); } Debug.Assert(!ClientMarketData.AreLocksHeld); } return(positionList); }
/// <summary> /// Rebalances an AppraisalModelSet to sector targets. The model is applied to the aggregate market value of the /// account and it's children. /// </summary> /// <param name="accountId">The parent account to be rebalanced.</param> /// <param name="modelId">The sector model to be used.</param> /// <returns>A set of proposed orders.</returns> public static RemoteBatch Rebalance(ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow) { // Make sure the scheme still exists in the in-memory database. We need it to rebalance to calculate // sector totals. ClientMarketData.SchemeRow schemeRow; if ((schemeRow = ClientMarketData.Scheme.FindBySchemeId(modelRow.SchemeId)) == null) { throw new ArgumentException("Scheme doesn't exist in the ClientMarketData", modelRow.SchemeId.ToString()); } // All the market values need to be normalized to a single currency so the sectors can be aggregated. This // value is made available to all methods through a member rather than passed on the stack. ClientMarketData.CurrencyRow currencyRow = accountRow.CurrencyRow; // The final result of this method is a command batch that can be sent to the server. RemoteBatch remoteBatch = new RemoteBatch(); RemoteTransaction remoteTransaction = remoteBatch.Transactions.Add(); // Calculate the total market value for the appraisal and all the sub-accounts. This will be the denominator // in all calculations involving sector percentages. This feature makes a 'Merge' rebalancer different from a // 'Wrap' rebalance. The 'Wrap' uses the sub-account's market value as the denominator when calculating // sector market values. decimal accountMarketValue = MarketValue.Calculate(accountRow.CurrencyRow, accountRow, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts); // The outline of the appraisal will be needed to make calculations based on a position, that is a security, // account, position type combination grouped by a security classification scheme. AppraisalSet appraisalSet = new Appraisal(accountRow, schemeRow, true); // By cycling through all the immediate children of the scheme record, we'll have covered the top-level // sectors in this appraisal. foreach (AppraisalSet.SchemeRow driverScheme in appraisalSet.Scheme) { foreach (AppraisalSet.ObjectTreeRow driverTree in driverScheme.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { foreach (AppraisalSet.SectorRow driverSector in driverTree.ObjectRowByFKObjectObjectTreeChildId.GetSectorRows()) { // The appraisal set collects the ids of the records used. We need to look up the actual sector // record from the ClientMarketData in order to search through it and aggregate sub-sectors and // securities. ClientMarketData.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(driverSector.SectorId); // Get the market value of the top-level sector, including all subaccounts and all positions. decimal actualSectorMarketValue = MarketValue.Calculate(currencyRow, accountRow, sectorRow, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts); // This will find the model percentage of the current top-level sector. If the sector wasn't // specified in the model, assume a value of zero, which would indicate that we're to sell the // entire sector. ClientMarketData.SectorTargetRow sectorTargetRow = ClientMarketData.SectorTarget.FindByModelIdSectorId(modelRow.ModelId, driverSector.SectorId); decimal targetPercent = sectorTargetRow == null ? 0.0M : sectorTargetRow.Percent; // The target market value is calculated from the model percentage and the actual aggregate // account market value. decimal targetSectorMarketValue = accountMarketValue * targetPercent; // Now that we have a target to shoot for, recursively descend into the structure calculating // propsed orders. RecurseSectors(remoteBatch, remoteTransaction, currencyRow, modelRow, driverSector.ObjectRow, actualSectorMarketValue, targetSectorMarketValue); } } } // This object holds a complete set of proposed orders to achieve the sector targets in the model. return(remoteBatch); }
/// <summary> /// Creates a temporary model based on the current sector 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 sector weights of the account.</returns> private static ModelBatch CreateSectorSelfModel(ClientMarketData.AccountRow accountRow, ClientMarketData.SchemeRow schemeRow) { // This command batch will create a temporary model and populate it with the current position level percentages as the // target values. 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 model. 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.Sector); insertModel.Parameters.Add("name", "Untitled"); insertModel.Parameters.Add("schemeId", schemeRow.SchemeId); insertModel.Parameters.Add("algorithmId", Algorithm.SectorMergeRebalancer); insertModel.Parameters.Add("temporary", true); // The 'Self Sector' uses the market value of all the account and sub-account. decimal accountMarketValue = MarketValue.Calculate(accountRow.CurrencyRow, accountRow, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts); // No need to construct a model if the account market value is zero. 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 industry classification sectors. Common.Appraisal appraisal = new Common.Appraisal(accountRow, schemeRow, true); // The object Type for this operation. RemoteType sectorTargetType = remoteAssembly.Types.Add("Shadows.WebService.Core.SectorTarget"); // Now that we have an outline to follow, we are going to run through each of the sectors, calculate the market // value, and create an entry in the temporary model for that sector and it's current weight of the overall market // value. foreach (AppraisalSet.SchemeRow driverScheme in appraisal.Scheme) { foreach (AppraisalSet.ObjectTreeRow driverTree in driverScheme.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId()) { foreach (AppraisalSet.SectorRow driverSector in driverTree.ObjectRowByFKObjectObjectTreeChildId.GetSectorRows()) { // This sector is the destination for the market value calculation. ClientMarketData.SectorRow sectorRow = ClientMarketData.Sector.FindBySectorId(driverSector.SectorId); // Calculate the market value of all the securities held by all the accounts in the current sector. decimal sectorMarketValue = MarketValue.Calculate(accountRow.CurrencyRow, accountRow, sectorRow, MarketValueFlags.EntirePosition | MarketValueFlags.IncludeChildAccounts); // Add the position level target to the model. RemoteMethod insertSector = sectorTargetType.Methods.Add("Insert"); insertSector.Parameters.Add("modelId", insertModel.Parameters["modelId"]); insertSector.Parameters.Add("sectorId", sectorRow.SectorId); insertSector.Parameters.Add("percent", sectorMarketValue / accountMarketValue); } } } } // Save the reference to the 'modelId' return parameter. modelBatch.ModelIdParameter = insertModel.Parameters["modelId"]; // This batch will create a temporary model based on the sector totals of the original account. return(modelBatch); }