/// <summary> /// Initializes a Scheme. /// </summary> /// <param name="configurationId">Defines which external fields are used to identify an object.</param> /// <param name="schemeId">The scheme identifier.</param> /// <returns>A scheme record, null if the identifier doesn't exist.</returns> protected override void Initialize(int schemeId) { // Use the specified configuration to find the internal scheme identifier. ClientMarketData.SchemeRow schemeRow = ClientMarketData.Scheme.FindBySchemeId(schemeId); if (schemeRow == null) { throw new Exception(String.Format("Scheme {0} doesn't exist", schemeId)); } // Initialize the base class. base.Initialize(schemeId); }
/// <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); } } }
/// <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> /// Chooses or creates a model for the appraisal. /// </summary> /// <param name="accountId">The account used to select a model.</param> public static int SelectModel(int accountId) { // The logic in this method will determine if a temporary model is needed and built it. If a temporary model is // required, it will be built using this command batch. In all cases, the appropriate model for the given account will // be returned to the caller. In some cases, a model will be constructed on the fly from the existing values in the // account. These temporary models will use most of the position and trading tables ModelBatch modelBatch = null; 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.ModelLock.AcquireWriterLock(CommonTimeout.LockWait); ClientMarketData.SectorTargetLock.AcquireWriterLock(CommonTimeout.LockWait); ClientMarketData.PositionTargetLock.AcquireWriterLock(CommonTimeout.LockWait); ClientMarketData.ObjectLock.AcquireWriterLock(CommonTimeout.LockWait); ClientMarketData.ObjectTreeLock.AcquireWriterLock(CommonTimeout.LockWait); ClientMarketData.OrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.PriceLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.ProposedOrderLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SchemeLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SectorLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.SecurityLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TaxLotLock.AcquireReaderLock(CommonTimeout.LockWait); ClientMarketData.TransactionTypeLock.AcquireReaderLock(CommonTimeout.LockWait); // Find the account record that is being opened. ClientMarketData.AccountRow accountRow = ClientMarketData.Account.FindByAccountId(accountId); if (accountRow == null) { throw new Exception(String.Format("Account {0} has been deleted", accountId)); } // The objective is to find out whether a 'Self' model must be created from the existing positions, or whether a // an empty or a copy of a model is required to view an account appraisal. The first test is to see whether any model // has been assigned to the account. if (accountRow.IsModelIdNull()) { // This will create an empty position model for the appraisal. modelBatch = Models.CreateEmptyModel(accountRow); } else { // At this point, a model has been assigned to the account. Get the model and find out if a temporary copy // needs to be made. ClientMarketData.ModelRow modelRow = ClientMarketData.Model.FindByModelId(accountRow.ModelId); if (modelRow == null) { throw new Exception(String.Format("Model {0} has been deleted", accountRow.ModelId)); } // A 'self' model is one that requires a calculation of the current positions. if (!modelRow.SectorSelf && !modelRow.SecuritySelf) { // Currently, the existing model is used on an appraisal. Any changes to the model in the appraisal view // will be stored in the persistent model. It may be useful sometime in the future to make a copy of the // model and prompt the user to save it when the appraisal is closed. return(modelRow.ModelId); } else { // Make sure that the account has been assigned a scheme before attempting to build a model from it. if (accountRow.IsSchemeIdNull()) { throw new Exception(String.Format("No scheme has been assigned to account {0}.", accountRow)); } // If the account has a default scheme, make sure it still exists. ClientMarketData.SchemeRow schemeRow = ClientMarketData.Scheme.FindBySchemeId(accountRow.SchemeId); if (schemeRow == null) { throw new ArgumentException("This scheme has been deleted", accountRow.SchemeId.ToString()); } // Create a model based on the current sector totals. if (modelRow.SectorSelf) { modelBatch = Models.CreateSectorSelfModel(accountRow, schemeRow); } // Create a model based on the current position totals. if (modelRow.SecuritySelf) { modelBatch = Models.CreatePositionSelfModel(accountRow, schemeRow); } } } } 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.ModelLock.IsWriterLockHeld) { ClientMarketData.ModelLock.ReleaseWriterLock(); } if (ClientMarketData.SectorTargetLock.IsWriterLockHeld) { ClientMarketData.SectorTargetLock.ReleaseWriterLock(); } if (ClientMarketData.PositionTargetLock.IsWriterLockHeld) { ClientMarketData.PositionTargetLock.ReleaseWriterLock(); } if (ClientMarketData.ObjectLock.IsWriterLockHeld) { ClientMarketData.ObjectLock.ReleaseWriterLock(); } if (ClientMarketData.ObjectTreeLock.IsWriterLockHeld) { ClientMarketData.ObjectTreeLock.ReleaseWriterLock(); } if (ClientMarketData.OrderLock.IsReaderLockHeld) { ClientMarketData.OrderLock.ReleaseReaderLock(); } if (ClientMarketData.PriceLock.IsReaderLockHeld) { ClientMarketData.PriceLock.ReleaseReaderLock(); } if (ClientMarketData.ProposedOrderLock.IsReaderLockHeld) { ClientMarketData.ProposedOrderLock.ReleaseReaderLock(); } if (ClientMarketData.SchemeLock.IsReaderLockHeld) { ClientMarketData.SchemeLock.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); } // At this point, a batch is ready to be sent that will create the model and populate it with target values. The data // structure is an overloaded version of the 'RemoteBatch' class. The 'ModelBatch' contains a member which references // the 'modelId' return value from the creation of the model. This value will be returned to the caller as a reference // to the temporary model. ClientMarketData.Send(modelBatch); // Rethrow a generic error message for the failed model. if (modelBatch.HasExceptions) { throw new Exception("Can't create model."); } // Return the model identifier generated by the server. return((int)modelBatch.ModelIdParameter.Value); }
/// <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); }