AMMDocumentationWebsite ↗
how to · 4 min read

Create streaming chart and screener indicators

Declare stable types, emit causal samples and package renderer metadata with the plugin.

Reviewed 2026-10-24 · Windows & macOS

On this page

Use the streaming plugin contract#

Create an external scaffold with kind indicator or both. A chart indicator implements IChartIndicatorPlugin and creates an instrument/run-scoped IChartIndicatorStream. The host has no batch compatibility implementation to fill in missing streaming behavior. Publish the manifest-backed payload under a watched root and inspect the loaded indicator catalogue.

Keep the Type stable. Saved charts, screener columns, algorithms and templates store it. A friendly label can be edited without changing that ID; a casually renamed Type breaks existing configurations. The catalogue declares label, description, default panel and parameter definitions/ranges. Use the same identifier in runtime metadata and samples.

A minimal display-bar indicator#

This close-line probe shows the structure without pretending to implement a trading signal:

using AMM.Algorithms;

public sealed class CloseProbePlugin : IChartIndicatorPlugin
{
    public string Type => "my_close_probe";
    public PluginIndicatorCatalogEntry Catalog => new()
    {
        Type = "my_close_probe",
        Label = "Close probe",
        Description = "Displays the received display-bar close.",
        DefaultPanelId = "main"
    };
    public IChartIndicatorStream CreateStream(
        ChartIndicatorDefinition indicator,
        IPluginIndicatorSessionCalendar? calendar) => new Stream(indicator);

    private sealed class Stream : IChartIndicatorStream
    {
        public Stream(ChartIndicatorDefinition indicator)
        {
            Metadata = new PluginIndicatorSeriesMetadata
            {
                IndicatorId = indicator.Id, Type = "my_close_probe",
                ValueLabel = "Close", ValueStyle = "line"
            };
        }
        public PluginIndicatorSeriesMetadata Metadata { get; }
        public void OnSourceBar(PluginIndicatorBar bar) { }
        public PluginIndicatorSample? OnDisplayBar(PluginIndicatorBar bar) =>
            new() { Timestamp = bar.Timestamp, Value = bar.Close };
    }
}

Place it in the scaffold project, build using the generated README and add the discovered type to a chart. Compare the line with the candle close. The default zero warmup is appropriate for this probe because it has no rolling state. A real rolling indicator must declare sufficient history and implement parameter validation.

Source bars and display bars#

The stream receives OnSourceBar before display bars derived from that observation. Rolling indicators normally compute on display bars; session-sensitive and multi-timeframe indicators can retain source-time context. A sample must carry the current display-bar timestamp so it maps to the correct candle.

Keep multi-timeframe values causal. A completed H4 condition can persist over later lower-timeframe display bars, while a one-bar entry pulse belongs only to its exact confirmation candle. Do not shift a marker to whichever display timestamp happens to be closest. Use the provided session calendar rather than assuming midnight UTC is every instrument's day boundary.

A null sample means no sample exists and breaks a rendered line. It is appropriate before readiness, but repeated nulls after readiness can create misleading gaps. A neutral computed zero is different from unavailable. Emit ready lanes while a slower lane warms; do not blank the entire indicator until every optional higher timeframe is ready.

Separate warmup from visible context#

GetRequiredWarmupBars requests hidden seed history. GetMinimumVisibleBars requests useful painted context. A multi-month seed requirement should not force the chart to display that entire history. Test the left edge after a cold load, scrolling, changing timeframe and opening the same indicator in a screener.

Library indicators differ in partial-value behavior. SMA/EMA and several volatility calculations can emit immediately; RSI/Stochastic and other series have specific readiness rules. Preserve the formula's declared seed and smoothing. AMM's SMA-seeded EMA and first-close EMA are distinct tools; selecting either without recording the choice can change parity.

Publish generic renderer metadata#

PluginIndicatorSeriesMetadata supplies the primary label/style and named extra-value labels/styles/order. Samples supply decimal values. Use generic roles such as line, histogram, reference level, signal marker, support/resistance or background. Put an ordinary price overlay in panel main; a separate panel ID creates its own pane.

Use persistent level styles for real horizontal runs. Sparse plan prices and diagnostic event values can use presentation-only metadata instead of painting fictitious continuous levels. If a line changes colour through separate outputs, share the transition sample between outgoing and incoming segments so it remains connected.

Trade-entry markers use the shared entry role, enabling the chart's signal-visibility control to hide badges without disabling calculations, alerts or screener values. Ordinary candlestick detections should keep their reversal/continuation semantics instead of being mislabeled as entries.

Parameters, screeners and algorithms#

Declare every public parameter with a stable key, type, default and useful bounds. Test invalid values at runtime; UI bounds alone are insufficient for saved JSON/API requests. Screeners consume the plugin's numeric primary/extra outputs, so use stable output keys and explicit neutral pulses where a stale prior signal must clear.

Algorithms request a custom type through ChartIndicatorDefinition with a stable instance ID, CustomType, panel and scalar parameters. Several instances can share the same Type while using different IDs/periods. Templates must keep those instance IDs coherent with any rules referring to them.

Verify and maintain#

Test small known input sequences, warmup edges, parameter extremes, resets, source/display ordering and no-lookahead behavior. Compare same-feed, same-session observations before claiming visual or numerical parity with another platform. A research reconstruction is not automatically the protected original indicator.

Publish the complete DLL/manifest/dependency package, verify loaded identity and reopen a new run. Keep source formulas, tests and companion templates together. See Plugins, Algorithm authoring and Plugin templates for the surrounding workflow.

Application screenshotOpen original ↗