예제 #1
0
        /// <summary>
        /// Creates a temporary copy of a model portfolio.
        /// </summary>
        /// <param name="modelRow">The original model record.</param>
        /// <returns>A batch of commands that will create a copy of the original model.</returns>
        private static ModelBatch CopyModel(ClientMarketData.ModelRow modelRow)
        {
            // Create the batch and fill it in with the assembly and type needed for this function.
            ModelBatch     modelBatch     = new ModelBatch();
            RemoteAssembly remoteAssembly = modelBatch.Assemblies.Add("Service.Core");
            RemoteType     remoteType     = remoteAssembly.Types.Add("Shadows.WebService.Core.Model");

            // This method will insert a copy of the original model's header.
            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("objectTypeCode", modelRow.ObjectRow.ObjectTypeCode);
            insertModel.Parameters.Add("name", String.Format("Copy of {0}", modelRow.ObjectRow.Name));
            insertModel.Parameters.Add("schemeId", modelRow.SchemeId);
            insertModel.Parameters.Add("algorithmId", modelRow.AlgorithmId);
            insertModel.Parameters.Add("temporary", true);
            insertModel.Parameters.Add("description", modelRow.ObjectRow.Description);

            // For a sector model, copy each of the sector level targets into the destination model.
            if (modelRow.ModelTypeCode == ModelType.Sector)
            {
                // The object Type for this operation.
                RemoteType sectorTargetType = remoteAssembly.Types.Add("Shadows.WebService.Core.SectorTarget");

                // Add the position level target to the model.
                foreach (ClientMarketData.SectorTargetRow sectorTargetRow in modelRow.GetSectorTargetRows())
                {
                    RemoteMethod insertSector = sectorTargetType.Methods.Add("Insert");
                    insertSector.Parameters.Add("modelId", insertModel.Parameters["modelId"]);
                    insertSector.Parameters.Add("sectorId", sectorTargetRow.SectorId);
                    insertSector.Parameters.Add("percent", sectorTargetRow.Percent);
                }
            }

            // For a position model, copy each of the position level targets into the destination model.
            if (modelRow.ModelTypeCode == ModelType.Security)
            {
                // The object Type for this operation.
                RemoteType positionTargetType = remoteAssembly.Types.Add("Shadows.WebService.Core.PositionTarget");

                // Add the position level target to the model.
                foreach (ClientMarketData.PositionTargetRow positionTargetRow in modelRow.GetPositionTargetRows())
                {
                    RemoteMethod insertSecurity = positionTargetType.Methods.Add("Insert");
                    insertSecurity.Parameters.Add("modelId", insertModel.Parameters["modelId"]);
                    insertSecurity.Parameters.Add("securityId", positionTargetRow.SecurityId);
                    insertSecurity.Parameters.Add("positionTypeCode", positionTargetRow.PositionTypeCode);
                    insertSecurity.Parameters.Add("percent", positionTargetRow.Percent);
                }
            }

            // Save the reference to the 'modelId' return parameter.
            modelBatch.ModelIdParameter = insertModel.Parameters["modelId"];

            // This batch will create a copy of the original model.
            return(modelBatch);
        }
예제 #2
0
        /// <summary>
        /// Constructs a well formed, but empty, AppraisalDocument.
        /// </summary>
        public AppraisalDocument()
        {
            // Initialize the members.
            this.accountRow = null;
            this.modelRow   = null;

            // Create the root element and add it to the document.
            this.AppendChild(new AppraisalElement(this));
        }
예제 #3
0
        /// <summary>
        /// Returns a set of orders that will achieve the targets specified by the model.
        /// </summary>
        /// <param name="accountRow">The account or parent account to be rebalanced.</param>
        /// <param name="modelRow">The target percentages to use for rebalancing.</param>
        /// <returns>A Dataset of new, updated and deleted orders.</returns>
        public static RemoteBatch Rebalance(ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow)
        {
            // The orders to insert, update and delete orders to achieve the target percentages will be put in this DataSet.
            RemoteBatch       remoteBatch       = new RemoteBatch();
            RemoteTransaction remoteTransaction = remoteBatch.Transactions.Add();

            // Rebalance the parent account and all it's children.
            SelectedSecurity.RecurseAccounts(remoteBatch, remoteTransaction, accountRow, modelRow);

            // This is the sucessful result of rebalancing.
            return(remoteBatch);
        }
예제 #4
0
        /// <summary>
        /// Returns a set of orders that will achieve the targets specified by the model.
        /// </summary>
        /// <param name="accountRow">The account or parent account to be rebalanced.</param>
        /// <param name="modelRow">The target percentages to use for rebalancing.</param>
        /// <returns>A Dataset of new, updated and deleted 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 the appraisal.
            ClientMarketData.SchemeRow schemeRow;
            if ((schemeRow = ClientMarketData.Scheme.FindBySchemeId(modelRow.SchemeId)) == null)
            {
                throw new ArgumentException("Scheme doesn't exist in the ClientMarketData", modelRow.SchemeId.ToString());
            }

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

            // Rebalance the parent account and all it's children.
            RecurseAccounts(remoteBatch, remoteTransaction, accountRow, modelRow, schemeRow);

            // The sucessful result of rebalancing.
            return(remoteBatch);
        }
예제 #5
0
        /// <summary>
        /// Returns a set of orders that will achieve the targets specified by the model.
        /// </summary>
        /// <param name="accountRow">The account or parent account to be rebalanced.</param>
        /// <param name="modelRow">The target percentages to use for rebalancing.</param>
        /// <returns>A Dataset of new, updated and deleted orders.</returns>
        public static RemoteBatch Rebalance(ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow)
        {
            // The orders to insert, update and delete orders to achieve the target percentages will be put in this
            // DataSet.
            RemoteBatch       remoteBatch       = new RemoteBatch();
            RemoteTransaction remoteTransaction = remoteBatch.Transactions.Add();

            // The outline of the appraisal will be needed to make calculations based on a position, that is a security,
            // account, position type combination.  Note that we're also including all the model securities in the
            // outline.  This triggers a rebalance if a security exists in the model, but doesn't exist yet in the
            // appraisal.
            AppraisalSet appraisalSet = new Appraisal(accountRow, modelRow, true);

            // Rebalance the parent account and all it's children.
            Security.RecurseAccounts(remoteBatch, remoteTransaction, appraisalSet, accountRow, modelRow);

            // This is the sucessful result of rebalancing.
            return(remoteBatch);
        }
예제 #6
0
        /// <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);
                }
            }
        }
예제 #7
0
        /// <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);
                }
            }
        }
예제 #8
0
        /// <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);
                }
            }
        }
예제 #9
0
        /// <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);
            }
        }
예제 #10
0
        /// <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);
        }
예제 #11
0
        /// <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);
        }
예제 #12
0
        /// <summary>
        /// Constructs an AppraisalDocument.
        /// </summary>
        /// <param name="accountRow">A record containing the account or fund data.</param>
        /// <param name="modelRow">A record containing the model that is superimposed on the appraisal data for
        /// rebalancing.</param>
        public AppraisalDocument(ClientMarketData.AccountRow accountRow, ClientMarketData.ModelRow modelRow)
        {
            // Create a view of the proposed orders that makes it easy to aggregate by position.
            this.proposedOrderView      = new DataView(ClientMarketData.ProposedOrder);
            this.proposedOrderView.Sort = "[AccountId], [SecurityId], [PositionTypeCode]";

            // Create a view of the orders that makes it easy to aggregate by position.
            this.orderView      = new DataView(ClientMarketData.Order);
            this.orderView.Sort = "[AccountId], [SecurityId], [PositionTypeCode]";

            // Create a view of the allocations that makes it easy to aggregate by position.
            this.allocationView      = new DataView(ClientMarketData.Allocation);
            this.allocationView.Sort = "[AccountId], [SecurityId], [PositionTypeCode]";

            // This makes the account and model avaiable to the recursive methods.
            this.accountRow = accountRow;
            this.modelRow   = modelRow;

            // Create the root element and add it to the document.
            AppraisalElement rootElement = new AppraisalElement(this);

            this.AppendChild(rootElement);

            // Create and populate the DataSet that represents the outline of the appraisal.  This function creates a
            // set of linked structures that contains only the sectors, securities and positions that will appear in
            // this document. This driver is built from the bottom up, meaning that we start with the tax lots,
            // proposed orders, orders and allocations associated with the given account and build the driver up to
            // the topmost security classification scheme.  The alternative -- starting with the classification scheme
            // and building down until we join with the tax lots, etc, -- turned out to be six times slower.
            this.appraisal = new Common.Appraisal(accountRow, modelRow, true);

            // This sector is a catch-all heading for securities not mapped to the given hierarchy.  If everything is
            // mapped, then this sector won't appear in the document.
            SectorElement unclassifiedSector = null;

            // Now that the driver is built, we can begin constructing the document.  The first section is the
            // 'Unclassified' sector.  This section catches all securities that aren't explicitly mapped to the
            // hierarchy.  This is important because classification schemes are not guaranteed to map every security.
            // Without this section, those unmapped securities wouldn't appear on the appraisal and wouldn't be
            // included in the NAV calculation.  That is very bad.  Note also that we skip over the classification
            // scheme record during the check.  We know that the security classification scheme is at the top of the
            // hierarchy and won't have any parents.  Every other record that doesn't have a parent in the hierarchy
            // is 'Unclassified'.
            foreach (AppraisalSet.SecurityRow parentSecurity in this.appraisal.Security)
            {
                if (parentSecurity.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeChildId().Length == 0)
                {
                    // If the document doesn't have an 'Unclassified' sector yet, then add the sector heading.  All
                    // secutiries that are not mapped to the given hierarchy will appear under this catch-all header.
                    if (unclassifiedSector == null)
                    {
                        rootElement.InsertBySortOrder(unclassifiedSector = new SectorElement(this));
                    }

                    // Attach the each of the unclassified securities to the unclassified sector heading.
                    BuildDocument(unclassifiedSector, parentSecurity.ObjectRow);
                }
            }

            // The report is built recursively.  The 'AppraisalSet', constructed above, represents an 'inner join' of the
            // hierarchy information to the active position information.  We'll begin traversing the 'AppraisalSet' from
            // the top level security: a single node representing the classification scheme.
            foreach (AppraisalSet.SchemeRow schemeRow in this.appraisal.Scheme)
            {
                foreach (AppraisalSet.ObjectTreeRow objectTreeRow in schemeRow.ObjectRow.GetObjectTreeRowsByFKObjectObjectTreeParentId())
                {
                    BuildDocument(rootElement, objectTreeRow.ObjectRowByFKObjectObjectTreeChildId);
                }
            }
        }