Market Structure Toolkit
All plugins

Plugin libraryMarket structure

Plugin

Market Structure Toolkit

An example that bundles the nineteen catalogue elements of the market structure group (BoS, CHoCH, FVG, order blocks, Fibonacci, OTE, liquidity, kill zones) in one plugin indicator.

IndicatorNo networkDesktop onlyv1.0.0

About

This template is an example and adds nothing of its own. The market structure elements are part of the engine's indicator catalogue: structure (BoS, CHoCH), fvg, ifvg, order_block, breaker_block, fib_retracement, fib_extension, premium_discount, ote, liquidity_sweep, equal_levels, liquidity, bpr, mitigation_block, rejection_block, displacement, killzones, midnight_open and opening_gaps. A strategy reads them one by one with ctx.ind("fvg", ...) or all nineteen at once with ctx.market_structure(...), Blocks offers them in the group Market structure, and the chart draws them when you add them as indicators. Their definitions, warm up and limits are on the indicator pages. That is the real implementation, and the one to use in a strategy.

What the template shows is how a plugin bundles catalogue elements in a plugin indicator. It sorts the parameters to the elements that take them with split from orionquant_engine.indicators.market (the function ctx.market_structure uses), builds each stream with get(id).make(**inputs), passes every bar to each stream in update(bar), and hands on each element's outputs as a read only reading, the way ctx.ind does. The parameters, their defaults and their bounds are read from the catalogue as well. The template finds no swing, measures no gap and computes nothing from prices: every value is the catalogue stream's own, so it is exactly what this reads at the same bar with the same inputs:

pythonms = ctx.market_structure(length=3)
if ms.trend == 1 and ms.premium_discount.zone == -1:
    ctx.signal("LONG")

Start here when a plugin of your own needs a catalogue indicator inside it, for example a filter of your own on top of the order blocks: build the stream the same way and keep your logic next to it.

Parameters

NameTypeDefaultRange or optionsDescription
lengthint3min 1, max 50Swing bars on each side, for every element built on swings: structure, the order, breaker, mitigation and rejection blocks, both Fibonacci elements, premium_discount, ote, liquidity, liquidity_sweep and equal_levels. A swing is confirmed this many bars after its extreme.
break_onchoice"close"close, wickWhat breaks a swing in structure and the order, breaker and mitigation blocks: a close beyond it, or with wick a high or low beyond it.
atr_lengthint14min 1, max 500Bars in Wilder's ATR that sizes the gaps of fvg, ifvg, bpr and displacement, the body of a displacement and the tolerance of equal_levels.
min_sizefloat0.25min 0, max 10The smallest gap that counts in fvg, ifvg, bpr and displacement, in multiples of the ATR at its third candle. 0 counts every gap.
fillchoice"full"full, halfWhen a fair value gap counts as filled: full when price has passed through the whole gap, half at its middle (consequent encroachment).
max_zonesint20min 1, max 500Zones or levels kept per side in every element that keeps them (the gaps, blocks, ranges and liquidity levels); past it the oldest goes without an event.
zonechoice"wick"wick, bodyWhat an order, breaker or mitigation block spans: wick for the full candle, body for its body only.
tolerancefloat0.1min 0, max 2How far apart two swing highs (or lows) may be and still count as equal in equal_levels, in multiples of the ATR.
min_wickfloat0.5min 0, max 1The smallest wick a rejection block counts, as a share of its candle's range.
min_bodyfloat1min 0, max 10The body a displacement candle needs: more than this many ATRs.
dayschoice"mon_fri"mon_fri, every_dayWhich days killzones and midnight_open count: mon_fri for Monday to Friday, every_day for markets that trade at the weekend.

Use it in a strategy

pythontk = ctx.plugin_indicator("market_structure_toolkit", "toolkit", {"length": 3})
if tk is not None and tk.value is not None:
    # Trend up and the close inside the OTE zone of an up leg: long.
    if tk.value == 1 and tk.ote.leg == 1 and tk.ote.inside == 1:
        ctx.signal("LONG")
    # A change of character down: out.
    elif tk.structure.choch == -1:
        ctx.signal("EXIT")

The same strategy without the plugin reads ms = ctx.market_structure(length=3) and then ms.trend, ms.ote.leg, ms.ote.inside and ms.choch, with the same values at every bar. That form also runs in Blocks, Rust and C# and reaches the exports; the plugin form runs in Python strategies only.

What it provides

valueValueThe trend of the structure element: 1 up, -1 down, 0 before the first break. None while it warms up.
structureValueThe reading of `structure`: trend, bos, choch, swing_high, swing_low, bos_level and choch_level.
fvgValueThe reading of `fvg`: bull_top, bull_bottom, bear_top, bear_bottom of the nearest open gaps, bull_new, bear_new, bull_filled and bear_filled.
ifvgValueThe reading of `ifvg`: the same zone lines as fvg, with bull_broken and bear_broken.
order_blockValueThe reading of `order_block`: the zone lines of the nearest blocks, the new flags, bull_broken and bear_broken.
breaker_blockValueThe reading of `breaker_block`: the same lines as order_block, for broken blocks kept as zones of the other side.
fibValueThe reading of `fib_retracement`: level_0, level_236, level_382, level_500, level_618, level_786, level_1000 and leg.
fib_extensionValueThe reading of `fib_extension`: level_1272, level_1618, level_2000, level_2618 and leg.
premium_discountValueThe reading of `premium_discount`: equilibrium, range_high, range_low and zone (1 premium, -1 discount).
oteValueThe reading of `ote`: top, bottom, inside and leg of the 0.62 to 0.79 retracement zone.
liquidity_sweepValueThe reading of `liquidity_sweep`: up and down on the bar of a sweep, up_level and down_level the last swept prices.
equal_levelsValueThe reading of `equal_levels`: eqh and eql, the nearest pools of equal highs and lows, with their new and taken flags.
liquidityValueThe reading of `liquidity`: buy_side and sell_side, the nearest untaken swing high and low, with buy_taken and sell_taken.
bprValueThe reading of `bpr`: the zone lines of the nearest balanced price ranges, the new flags, bull_broken and bear_broken.
mitigation_blockValueThe reading of `mitigation_block`: the same lines as order_block, for the swing candle a failure swing broke.
rejection_blockValueThe reading of `rejection_block`: the same lines as order_block, for the long wicks at swing highs and lows.
displacementValueThe reading of `displacement`: up, down, and high, low and body_atr of the last displacement candle.
killzonesValueThe reading of `killzones`: asia, london_open, new_york_open and london_close, each with its high and low.
midnight_openValueThe reading of `midnight_open`: open, position (1 above, -1 below) and first_bar.
opening_gapsValueThe reading of `opening_gaps`: top, bottom and filled of the new week gap (nwog) and the new day gap (ndog).

When values are known

Every value is known when the catalogue element knows it, at the close of the bar, exactly as ctx.ind gives it: a swing length bars after its extreme, a gap on its third candle. Nothing reported changes with later bars. Each reading is a snapshot: keeping one past its bar keeps the values it had. Warm up is each element's own; an output is None until its element has something to show.

Limits

  • An example only: it adds nothing to the catalogue elements, and in a strategy ctx.market_structure or ctx.ind is shorter and does the same.
  • One set of parameters for all nineteen elements; for a different length per element, read each one with ctx.ind.
  • The definitions belong to the catalogue: a change there reaches this plugin without a new version of it.
  • A plugin indicator is not drawn on the chart and is not available in Blocks, Pine, Rust or C#; it runs on the run's own instrument only. The catalogue elements themselves are.
OrionQuant