Parameters

Indicators

Parameters

Expose user-tunable inputs — periods, colours, toggles, file paths — that the settings dialog renders, the workspace persists, and the engine uses to tell two indicators apart.

What a parameter is

A parameter is a public read/write property tagged with [IndicatorParameter] (from TradeStrike.Pipeline.Indicators). The framework reflects over these to do three things:

  • Render an editor. The settings dialog shows one row per attributed property.
  • Persist + restore. The value round-trips through the chart template / workspace.
  • Form the content key. The indicator's ContentKey is a deterministic hash of its type plus parameter values — so SMA(14) and SMA(50) are distinct instances, and two requests with the same key resolve to the same shared instance (diamond-dependency dedup).
Every parameter needs a real default. The host constructs the indicator with its defaults first, then applies the saved value. A parameter with no sensible default value will misbehave the moment a template is loaded.

Declaring parameters

declaring parameters[IndicatorParameter(
    DisplayName = "Period",
    Description = "Number of bars in the lookback window.",
    Group = "Parameters",
    Order = 0,
    MinValue = 1, MaxValue = 5000, Step = 1)]
public int Period { get; set; } = 14;

[IndicatorParameter(DisplayName = "Smoothing", Group = "Parameters", Order = 1)]
public SmoothingMode Smoothing { get; set; } = SmoothingMode.Exponential;   // enum -> dropdown

[IndicatorParameter(DisplayName = "Show midline", Group = "Display", Order = 0)]
public bool ShowMidline { get; set; } = true;

DisplayName defaults to the property name; Description becomes the editor tooltip. MinValue, MaxValue and Step are advisory — the editor enforces them, but a programmatic assignment honours whatever value the caller passes. The framework never silently clamps; if you need a hard bound, validate and throw in OnInit.

Editor types

By default the editor is inferred from the property's CLR type. Override it with the Editor field (a ParameterEditorKind) when the type alone is ambiguous.

Property type Editor
int / double Numeric spinner (honours Min/Max/Step).
bool Checkbox.
string Text box.
any enum Dropdown of the enum members.
ChartColor Colour picker.
string + Editor = ParameterEditorKind.FilePicker Path field + Browse button (use FileFilter for the dialog filter).
string + Editor = ParameterEditorKind.SymbolPicker Symbol field + Browse into the host's instrument picker (for a benchmark / comparison symbol).
non-obvious editors[IndicatorParameter(DisplayName = "Blueprint file",
    Editor = ParameterEditorKind.FilePicker,
    FileFilter = "Blueprint (*.algo)|*.algo|All files (*.*)|*.*")]
public string BlueprintPath { get; set; } = "";

[IndicatorParameter(DisplayName = "Benchmark", Editor = ParameterEditorKind.SymbolPicker)]
public string BenchmarkSymbol { get; set; } = "ES";

Groups and ordering

Group buckets parameters under a header in the dialog (matched by exact string), and Order sequences rows within a group (lower first; ties fall back to declaration order). Use a small handful of stable group names — "Parameters", "Display", "Calculation" — so related controls sit together.

Reading parameters in your code

Read parameter values in OnInit (after base.OnInit), not in the constructor. The host constructs the indicator, then applies the saved values, then calls OnInit — so the constructor sees only defaults, while OnInit sees the user's real choices. This is the place to validate and to size buffers from a period.

validate in OnInitpublic override void OnInit(IIndicatorContext ctx)
{
    base.OnInit(ctx);
    if (Period < 1)
        throw new System.ArgumentOutOfRangeException(nameof(Period), "Period must be >= 1.");
    _window = new double[Period];   // allocate state from the resolved parameter
}

When the user edits a math-affecting parameter, the host re-runs OnResetOnInitOnDataLoaded so the whole history recomputes with the new value — reallocating your buffers in OnInit is what makes that work.

Parameters from a nested object

A large indicator can keep its parameters in a separate, reusable settings POCO instead of scattering dozens of properties on the indicator. Tag the indicator with [ParameterContainer(typeof(...))] and implement IParameterContainerProvider; the platform discovers, edits and persists the container's tagged properties through the same machinery.

parameter containerpublic sealed class BandSettings
{
    [IndicatorParameter(DisplayName = "Width (StdDev)", Group = "Bands")]
    public double Width { get; set; } = 2.0;

    [IndicatorParameter(DisplayName = "Fill", Group = "Bands")]
    public ChartColor Fill { get; set; } = new(33, 150, 243, 40);
}

[ParameterContainer(typeof(BandSettings))]
public sealed class MyBands : IndicatorBase, IParameterContainerProvider
{
    public BandSettings Settings { get; } = new();
    public object ParameterContainer => Settings;   // IParameterContainerProvider
}

Set AllProperties = true on the attribute to surface every persistable public property of the container as a parameter without tagging each one individually — handy for a settings object with 100+ fields.

Built-in parameters you inherit

IndicatorBase already declares two parameters every indicator gets for free, grouped under "Display":

  • ShowPriceMarkers (bool) — a master toggle for this indicator's price-axis labels; off hides every marker regardless of per-plot settings.
  • Displacement (int) — shifts the indicator's plots horizontally by N bars (positive = right/future, negative = left/past). It is purely visual: the computed value series is never altered, so alerts and strategies keep reading the true values.