Indicators

Reference

Indicators

The contract surface for building indicators: the IIndicator lifecycle, the IIndicatorContext runtime view, the IndicatorBase base class, the declaration attributes, the cadence enums, and the optional render / interaction capability interfaces. Everything here lives in TradeStrike.Pipeline.Indicators.

IIndicator INTERFACE

The lifecycle contract every indicator implements (it extends IDisposable). The runtime calls the hooks; implementations never call them on each other — they communicate through child indicators and the shared bar/series storage. Callbacks are single-threaded per instance: no two hooks fire in parallel for the same indicator.

Lifecycle ordering: OnInit once → OnDataLoaded once (with all historical bars already in storage) → OnBarUpdate per closed/intra-bar event at the Calculate cadence → OnMarketData per live tick (only if ProcessesMarketData) → OnDataSeriesReady when an added series finishes backfill → OnDispose once. Live hooks never fire during backfill.

Properties

Member Description
string ContentKey { get; } Stable identity across runs, derived from (Type, ParameterValues, InputSeriesSpecs). Two requests with the same key resolve to the same instance (diamond-dependency dedup).
CalculationMode Calculate { get; } Cadence the runtime fires OnBarUpdate on the primary series. Must be stable for the indicator's lifetime. Default OnBarClose.
bool ProcessesMarketData { get; } Opt into raw OnMarketData tick delivery. Default false — non-opted indicators pay zero per-tick dispatch cost. Independent of Calculate.
bool ProcessesMarketDepth { get; } Opt into OnMarketDepth + OnMarketByOrder callbacks. Default-interface member, default false.
IReadOnlyList<IPlot> Plots { get; } Renderable outputs — one IPlot per line (SMA = 1, MACD = 3). Stable list reference; allocate plots in the ctor/OnInit and never resize. Plot properties are mutable at runtime.
IReadOnlyList<IIndicatorLine> Lines { get; } Horizontal threshold lines (RSI 30/70, ADX 20/40). Default-interface member returns empty.
bool ShowPriceMarkers { get; } Master toggle overriding every plot's ShowPriceMarker. Default true (per-plot setting wins).
int Displacement { get; } Purely visual horizontal shift of plots in bars (positive = right/future). The computed value series is never altered. Default 0.
ImmutableArray<IIndicator> Children { get; } Live snapshot of attached child indicators in registration order. Lock-free (atomic swap).

Lifecycle & child-tree methods

Signature Description
void OnInit(IIndicatorContext ctx) Once after construction. Declare secondary series, instantiate known children, read parameters. ctx.Bars(0) is available.
void OnDataLoaded(IIndicatorContext ctx) Once after all historical bars are loaded — compute historical state in a single (typically vectorised) pass.
void OnBarUpdate(IIndicatorContext ctx) A live bar closed/updated on one of the indicator's series; ctx.BarsInProgress identifies which.
void OnMarketData(in Tick tick, IIndicatorContext ctx) Per live tick, only when ProcessesMarketData is true.
void OnMarketDepth(in DepthUpdate update, IIndicatorContext ctx) Per aggregated L2 depth event (ProcessesMarketDepth). The book is applied before this fires. Default no-op.
void OnMarketByOrder(in MboEvent evt, IIndicatorContext ctx) Per Market-By-Order event (ProcessesMarketDepth). Default no-op.
void OnDataSeriesReady(int seriesIndex, IIndicatorContext ctx) An AddDataSeries series finished backfilling; ctx.Bars(seriesIndex) now returns real bars.
void OnDispose() Once on teardown. Release non-managed resources; children are disposed automatically.
void AttachChild(IIndicator child) Called by the child's own ctor — not user code. Registers the child; thread-safe.
void DetachChild(IIndicator child) Detach without disposing (call child.Dispose() instead, which routes here). No-op if not attached.
IStrategypublic interface IStrategy : IIndicator extends the lifecycle with order/position hooks (declared so consumers can type-check "indicator vs strategy"; the execution callbacks land in a later phase). See the Strategies reference.

IIndicatorContext INTERFACE

The runtime view an indicator has of its environment, passed to every lifecycle hook. Single-threaded with respect to the indicator. Do not cache it across threads or persist it past a callback. Many members are default-interface methods so test stubs and foreign hosts compile unchanged.

Series & per-callback metadata

Member Description
IBarSeries Bars(int seriesIndex) Bars for a series; 0 = primary. Added series live at the indices returned by AddDataSeries.
int SeriesCount { get; } Number of subscribed series (primary + added).
int CurrentBar { get; } Last-bar index on the firing series (Bars(BarsInProgress).Count - 1); -1 in warmup.
CurrentBarsAccessor CurrentBars { get; } Per-series last-bar indexer (ctx.CurrentBars[1]) for MTF indicators. Zero-allocation struct.
int BarsInProgress { get; } Which series this callback is firing for (0 = primary; always 0 on OnDataLoaded).
bool IsFirstTickOfBar { get; } True when the current tick opened a new bar on BarsInProgress.
bool IsBarClosed { get; } True for a closed-bar OnBarUpdate; false intra-bar. Default true.
long SequenceNumber { get; } Monotonic event sequence for deterministic logging/replay.
bool IsHistorical { get; } True during backfill/catch-up (!IsLive). Gate alerts/broker calls on this.
bool IsLive { get; } True during realtime dispatch. Default false (conservative).

Resources, orderflow & instrument info

Member Description
Task<int> AddDataSeries(BarSpecification spec) Subscribe an additional series. Idempotent for an already-subscribed spec; OnDataSeriesReady fires when bars are available.
void RemoveDataSeries(int seriesIndex) Drop an added series. Index 0 throws InvalidOperationException.
IIndicatorContext CreateChildContext(IBarSeries childPrimary, string childIdentity) Build a per-child context (called by AttachChild). Default returns this.
IIndicatorResolver? Indicators { get; } Host catalog of instantiable indicator types (built-ins + plugins), or null. Tolerate null.
ISeries<OrderFlowBar> OrderFlow(int seriesIndex) Per-bar orderflow. Only indicators marked [RequiresOrderFlow] may call it (else throws).
ISeries<double> OpenInterest(int seriesIndex) Per-bar OI scalar series (NaN where unreported). Default empty series; read defensively.
IOrderBook? OrderBook { get; } Live shared depth book, or null. Applied before depth/MBO callbacks. Default null.
double? TickSize { get; } Minimum price increment, or null. Default null.
double? PointValue { get; } Currency value of one full point, or null. Default null.
TimeZoneInfo? DisplayTimeZone { get; } The chart's display time zone (bars are already normalized to it). Default null.

Trading hours, rollovers & diagnostics

Member Description
TradingHoursTemplate? TradingHours { get; } Session/holiday calendar for the instrument, or null (treat as 24/7).
ISessionIterator? CreateSessionIterator(TradingHoursTemplate? template = null) Fresh stateful session iterator (NT's new SessionIterator(Bars)). Default null on stubs.
bool IsFirstBarOfSession { get; } True when the current OnBarUpdate bar opens a new session. Default false.
IReadOnlyList<DateTime> GetContractRolloversUtc(DateTime fromUtc, DateTime toUtc) Futures rollover instants in the bar timeline. Default empty.
void Print(string message) Dev trace to the host log window (NT's Print()).
void LogWarning(string message) Non-fatal anomaly; indicator keeps running.
void LogError(string message, Exception? exception = null) Fatal-for-this-callback condition; runtime keeps dispatching.
void Alert(string message) Raise a user-facing alert (live only). Message doubles as the rearm id.
void Alert(string id, string message, string? soundPath = null, AlertSeverity severity = Info, double rearmSeconds = 0) Full-control alert. Same id within rearmSeconds won't re-fire. Default no-op.

IndicatorBase BASE CLASS

The class plugin authors derive from. It implements IIndicator, IOrderFlowResolutionAware and IInputSourceAware, and provides empty virtual lifecycle hooks (override only what you need), lazy ContentKey, idempotent Dispose, the parent-owned child tree, and the plot/line registration helpers.

Sma.csusing TradeStrike.Pipeline.Indicators;
using TradeStrike.Pipeline.Plots;
using TradeStrike.Pipeline.Series;

[IndicatorDescription("Simple moving average of the input series.")]
[IndicatorCategory(IndicatorCategory.Trend)]
[IndicatorInput(IndicatorInputKind.Scalar)]
[IndicatorPlot("SMA", IsOverlay = true, Kind = PlotKind.Overlay, Unit = PlotValueUnit.Price)]
public sealed class Sma : IndicatorBase
{
    private readonly Plot _sma = new();

    [IndicatorParameter(DisplayName = "Period", MinValue = 1, MaxValue = 500, Step = 1)]
    public int Period { get; set; } = 14;

    public Sma(IIndicator? parent = null, ISeries<double>? input = null)
        : base(parent, input)
    {
        AddPlot(_sma);
    }

    public override void OnBarUpdate(IIndicatorContext ctx)
    {
        int n = Math.Min(Period, ctx.CurrentBar + 1);
        double sum = 0;
        for (int i = 0; i < n; i++) sum += Input[i];
        _sma.Values[0] = sum / n;
    }
}

Construction

Every subclass exposes one ctor of the shape Xxx(IIndicator? parent = null, ISeries<double>? input = null, /* params */) : base(parent, input). The base ctor stores Input/Bars and, when parent is non-null, registers as a child. The child's own OnInit/OnDataLoaded are deferred to the first dispatch event so the derived ctor body sets Period, plots, etc. before lifecycle math runs.

protected IndicatorBase(IIndicator? parent = null, ISeries<double>? input = null, IBarSeries? bars = null);

Public & protected members

Member Description
ISeries<double> Input { get; set; } Scalar source for SMA/EMA/RSI-style indicators; null defaults to primary Bars(0).Close at init.
IBarSeries Bars { get; set; } Bar source for OHLCV indicators (ATR/ADX/VWAP); null defaults to primary Bars(0).
InputSourceDescriptor InputSource { get; set; } The universal "Input series" parameter backing store (surfaces in the editor only for Scalar input kind).
OrderFlowResolution OrderFlowResolution { get; set; } The universal orderflow "Resolution" backing store (surfaces only for [RequiresOrderFlow] indicators). Default Auto.
bool ShowPriceMarkers { get; set; } Master price-marker toggle (attributed parameter, group "Display").
int Displacement { get; set; } Visual bar shift (attributed parameter, ContentKeyDefault = 0).
IReadOnlyList<IPlot> Plots { get; } / Lines { get; } The registered plots and threshold lines.
IIndicatorContext Context { get; } Protected. The most recent context the runtime supplied. Do not cache across threads.
const int MaxChildDepth = 16 Maximum parent→child chain depth (cycle + depth guarded by AttachChild).
protected void AddPlot(IPlot plot) Register a renderable output (append-only; earlier entries render behind later ones).
protected void AddLine(IIndicatorLine line) Register a horizontal threshold line.
protected void AddLine(string name, double value, ChartColor color, double lineWidthPx = 1.0, PlotStyle style = Line, bool participatesInAutoScale = true) Convenience overload constructing the default IndicatorLine.
protected void AdvanceChildren(IIndicatorContext ctx) Advance attached children for the current live bar (call at the top of OnBarUpdate when you read a child's Output).
protected virtual void OnReset(IIndicatorContext ctx) Reset hook for recalc: clears clearable plot series + disposes children. Override to drop custom accumulators (call base first).

Overridable virtuals

Signature Description
virtual void OnInit(IIndicatorContext ctx) Base seeds Context, resolves InputSource, and defaults Input/Bars. Call base.OnInit(ctx) first.
virtual void OnDataLoaded(IIndicatorContext ctx) No-op by default. Vectorised backfill pass.
virtual void OnBarUpdate(IIndicatorContext ctx) No-op by default. BIP filtering is handled by the framework wrapper.
virtual void OnMarketData(in Tick, …) / OnMarketDepth / OnMarketByOrder / OnDataSeriesReady / OnDispose All no-op by default; override what you need.
virtual string ContentKey { get; } Lazily computed via IndicatorMetadata.For(GetType()).ComputeContentKey(this).
virtual CalculationMode Calculate { get; protected set; } Default OnBarClose. Set in your ctor to opt into per-tick updates.
virtual bool ProcessesMarketData { get; protected set; } / ProcessesMarketDepth Default false; set in your ctor to receive tick / depth callbacks.

CalculationMode ENUM

Frequency at which the runtime dispatches OnBarUpdate on the primary series. Mirrors NinjaTrader's three modes. Controls only the bar-callback cadence — raw-tick delivery is the separate ProcessesMarketData opt-in.

Value Meaning
OnBarClose = 0 Fire once per closed bar (default). Lowest CPU — the right choice for EMA/SMA/RSI.
OnPriceChange = 1 Fire whenever the bar's Close changes; skips repeat-price quotes.
OnEachTick = 2 Fire on every tick. Highest CPU — for tick-precision VWAP, footprint, volume-reactive indicators.

CalculationCadence STATIC HELPER

The single source of truth for "given a CalculationMode and the current update, should the callback fire?" — shared by the indicator runtime and the strategy runner.

public static bool ShouldFire(CalculationMode mode, bool isClosed, bool closeChanged);
//  OnBarClose    => isClosed
//  OnPriceChange => isClosed || closeChanged
//  OnEachTick    => true

Attributes

Class- and property-level attributes the framework reads by reflection to compute content keys, render the parameter editor, persist values, and route data. All are Inherited = true.

IndicatorParameter (property)

Marks a property as a user-tunable parameter. Constraints are advisory (the editor enforces them; programmatic sets are not clamped).

Member Description
string? DisplayName Editor label (defaults to the property name, humanised).
string? Description Tooltip text.
string? Group Logical group; the editor renders a group together.
int Order Display order within the group (lower first; ties break by declaration order).
object? MinValue, MaxValue, Step Advisory numeric bounds + spinner increment.
object? ContentKeyDefault Omit-when-default sentinel: the parameter contributes no content-key term while equal to this value (keeps pre-existing keys byte-identical when adding a new parameter).
ParameterEditorKind Editor Editor control hint. Default Auto.
string? FileFilter Win32 dialog filter for FilePicker.

ParameterEditorKind enum: Auto = 0 (host picks from the CLR type), FilePicker = 1 (path field + Browse), SymbolPicker = 2 (shared instrument picker for a benchmark/comparison symbol).

IndicatorPlot (class, repeatable)

Declares a named plot the indicator produces — one ISeries<T> per plot. An indicator with zero declared plots is refused (no observable output).

Member Description
string Name (ctor) The plot name.
string? Description Optional description.
bool IsOverlay Overlay on the price panel vs a separate panel.
PlotKind Kind Shape (defaults to Overlay when IsOverlay, else Unspecified).
double RangeMin, RangeMax Inclusive value bounds; double.NaN = unbounded (mapped to null).
PlotValueUnit Unit Value unit.
PlotBullishDirection BullishDirection Whether rising = bullish/bearish/neither.
string? Reference Notable levels, e.g. "30/70 overbought/oversold".

Class-level marker & metadata attributes

Attribute What it declares
[IndicatorDescription(string)] Human-readable description (required on every built-in; non-empty or it throws).
[IndicatorCategory(IndicatorCategory)] The indicator's family (see enum below).
[IndicatorInput(IndicatorInputKind)] How the indicator consumes data: Scalar (surfaces the Input-series picker), Bars, or None.
[RequiresOrderFlow] Opt into pre-aggregated orderflow delivery + the synthesized "Resolution" parameter.
[RequiresOpenInterest] Opt into the per-bar Open-Interest channel (routing hint only; the channel is always reachable, all-NaN without an OI provider).
[SingleInstancePerChart] At most one instance of this concrete type per chart; duplicate adds are silently ignored.
[ParameterContainer(Type)] Delegate parameters to a separate settings POCO (set AllProperties = true to surface every persistable property). The indicator must also implement IParameterContainerProvider.

Enums: IndicatorCategory = Unspecified, Trend, Momentum, Volume, Volatility, OrderFlow, Structure, Custom. IndicatorInputKind = Scalar = 0, Bars = 1, None = 2.

Metadata & identity

Reflection-extracted, per-type cached data driving content keys, the parameter editor, and discovery.

Member Description
IndicatorMetadata.For(Type) / For<T>() Cached metadata for a type. Synthesizes the "Input series" and orderflow "Resolution" parameters when applicable.
string ComputeContentKey(object instance) Deterministic, locale-invariant content key from name-sorted parameter values (honours ContentKeyDefault).
static object ResolveParameterTarget(object indicator, ParameterDescriptor) The object a descriptor's get/set targets (the indicator itself, or its [ParameterContainer] object).
IReadOnlyList<ParameterDescriptor> Parameters / IReadOnlyList<PlotDescriptor> Plots Declared parameters and plots.
IndicatorType, Description, Category, InputKind Type + descriptive metadata.
bool RequiresOrderFlow, RequiresOpenInterest, SingleInstancePerChart, IsInert Structural flags. IsInert = withheld premium form (empty editor, still constructs/removes).

Records: ParameterDescriptor(PropertyInfo Property, string Name, string DisplayName, string Description, string Group, int Order, object? MinValue, object? MaxValue, object? Step, ParameterEditorKind Editor, string FileFilter) with init-only ContentKeyDefault; PlotDescriptor(string Name, string Description, bool IsOverlay) with init-only Semantics.

IParameterContainerProviderobject ParameterContainer { get; }: the live settings instance for a [ParameterContainer] indicator.

PlotSemantics

The machine-readable meaning of a plot's value, so a consumer can interpret RSI = 71.6 vs EMA = 5012 without recognising the indicator. PlotSemantics.Default = everything unspecified.

public sealed record PlotSemantics(
    PlotKind Kind, double? RangeMin, double? RangeMax,
    PlotValueUnit Unit, PlotBullishDirection BullishDirection, string? Reference);
// bool HasRange => RangeMin.HasValue && RangeMax.HasValue;
Enum Values
PlotKind Unspecified, Overlay, Oscillator, Histogram, Signal, Band
PlotValueUnit Unspecified, Price, Percent, Ratio, Ticks, Raw
PlotBullishDirection None, Up, Down

Input sources

IInputSourceAware / IOrderFlowResolutionAware — backing-store contracts (InputSourceDescriptor InputSource { get; set; } and OrderFlowResolution OrderFlowResolution { get; set; }) that IndicatorBase implements so the editor/codec descriptors are synthesized only for the right indicators.

InputPriceType enum (mirrors NT's price types plus channels): Close = 0, Open, High, Low, Median, Typical, Weighted, OHLC4, Volume, OpenInterest = 9.

InputSourceDescriptor — a serializable value type describing where the scalar Input comes from: a price type, or another indicator's plot (a private configured copy). Its canonical string is a frozen, persisted grammar; structural equality is semantic equality.

Member Description
static InputSourceDescriptor Close { get; } The default (equals default(InputSourceDescriptor)).
static InputSourceDescriptor Price(InputPriceType type) A price-source descriptor.
static InputSourceDescriptor Indicator(string catalogKey, IReadOnlyDictionary<string,string>? parameters = null, string? plotName = null) An indicator-plot source (parameters canonicalized).
InputPriceType PriceType · string? IndicatorKey, IndicatorParametersText, PlotName The descriptor's components.
bool IsPrice, IsDefault Classification helpers.
IReadOnlyDictionary<string,string> GetParameterMap() The source indicator's overrides as a name→value map.
string ToString() / ToDisplayString() Canonical (persisted) form / human-readable form.
static InputSourceDescriptor Parse(string) / bool TryParse(string?, out …) Parse from string (throwing / non-throwing).

Optional capability interfaces

An indicator opts into extra host integration by ALSO implementing one of these alongside IIndicator. The core indicator surface stays minimal — only the indicators that need a capability take the dependency.

Interface Opts into
IIndicatorPanelHint Default panel placement via PanelPlacement DefaultPanel { get; } (PriceOverlay or NewSubpanel). Non-implementers default to PriceOverlay.
IChartCustomRender A custom paint pass on the indicator's own panel: void OnCustomRender(IIndicatorRenderContext), plus CustomRenderLayer Layer and bool SuppressDefaultBarRendering.
IChartBackgroundRender An extra below-bars paint pass: void OnRenderBackground(IIndicatorRenderContext) — for dual-layer studies (volume profile backdrop + foreground).
IPricePanelOverlayRender Painting onto the main price panel even from a sub-panel: void OnRenderPricePanel(IIndicatorRenderContext) + CustomRenderLayer PricePanelLayer (NT's DrawOnPricePanel).
IBarColorSource Per-bar candle recolouring (NT paint-bars): bool PaintBarsEnabled { get; } and bool TryGetBarColor(int barIndex, in Bar bar, out ChartColor color). Last enabled source wins.
IChartClickReceiver React to a left-click in the plot area: bool OnChartClick(ChartClickContext ctx) (return true to consume). Pairs with a custom render that drew the control.
IChartMouseMoveReceiver Hover-driven decorations: void OnChartMouseMove(ChartMouseMoveContext ctx) (a NaN position = mouse left the chart).
IRepaintNotifier Request a frame off-cadence (timer/async): event Action? RepaintRequested + void RequestRepaint(). The chart coalesces N requests to one paint.
IRecalculateNotifier Ask the host to re-run history (not just repaint) when math-affecting state changed: event Action? RecalculateRequested.
IIndicatorToolbarMenu Contribute a chart-toolbar dropdown: string MenuTitle, IReadOnlyList<ToolbarMenuItem> BuildMenu(), event Action? MenuChanged.
IIndicatorContextMenu Contribute right-click menu items at a click location: IReadOnlyList<ToolbarMenuItem> BuildContextMenu(IndicatorContextMenuLocation) + event Action? ContextMenuChanged.

Context structs: ChartClickContext(double X, double Y); ChartMouseMoveContext(double X, double Y) with IsInside + Outside; IndicatorContextMenuLocation(int BarIndex, double Price, DateTime Time, double PixelX, double PixelY). CustomRenderLayer enum = BelowBars, AboveBars (default). PanelPlacement enum = PriceOverlay, NewSubpanel.

IIndicatorRenderContext & menu items

The per-frame context passed to the render capability methods. Single-threaded (WPF UI thread); do not cache it across frames.

Member Description
IChartRenderer Renderer { get; } Active renderer (DrawLine/DrawText/FillRect/…).
ChartViewport Viewport { get; } Bar↔pixel + price↔pixel transforms for the current paint.
IBarSeries? Bars { get; } Primary bar series being rendered (null on an empty tab).
DateTime Now { get; } Wall-clock at frame start, in the bar timeline (for BarTimer-style countdowns; pinnable in tests).
PlotAreaRect PlotArea { get; } Pixel rect of the candle pane (excludes axis strips). PlotAreaRect(X, Y, Width, Height) with Right/Bottom.
double HeaderInsetPx { get; } / double ContentTopPx { get; } Top chrome reserved by the legend; safe starting Y for top-anchored content. Default 0.
TimeZoneInfo? DisplayTimeZone { get; } The chart's display zone (for session/news shading). Default null.
IReadOnlyList<IDrawingTool> Drawings { get; } User-placed drawing tools (read-only). Default empty.
PerfScope BeginScope(string category, string? detail = null) Nested perf sub-scope for the Task Manager. Default no-op.

ToolbarMenuItem hierarchy

A closed record hierarchy (the host pattern-matches the concrete types) reused by both IIndicatorToolbarMenu and IIndicatorContextMenu. Built fresh per open — do not cache.

Record Use
ToolbarMenuItem(string Label) Abstract base.
ToolbarCommand(Label, Action Invoke, bool IsEnabled = true) Click-to-invoke action.
ToolbarCheckItem(Label, Func<bool> IsChecked, Action<bool> Toggle, bool IsEnabled = true) Two-state toggle (re-queried each open).
ToolbarSubmenu(Label, IReadOnlyList<ToolbarMenuItem> Items, Func<bool>? IsChecked = null) Nested submenu.
ToolbarSeparator() Visual divider.
ToolbarNumberInput(Label, Func<double> GetValue, Action<double> SetValue, double Min, double Max, int Decimals = 0, bool IsEnabled = true) Inline numeric field (clamped, rounded).
ToolbarSlider(…same shape as ToolbarNumberInput…) Inline draggable slider.
ToolbarTextInput(Label, Func<string> GetText, Action<string> SetText, bool IsEnabled = true) Inline free-text field (raw string pushed; indicator validates).
ToolbarDescription(string Text) Greyed-out help line.
ToolbarRadioGroup(Label, IReadOnlyList<ToolbarRadioOption> Options) Mutually-exclusive option group. ToolbarRadioOption(Label, Func<bool> IsSelected, Action Select, bool IsEnabled = true).
ToolbarColorPicker(Label, Func<ChartColor> GetColor, Action<ChartColor> SetColor, bool IsEnabled = true) Reversible colour-picker row.

Helpers

Type Description
CurrentBarsAccessor Zero-allocation struct returned by ctx.CurrentBars; this[int seriesIndex] = last-bar index on that local series (-1 if empty).
IntraBarRefreshGate Throttle for expensive intra-bar refresh work. ShouldRefresh(bool force = false); const int DefaultIntervalMs = 250. Single-threaded; injectable clock.
BarTimeProjection (static) Time-axis math for custom rendering: FractionalBarIndex(DateTime, IReadOnlyList<DateTime> barStarts, TimeSpan barDuration), EstimateBarDuration(barStarts, int sample = 64), and the zero-copy BarStartList(IBarSeries) view.

Factory, resolver & logger

Type Members
IIndicatorFactory String-key creation for declarative consumers: bool Knows(string typeKey), IIndicator Create(string typeKey, IReadOnlyDictionary<string,string>? parameters = null).
IIndicatorResolver Type-keyed catalog (built-ins + plugins): IReadOnlyList<Type> AvailableTypes, IIndicator? Create(Type, IReadOnlyDictionary<string,object>? overrides = null) (returns null when unknown). Reachable via ctx.Indicators or IndicatorResolverAmbient.Resolver.
IndicatorResolverAmbient (static) Process-wide resolver install: Resolver, SetResolver(…), scoped Use(…). Default null = engine built-ins only.
IIndicatorLogger Diagnostic sink (the target of Print/LogWarning/LogError): Print(identity, message), Warning(…), Error(identity, message, Exception? = null). Thread-safe. NullIndicatorLogger.Instance is the no-op.
Premium definition seam. IIndicatorDefinitionSource + IndicatorDefinitionAmbient let the host gate premium indicators' editor metadata on cloud-served specs (verdicts Reflection / Spec / Withheld via DefinitionVerdict + DefinitionResult). A process that installs no source sees pure local reflection. Plugin authors normally never touch this — content keys stay byte-identical across verdicts.

Alerts

IndicatorAlert — the payload raised by ctx.Alert(…):

public readonly record struct IndicatorAlert(
    string Id, string Message, DateTime TimeUtc,
    AlertSeverity Severity = AlertSeverity.Info, string? SoundPath = null,
    double RearmSeconds = 0, string? InstrumentId = null);

IIndicatorAlertSink — host seam delivering alerts to the user: void Dispatch(in IndicatorAlert alert). Thread-safe; return promptly.