Clone our Wealth-Lab 8 Extension Demo project on GitHub to get a head start in developing your own Extensions!

Position Sizer API

A Position Sizer Extension determines the quantity of shares, contracts, or other units to assign to a Transaction during a WealthLab 9 backtest. A Position Sizer has access to the current and historical equity and cash levels of the simulation, open and closed Positions, Transactions being processed, and other information that can influence the sizing decision. Position Sizers are available in the Position Sizing section of the Strategy Settings.

Build Environment

You can create an Advanced Position Sizer in a .NET development tool such as Visual Studio 2026. Create a class library project that targets .NET10, then reference the WealthLab.Core library DLL that you'll find in the WL9 installation folder.

Your Advanced Position Sizer will be a class in this library that descends from PositionSizerBase, which is defined in the WealthLab.Core library, in the WealthLab.Backtest namespace. After you implement and build your library, simply copy the resulting assembly DLL into the WL9 installation folder. The next time WL9 starts up, it will discover your Advanced Position Sizer, making it available in appropriate locations of the WL9 user interface.

Visual Studio 2026 Build Environment

Accessing the Host (WL9) Environment

The IHost interface provides access to the current WealthLab environment. Extensions can use it to retrieve application-level information and services, such as the location of the user's WealthLab data folder or the DataSets defined by the user.

You can access the current IHost instance from anywhere in your extension through the WLHost singleton and its Instance property. For example, the following code retrieves the path to the user's WealthLab data folder:

string folder = WLHost.Instance.DataFolder;

Use WLHost.Instance whenever your extension needs access to functionality exposed by the IHost interface.

Descriptive Properties

Override the following properties to describe your Position Sizer and indicate which features it supports.

Name

public abstract string Name

Return the descriptive name of the Position Sizer. WealthLab displays this name in the Position Sizing selector in Strategy Settings.

Description

public virtual string Description

Return a brief description of how the Position Sizer works. WealthLab displays this description when the user selects or configures the Position Sizer.

UsesBasisPrice

public virtual bool UsesBasisPrice

Return true if the Position Sizer should allow the user to select the basis price used when calculating position size. For Market orders, the user can select either:

  • Current bar closing price
  • Next bar opening price

If UsesBasisPrice returns false, this option is hidden from the Position Sizing interface.

UsesMaxRiskPct

public virtual bool UsesMaxRiskPct

Return true if your Position Sizer uses the Max Risk Percent sizing mechanism. This allows WealthLab to automatically issue a Stop Loss order at the Transaction's RiskStopLevel when the corresponding Trading Preference is enabled.

GenerateParameters

protected virtual void GenerateParameters()

Override this method to define Parameters that configure your Position Sizer. Add the appropriate Parameter instances to the inherited Parameters collection. PositionSizerBase derives from Configurable, so Position Sizers use the standard WealthLab Parameter configuration framework.

Sizing Positions

Initialize

public virtual void Initialize()

WealthLab calls this method before it begins sizing Positions. Override it to perform one-time initialization, such as reading configured Parameter values into local fields.

SizePosition

public abstract double SizePosition(
    Transaction t,
    BarHistory bars,
    int idx,
    double basisPrice,
    double equity,
    double cash)

WealthLab calls SizePosition each time it needs to determine the quantity for a Transaction. Return the desired position size as a double. The parameters provide:

  • t - The Transaction being sized.
  • bars - The BarHistory associated with the Transaction.
  • idx - The current index in the BarHistory.
  • basisPrice - The price WealthLab should use as the basis for sizing.
  • equity - Current simulated account equity.
  • cash - Current simulated available cash.

The basisPrice depends on the order type. For a Market order, it is based on the Position Sizing setting selected by the user:

  • Current bar closing price
  • Next bar opening price

For Limit and Stop orders, the basis price is the Limit or Stop price. Use these values together with the simulation-related properties described below to calculate and return the desired quantity. Returning zero prevents the Transaction from being taken.

PositionSizerBase exposes information about the current state of the backtest.

EquityCurve

public TimeSeries EquityCurve

Contains the simulated account equity over time. The series is populated only through the point in the simulation at which the current Transaction is being sized.

CashCurve

public TimeSeries CashCurve

Contains the available simulated cash over time.

DrawdownCurve

public TimeSeries DrawdownCurve

Contains the simulated account drawdown over time. Drawdown represents the peak-to-trough decline in the equity curve and is calculated on a closing-price basis.

DrawdownPctCurve

public TimeSeries DrawdownPctCurve

Contains the percentage drawdown of the simulated account over time.

Orders

public List<Transaction> Orders

Contains Transactions that have already been sized and processed on the current bar. The collection can include both entry and exit orders.

OpenPositions

public List<Position> OpenPositions

Contains all Positions that are currently open in the simulation.

ClosedPositions

public List<Position> ClosedPositions

Contains all Positions that have already been closed.

Positions

public List<Position> Positions

Contains all Positions in the simulation, both open and closed.

Candidates

public List<Transaction> Candidates

Contains all entry Transactions currently being processed for position sizing on the current bar. This can be useful for Position Sizers that need to allocate capital among multiple simultaneous entry Signals.

Helper Methods

CalculatePositionSize

public double CalculatePositionSize(
    PositionSizeTypes pst,
    double value,
    BarHistory bars,
    double basisPrice,
    double equity,
    int? idx)

Calculates a position size using one of WealthLab's standard Position Sizing methods. The pst parameter specifies the sizing method. Supported PositionSizeTypes include:

  • Dollar - Allocate a fixed amount of currency specified by value.
  • Quantity - Use a fixed number of shares or contracts specified by value.
  • PctOfEquity - Allocate the percentage of current simulated equity specified by value.

You will typically call this method from SizePosition, passing along the bars, basisPrice, equity, and idx values supplied by WealthLab. For example:

return CalculatePositionSize(
    PositionSizeTypes.PctOfEquity,
    10.0,
    bars,
    basisPrice,
    equity,
    idx);

UseFuturesMode

public bool UseFuturesMode(BarHistory bars)

Returns true if Futures Mode should be used for the specified BarHistory. When Futures Mode applies, sizing logic can take advantage of contract-specific information available through:

bars.SymbolInfo

Important properties include:

  • PointValue
  • Margin
  • TickSize

The Margin property is particularly important because it determines how much capital is required to enter one contract. For example, a Position Sizer allocating $10,000 might use:

if (UseFuturesMode(bars))
    return 10000.0 / bars.SymbolInfo.Margin;
else
    return 10000.0 / basisPrice;

BasicPositionSizer

If your Position Sizer builds on WealthLab's standard sizing choices, derive from BasicPositionSizer instead of PositionSizerBase. BasicPositionSizer automatically provides two Parameters:

  1. Position Size Type - A StringChoice containing the standard sizing methods.
  2. Amount - A Double containing the sizing amount.

The standard methods are:

  • Fixed Dollar amount
  • Fixed Quantity
  • Percent of Equity

BasicPositionSizer also implements Initialize to read the configured values and SizePosition to calculate the appropriate quantity using CalculatePositionSize. Its SizePosition implementation follows this pattern:

public override double SizePosition(
    Transaction t,
    BarHistory bars,
    int idx,
    double basisPrice,
    double equity,
    double cash)
{
    return CalculatePositionSize(
        _posSizeType,
        _amount,
        bars,
        basisPrice,
        equity,
        idx);
}

Derive from BasicPositionSizer when you want to preserve WealthLab's standard sizing choices while adding additional rules or restrictions.

Example: Max Entries per Bar

The following Position Sizer derives from BasicPositionSizer. It retains the standard Position Sizing choices but returns a size of zero when the number of entry Transactions exceeds a configured maximum. For intraday Strategies, it can optionally apply the maximum across the entire trading day rather than separately to each bar.

using System.Linq;
using WealthLab.Core;

namespace WealthLab.Backtest
{
    public class MaxEntriesPerBar : BasicPositionSizer
    {
        public override void GenerateParameters()
        {
            base.GenerateParameters();

            Parameters.Add(
                new Parameter(
                    "Max Entries",
                    ParameterType.Int32,
                    2,
                    1.0,
                    999999999.0));

            Parameters.Add(
                new Parameter(
                    "For intraday trades, sum up positions opened during the day",
                    ParameterType.Boolean,
                    false));
        }

        public override string Name =>
            "Max Entries per Bar";

        public override string Description =>
            "Provides the basic Position Sizing options, " +
            "with the additional ability to limit the " +
            "number of entries taken on each bar or day.";

        public override void Initialize()
        {
            base.Initialize();

            _maxEntries =
                Parameters[2].AsInt;

            _considerIntraday =
                Parameters[3].AsBoolean;
        }

        public override double SizePosition(
            Transaction t,
            BarHistory bars,
            int idx,
            double basisPrice,
            double equity,
            double cash)
        {
            int count = 0;

            if (!_considerIntraday)
            {
                foreach (Transaction o in Orders)
                {
                    if (o.TransactionType.IsEntry())
                        count++;
                }
            }
            else
            {
                if (t.Bars.Scale.IsIntraday)
                {
                    var positionsInSymbolOpenedToday =
                        Positions
                            .AsParallel()
                            .AsOrdered()
                            .Reverse()
                            .TakeWhile(p =>
                                p.EntryDate.DayOfYear ==
                                    t.EntryDate.DayOfYear &&
                                p.Symbol == t.Symbol);

                    count +=
                        positionsInSymbolOpenedToday.Count();
                }
            }

            if (count >= _maxEntries)
                return 0.0;

            return base.SizePosition(
                t,
                bars,
                idx,
                basisPrice,
                equity,
                cash);
        }

        private int _maxEntries;
        private bool _considerIntraday;
    }
}

This pattern is useful when you want to retain WealthLab's standard sizing calculations while adding portfolio-level rules that can reject or modify individual entry Signals.