Plots & styles
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.
On this page
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?
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);
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.