Plots & styles

Indicators

Plots & styles

Every visible output is a plot: a series plus how to draw it. Lines, dashes, areas, histograms, step lines and indicator-side candles — all restylable at runtime.

Adding a plot

A plot is one line on the chart. An indicator can own several — SMA has 1, MACD has 3 (line, signal, histogram), Bollinger Bands have 3 (upper, middle, lower). You build a Plot over your output series and register it with the protected AddPlot helper. The Plot constructor is (string name, ISeries<double> values, PlotStyle style, ChartColor color, double lineWidthPx).

adding plotsprivate readonly ChunkedSeries<double> _macd   = new();
private readonly ChunkedSeries<double> _signal = new();
private readonly ChunkedSeries<double> _hist   = new();

public MyMacd(IIndicator? parent = null, ISeries<double>? input = null) : base(parent, input)
{
    // Order matters: earlier plots render BEHIND later ones.
    AddPlot(new Plot("Histogram", _hist,   PlotStyle.Histogram, new ChartColor(120, 120, 120), 1.0));
    AddPlot(new Plot("MACD",      _macd,   PlotStyle.Line,      new ChartColor(33, 150, 243), 1.5));
    AddPlot(new Plot("Signal",    _signal, PlotStyle.Line,      new ChartColor(244, 67, 54),  1.5));
}

Draw order follows registration order: add the histogram first so the lines sit on top of it. The Plot constructor validates its arguments — a blank name or a lineWidthPx <= 0 throws immediately rather than corrupting render state.

When can I call AddPlot?

Only in the constructor or OnInit, before any other hook. The Plots list is append-only and its reference is stable for the indicator's lifetime — the chart render pass caches it across paints and the settings dialog binds to a fixed set of rows. After init you may mutate plot properties freely, but you must never add or remove plots.

Plot styles

PlotStyle selects the rendering primitive. NaN values are always skipped (lines break, dots/bars are omitted).

Style Renders as
Line Continuous line through consecutive non-NaN values. The default.
Dashed / Dotted Same path as Line with a dash / dot stroke pattern.
Area Filled area between the value and the zero baseline.
Histogram Vertical bars from zero to the value (positive up, negative down).
Dots Discrete dots at each bar; no connecting line.
Step Holds each value as a horizontal segment until the next bar, then jumps — for levels that hold then change (opening-range, prior-day).
Candle Per-bar OHLC candles. Requires the plot to implement IOhlcPlot (see below).

Runtime restyling

IPlot is mutable on purpose. The settings dialog recolours and restyles plots at runtime without reconstructing the indicator — the chart re-reads the properties on the next render pass. A property change does not trigger a recalculation, only a repaint.

mutating a plotIPlot signal = Plots[2];
signal.Color       = new ChartColor(255, 152, 0);
signal.Style       = PlotStyle.Dashed;
signal.LineWidthPx = 2.0;
signal.Visible     = false;     // hide without removing the plot

This is exactly why exposing a colour as an [IndicatorParameter] of type ChartColor works: the dialog writes the new colour onto the plot and the next frame shows it.

Price-axis markers

Each plot can paint a small filled "pill" on the price axis at its most recent value via IPlot.ShowPriceMarker (default true). The indicator-level ShowPriceMarkers property (inherited from IndicatorBase) is a master gate: when false, no marker for this indicator paints regardless of the per-plot setting — one click silences a noisy indicator's axis labels.

Indicator-side candles

Some indicators are naturally per-bar OHLC rather than a single value — Cumulative Delta, for example, draws an open/high/low/close candle of the running delta. Use PlotStyle.Candle with a plot that implements IOhlcPlot, which carries the four parallel series. The built-in OhlcPlot implements it.

OHLC plot// OhlcPlot exposes OpenValues / HighValues / LowValues + Values (the close),
// all aligned by barsAgo, plus bull/bear colours and a body-fill ratio.
var ohlc = new OhlcPlot("CumDelta", _open, _high, _low, _close)
{
    BullishColor  = new ChartColor(38, 166, 154),
    BearishColor  = new ChartColor(239, 83, 80),
    BodyFillRatio = 0.7,
};
AddPlot(ohlc);
Tagging a plain single-series plot with PlotStyle.Candle surfaces a clear error rather than rendering garbage — the renderer needs all four series, which only IOhlcPlot provides.

Plot semantics

You can optionally declare a plot's machine-readable meaning via PlotSemantics so an automated consumer (an AI reading the chart, an alert engine) can interpret RSI = 71.6 correctly without recognising the indicator by name. Set it on the Plot at AddPlot time.

declaring semanticsAddPlot(new Plot("RSI", _rsi, PlotStyle.Line, new ChartColor(156, 39, 176), 1.5)
{
    Semantics = new PlotSemantics(
        Kind: PlotKind.Oscillator,
        RangeMin: 0, RangeMax: 100,
        Unit: PlotValueUnit.Percent,
        BullishDirection: PlotBullishDirection.Up,
        Reference: "30/70 oversold/overbought"),
});

When you declare nothing the plot reports PlotSemantics.Default ("semantics unknown") and consumers degrade gracefully.