Orderflow

Indicators

Orderflow ADVANCED

Per-price-level bid/ask volume per bar — the data behind footprint, delta and POC tools. Opt in once, and the framework pre-aggregates it for you.

Opting in

Footprint-class indicators need per-level, per-side traded volume. Computing that from raw ticks is expensive, so the framework only does it for indicators that ask. Tag the indicator with [RequiresOrderFlow] and read the per-bar data through ctx.OrderFlow(seriesIndex), an ISeries<OrderFlowBar> aligned 1:1 with the bars.

Calling OrderFlow without the attribute throws. The runtime rejects an un-opted-in indicator with an InvalidOperationException — a deliberate guard that catches the "I forgot the attribute" bug rather than silently returning empty data.

The data is delivered for history and live: a pre-aggregated OrderFlowBar reaches you through OnDataLoaded and OnBarUpdate, never as raw historical ticks. If you also want the raw live trade stream, opt into ProcessesMarketData separately — the two compose.

PocMarker.csusing TradeStrike.Pipeline.Indicators;
using TradeStrike.Pipeline.OrderFlow;
using TradeStrike.Pipeline.Plots;
using TradeStrike.Pipeline.Series;

[IndicatorDescription("Marks each bar's volume Point of Control — the price level with the most traded volume.")]
[IndicatorCategory(IndicatorCategory.OrderFlow)]
[IndicatorInput(IndicatorInputKind.None)]
[RequiresOrderFlow]                                  // <-- gates ctx.OrderFlow(...)
public sealed class PocMarker : IndicatorBase
{
    private readonly ChunkedSeries<double> _poc = new();

    public PocMarker(IIndicator? parent = null) : base(parent)
    {
        AddPlot(new Plot("POC", _poc, PlotStyle.Dots, new ChartColor(255, 152, 0), 4.0));
    }

    public override void OnDataLoaded(IIndicatorContext ctx)
    {
        var of = ctx.OrderFlow(0);
        for (int i = of.Count - 1; i >= 0; i--)
            _poc.Append(PocOf(of[i]));
    }

    public override void OnBarUpdate(IIndicatorContext ctx)
    {
        _poc.Append(PocOf(ctx.OrderFlow(0)[0]));
    }

    private static double PocOf(in OrderFlowBar bar)
        => bar.TryGetPoc(out double price, out _) ? price : double.NaN;
}

What an OrderFlowBar gives you

An OrderFlowBar is a value type wrapping the underlying Bar plus its per-price-level volume, designed for allocation-free reads in tight render loops. The key surface:

Member Meaning
Bar The OHLCV bar this orderflow snapshot corresponds to.
LevelCount / GetLevel(i) Number of price levels and the level at index i (index 0 = lowest price).
TryGetLevelAtPrice(price, out level) Look up the level at a specific price (true when it lines up on a tick).
TryGetPoc(out price, out volume) Point of Control — the highest-total-volume level (ties resolve to the lower price).
Delta / TotalBidVolume / TotalAskVolume / TotalTradeCount Bar-level aggregates, all O(1) (precomputed).
LocalDeltaHigh / LocalDeltaLow Intra-bar running-delta extremes (0-floored) — power cumulative-delta candle wicks.
TickSize / LowTicks The price grid the levels are indexed on.

Each OrderFlowLevel carries Price, BidVolume, AskVolume, TradeCount, plus computed TotalVolume and Delta (= Ask − Bid). When the producer tracked a per-side print split, HasTradeCountSplit is true and you can read GetBidTradeCount(i) / GetAskTradeCount(i).

Per-bar statistics

Some quantities can only be computed during tick aggregation (they depend on tick order). When available, OrderFlowBar.HasStats is true and Stats (an OrderFlowBarStats) exposes RunningDeltaMax/Min, VolumePerSecond, and commitment-of-traders helpers CotHigh / CotLow. Bars built without a tick aggregator (empty bars, MTF seeding) carry Stats == null, so read defensively.

Backfill resolution

OrderFlowResolution controls how much history is fetched: Tick (finest, exact aggressor and delta), Second, Minute (coarsest, fastest, with estimated bid/ask), and Auto (the default — derived from the chart's bar period). Surface it as a user choice by implementing IOrderFlowResolutionAware; every IndicatorBase already provides the backing OrderFlowResolution property, and the settings dialog synthesizes a "Resolution" dropdown only for indicators marked [RequiresOrderFlow] — so the dropdown never appears on an ordinary SMA.

What is lost at coarse resolution. Native Second/Minute bars carry no per-trade aggressor side, so bid/ask (and therefore delta) is estimated. Volume-at-price stays accurate; delta-sensitive tools (footprint, delta) see an approximation. The current forming bar is always built from live ticks and is exact regardless of the setting.

Open Interest

For the venue-reported Open Interest channel, tag the indicator with [RequiresOpenInterest] and read ctx.OpenInterest(seriesIndex) — a scalar series aligned 1:1 with the bars, with double.NaN on bars that report no OI. Unlike orderflow, this is a default member: contexts without an OI channel return a safe empty series, so read defensively (TryGet → NaN). The attribute only steers backfill routing; the channel is always reachable.

Orderflow gives you the data; turning it into footprint cells, profile columns or POC lines is a rendering job — see Custom rendering.