In order to create complex position sizing methods Position Sizer Plugins can be used.

Structurally, a Position Sizer is a .NET class that derives from PosSizerBase class included in the BTAnalyticsBase.dll assembly and is decorated with the PosSizerInfo Attribute.

The logic desired for the custom Position Sizer should be contained in the overriden CalculatePositionSize method inherited from the PositionSizerBase class. The Position Sizer can access a variety of information as properties inherited from the base class, such as the equity curve, active trades, closed trades, candidate trades, etc.

Also, Position Sizers are able to define Parameters that can be set from the Simulation Builder Dialog.

Parameter: Object that represents decimal or integer values. The parameters can be used in optimization plugins as optimization parameters because they define a range of valid values and a step value. Optimization Plugins may be available in the future.
ParameterSet: Represents a set of parameters.

 

The PosSizerBase class.

The PosSizerBase class is the base class for all the Position Sizers. Its simplified structure is as follows:

 

  public abstract class PosSizerBase

   {

      public ParameterSet Parameters { get; set; }

      public SimulationCurvesDAE GeneralCurves { get; set; }

      public IList<Position> ActiveTrades { get; set; }

      public IList<Position> ClosedTrades { get; set; }

      public IList<RawTrade> CandidateTrades { get; set; }

       public SimulationConfig SimulationConfig { get; set; }

 

      public virtual void InitializePosSizer()

     

      // Method that creates all the parameters used by the Position Sizer.

      public abstract ParameterSet CreateParameters();

 

      // Method that calculates the position size for the given trade.

      // Returns the position size (as shares/contracts)

      public abstract int CalculatePositionSize(RawTrade trade);

 

      // This object must inherit from UserControl

      private object _configurationUIUserControl=null;

      public object ConfigurationUIUserControl

       {

          get { return _configurationUIUserControl; }

          protected set { _configurationUIUserControl = value; }

       }

 

      public bool HasConfigurationUI

       {

          get { return ConfigurationUIUserControl!=null; }

       }

 

      // **********************************************************************************

      // To override only if the Position Sizer has configuration settings.

 

      // Method that creates an instance of the Configuration control.

      public virtual void InitializeUI()

 

      // Method that sets the Configuration control state using the current Position Sizer Configuration as a base.

      // (Position Sizer Configuration ---> Configuration Control State)

      public virtual void SetUIFromConfiguration()

 

      // Method that sets the Position Sizer Configuration using the Configuration control state as a base.

      // (Configuration control state ---> Position Sizer Configuration)

      public virtual void SetConfigurationFromUI()

 

      // Method that sets the currently active pararameters using the current Position Sizer configuration as a base.

      // (Position Sizer Configuration ---> Active/inactive Parameters)

      public virtual void SetActiveParametersFromConfiguration()

 

      // Method that serializes the current Position Sizer configuration to a string.

      // (Position Sizer Configuration ---> String)

      public virtual string GetConfigurationAsString()

 

      // Method that deserializes a string containing the configuration of the Position Sizer to the current Position Sizer configuration.

      // (String ---> Position Sizer Configuration)

      public virtual void SetConfigurationFromString(string s)

 

 

      // ******************* Access to the Attribute info **********************

 

      public Guid GetPosSizerId()      

      public string GetPosSizerName()      

      public string GetPosSizerDescription()      

   }

 


 

 

Creating a basic custom Position Sizer.

To make a basic Position Sizer, you must override at least the CreateParameters and CalculatePositionSize methods of the PosSizerBase class. You must also decorate the Position Sizer class with the PosSizerInfo attribute. This attribute should take as parameters the Position Sizer Name, an unique GUID that BTAnalytics will use to internally identify the Position Sizer and the Position Sizer description.

 

The CreateParameters method defines the list of parameters that will appear in the "Simulation Builder" dialog, under the "Position Sizing" Tab.

 

The CalculatePositionSize method calculates the position size for the given trade. In this method, you can access the parameters values, the equity curve, liquidity curve, active trades, candidate trades (the trades that are candidate to enter in the current bar) and closed trades. All those pieces of information can be accessed through the inherited properties.

 

The following code shows the structure of the Equity Percent Position Sizer.

 

  [PosSizerInfo("Equity Percent Sizer", "356F3DA5-36AA-4B28-8829-B13DB176B81C", "Equity Percent Position Sizing")]

  public sealed class EquityPercentPosSizer : PosSizerBase

   {

     

       #region Overrides of PosSizerBase

 

      public override ParameterSet CreateParameters()

       {

          Parameter param = new Parameter("Percent", 10, 5, 100, 5, false, false, Conventions.FPDecimals, "Percent of Equity",true);

          ParameterSet parameters = new ParameterSet(param);

          return parameters;

       }

     

      public override int CalculatePositionSize(RawTrade trade)

       {

          int returnValue = 0;

 

          if (Parameters.ExistsParameter("Percent"))

           {

              double amount = Parameters.GetParameter("Percent").Value*GeneralCurves.Equity/100;

              if (trade.Instrument.Type == InstrumentTypes.Stock)

               {

                  double basisPriceConvertedToSimCurrency = CurrencyConversionEngine.GetTradeBasisPriceConvertedToCurrency(trade, SimulationCurrency.AccountCurrency);

                   returnValue = (int)Math.Floor(amount / basisPriceConvertedToSimCurrency);

               }

              else // Futures:

               {

                   double marginConvertedToSimCurrency = CurrencyConversionEngine.GetPriceConvertedToCurrency(trade.Instrument.Margin, trade.EntryDate, trade.Instrument.QuoteCurrency, SimulationCurrency.AccountCurrency);

                   returnValue = (int)Math.Floor(amount / marginConvertedToSimCurrency);

               }

           }

 

          return returnValue;

       }

 

       #endregion

   }


 

 

Creating a Position Sizer with Configuration User Interface:

You can also create a Position Sizer with a configuration user interface that will allow you to configure the Position Sizer by clicking on the "Configuration..." button situated in the Position Sizer panel of the Simulation Builder. In order to do so, in addition to CreateParameters and CalculatePositionSize you must set the configuration user interface (setting the ConfigurationUIUserControl property) and override also the the following methods of the base class:

 

InitializeUI: You can use this method to create the Configuration User Interface control and to set the ConfigurationUIUserControl property.
SetUIFromConfiguration: This method must include the code that initializes the User Interface starting from the current Position Sizer Configuration.
SetConfigurationFromUI: This method is the reciprocal of the prior: Must set the Position Sizer Configuration starting from the User Interface selected values.
SetActiveParametersFromConfiguration: In this method you must include code to determine which parameters must be active or inactive based on the current Position Sizer Configuration.
GetConfigurationAsString: Method that should include code to serialize the current Configuration state to a string in order to persist it. (The string should contain information about the configuration but not about the Parameters, because the Parameters are automatically serialized internally by BTAnalytics).
SetConfigurationFromString: This method is the reciprocal of the prior: It should include code to deserialize a string containing a description of the Position Sizer Configuration.

 

The following code shows the structure of a Position Sizer that includes a configuration user interface.

 

 

  // This is an example showing how to implement an external position sizer.

  // This Position Sizer has an asociated configuration dialog that allows to select a position sizing method.

  // Depending upon the selected position sizing method different parameters are activated.

 

   [PosSizerInfo("Basic Configurable Position Sizer", "7842CFA5-B78D-439A-A30D-8C42BA700000", "Position Sizer that allows to select among some basic Position Sizers and can serve as a base for other Postion Sizers.")]

  public class BasicConfigurablePositionSizer : PosSizerBase

   {

      // Variables that store the current Position Sizer Configuration.

      PosSizerTypes _psType = PosSizerTypes.FixedMoney;

 

       #region Overrides of PosSizerBase

 

      // Method that creates all the parameters used by the Position Sizer.

      public override ParameterSet CreateParameters()

       {

          Parameter param1 = new Parameter("Money Amount", 1000, 1000, 1000, 100, false, false, Conventions.CurrencyDecimals, "Money Amount", true);

          Parameter param2 = new Parameter("Equity Pct", 10, 5, 100, 5, false, false, Conventions.FPDecimals, "Percent of Equity", true);

          Parameter param3 = new Parameter("Max Risk Pct", 5, 2, 20, 2, false, false, Conventions.FPDecimals, "Max Risk Percent", true);

          ParameterSet parameters = new ParameterSet(param1, param2, param3);

          return parameters;

       }

 

      // Method that creates an instance of the Configuration control.

      public override void InitializeUI()

       {

          BasicConfigurablePositionSizerUI configUI = new BasicConfigurablePositionSizerUI();

           ConfigurationUIUserControl = configUI;

       }

 

      // Method that sets the Configuration control state using the current Position Sizer Configuration as a base.

      // (Position Sizer Configuration ---> Configuration Control State)

      public override void SetUIFromConfiguration()

       {

          BasicConfigurablePositionSizerUI configUI = ConfigurationUIUserControl as BasicConfigurablePositionSizerUI;

          if (configUI != null)

               configUI.PositionSizerMethodSelected = _psType;

       }

 

      // Method that sets the Position Sizer Configuration using the Configuration control state as a base.

      // (Configuration control state ---> Position Sizer Configuration)

      public override void SetConfigurationFromUI()

       {

          BasicConfigurablePositionSizerUI configUI = ConfigurationUIUserControl as BasicConfigurablePositionSizerUI;

          if (configUI != null)

               _psType = configUI.PositionSizerMethodSelected;

 

           SetActiveParametersFromConfiguration();

       }

 

      // Method that sets the currently active pararameters using the current Position Sizer configuration as a base.

      // (Position Sizer Configuration ---> Active/inactive Parameters)

      public override void SetActiveParametersFromConfiguration()

       {

          if (_psType == PosSizerTypes.FixedMoney)

           {

               Parameters["Money Amount"].Active = true;

               Parameters["Equity Pct"].Active = false;

               Parameters["Max Risk Pct"].Active = false;

           }

          else if (_psType == PosSizerTypes.PercentOfEquity)

           {

               Parameters["Money Amount"].Active = false;

               Parameters["Equity Pct"].Active = true;

               Parameters["Max Risk Pct"].Active = false;

           }

          else if (_psType == PosSizerTypes.MaxPercentRisk)

           {

               Parameters["Money Amount"].Active = false;

               Parameters["Equity Pct"].Active = false;

               Parameters["Max Risk Pct"].Active = true;

           }

       }

 

      // Method that serializes the current Position Sizer configuration to a string.

      // (Position Sizer Configuration ---> String)

      public override string GetConfigurationAsString()

       {

          string cfgStr = "";

          if (_psType == PosSizerTypes.FixedMoney)

           {

               cfgStr = "FixedMoney";

           }

          else if (_psType == PosSizerTypes.PercentOfEquity)

           {

               cfgStr = "PercentOfEquity";

           }

          else if (_psType == PosSizerTypes.MaxPercentRisk)

           {

               cfgStr = "MaxPercentRisk";

           }

 

          return cfgStr;

       }

 

      // Method that deserializes a string containing the configuration of the Position Sizer to the current Position Sizer configuration.

      // (String ---> Position Sizer Configuration)

      public override void SetConfigurationFromString(string s)

       {

          if (s == "FixedMoney")

           {

               _psType = PosSizerTypes.FixedMoney;

           }

          else if (s == "PercentOfEquity")

           {

               _psType = PosSizerTypes.PercentOfEquity;

           }

          else if (s == "MaxPercentRisk")

           {

               _psType = PosSizerTypes.MaxPercentRisk;

           }

       }

 

      // Method that calculates the position size for the given trade.

      // Returns the position size (as shares/contracts)

      public override int CalculatePositionSize(RawTrade trade)

       {

          int returnValue = 0;

 

          if (_psType == PosSizerTypes.FixedMoney)

           {

              if (Parameters.ExistsParameter("Money Amount"))

               {

                  if (trade.Instrument.Type == InstrumentTypes.Stock)

                   {

                      double basisPriceConvertedToSimCurrency = CurrencyConversionEngine.GetTradeBasisPriceConvertedToCurrency(trade, SimulationConfig.AccountCurrency);

                       returnValue = (int)Math.Floor(Parameters.GetParameter("Amount").Value / basisPriceConvertedToSimCurrency);

                   }

                  else // Futures:

                   {

                      double marginConvertedToSimCurrency = CurrencyConversionEngine.GetPriceConvertedToCurrency(trade.Instrument.Margin, trade.EntryDate, trade.Instrument.QuoteCurrency, SimulationConfig.AccountCurrency);

                       returnValue = (int)Math.Floor(Parameters.GetParameter("Amount").Value / marginConvertedToSimCurrency);

                   }

               }

           }

          else if (_psType == PosSizerTypes.PercentOfEquity)

           {

              if (Parameters.ExistsParameter("Equity Pct"))

               {

                  double amount = Parameters.GetParameter("Equity Pct").Value * GeneralCurves.Equity / 100;

                  if (trade.Instrument.Type == InstrumentTypes.Stock)

                   {

                      double basisPriceConvertedToSimCurrency = CurrencyConversionEngine.GetTradeBasisPriceConvertedToCurrency(trade, SimulationConfig.AccountCurrency);

                       returnValue = (int)Math.Floor(amount / basisPriceConvertedToSimCurrency);

                   }

                  else // Futures:

                   {

                      double marginConvertedToSimCurrency = CurrencyConversionEngine.GetPriceConvertedToCurrency(trade.Instrument.Margin, trade.EntryDate, trade.Instrument.QuoteCurrency, SimulationConfig.AccountCurrency);

                       returnValue = (int)Math.Floor(amount / marginConvertedToSimCurrency);

                   }

               }

           }

          else if (_psType == PosSizerTypes.MaxPercentRisk)

           {

              if (Parameters.ExistsParameter("Max Risk Pct"))

               {

                  double maxRiskPct = Parameters.GetParameter("Max Risk Pct").Value;

                  double equity = GeneralCurves.Equity / 100;

                  double initalStopLevel = trade.AutoStopLevel;

 

                  double basisPriceConvertedToSimCurrency = CurrencyConversionEngine.GetTradeBasisPriceConvertedToCurrency(trade, SimulationConfig.AccountCurrency);

                  double entryPriceConvertedToSimCurrency = CurrencyConversionEngine.GetTradeEntryPriceConvertedToCurrency(trade, SimulationConfig.AccountCurrency);

                  double initialStopLevelConvertedToSimCurrency = CurrencyConversionEngine.GetPriceConvertedToCurrency(initalStopLevel, trade.EntryDate, trade.Instrument.QuoteCurrency, SimulationConfig.AccountCurrency);

                  double pointValue = trade.Instrument.PointValue; // don't convert to currency.

                  double marginConvertedToSimCurrency = CurrencyConversionEngine.GetPriceConvertedToCurrency(trade.Instrument.Margin, trade.EntryDate, trade.Instrument.QuoteCurrency, SimulationConfig.AccountCurrency);

 

                  double amount = (maxRiskPct * equity);

                  if (trade.Instrument.Type == InstrumentTypes.Stock)

                   {

                      int rv1 = (int)Math.Floor(amount / Math.Abs(entryPriceConvertedToSimCurrency - initialStopLevelConvertedToSimCurrency));

                      int rv2 = (int)Math.Floor(amount / basisPriceConvertedToSimCurrency);

 

                       returnValue = Math.Min(rv1, rv2);

                   }

                  else // Futures:

                   {

                      int rv1 = (int)Math.Floor(amount / (Math.Abs(entryPriceConvertedToSimCurrency - initialStopLevelConvertedToSimCurrency) * pointValue));

                      int rv2 = (int)Math.Floor(amount / marginConvertedToSimCurrency);

 

                       returnValue = Math.Min(rv1, rv2);

                   }

               }

           }

 

          return returnValue;

       }
 

 

       #endregion

   }

 


 

You can download a Microsoft Visual Studio sample project that shows several examples of Position Sizers in the downloads section of the BTAnalytics website: BTAnalytics Download Area (download BasicPositionSizers.Zip). Among the included examples are inheritance examples (Position Sizers that inherits and extends the behavior and appearance of other Position Sizers.)