Library · Pine Script v6 · Library

SMC Context Engine Private

by skipp-dev· code credited to preuss_steffen

This library builds real-time Smart Money Concepts context frames on each bar.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code833
Settings0inputs
LicenceNone statedfile: MPL-2.0
Last update2 days ago26 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new library, select all of the template text and paste. Press Save and give it a name.
  4. 4Publish or import itA library draws nothing. Publish it from the editor, then import its functions in your own scripts.

Source Code

833 lines of Pine Script v6 from SMC++/smc_context_engine_private.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

smc_context_engine_private.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © preuss_steffen

//@version=6
// @description SMC live context library (bus-v3 track). Derives per-bar domain
// frames from the engine primitives instead of the dead per-symbol snapshot:
// StructureFrame (BOS/CHoCH/trend/S-R via eng.detect_structure), ImbalanceFrame
// (FVG lifecycle + BPR + liquidity void via eng.fvgs_objects) and ZoneFrame
// (order-block lifecycle via eng.track_obs). Detection is the engine's SSOT in
// all three and is NOT golden-gated against Python. The imbalance rule layer
// (mitigation %, BPR overlap, void %, zone-bias count delta, state precedence)
// mirrors the golden parity contract in tests/fixtures/smc_context_golden.json;
// structure and zone are spec-only (Column B) — see docs/smc-context-semantics.md.
// Sweep/Pool scoring mirrors the Python golden while their live candidate
// detection is Pine-only and explicitly specified in docs/smc-context-semantics.md.
// Session classification uses independent IANA zones (Tokyo, London, New York),
// and build_context_frame() composes all six domains without running a second
// structure detector for the dependent ZoneFrame.
library("smc_context_engine_private", overlay = true)

// Pine requires every library referenced by a *used* type's fields to be imported
// (CE10293), and those libraries must come before the engine that pulls them into
// its types — so the engine is imported last, matching SMC_Long_Dip_Suite.
//   ct  — eng.FVG and eng.OrderBlock declare ct.LevelBreakMode fields.
//   d   — both declare d.SmcBox / d.SmcLine / d.SmcLabel fields.
//   pe  — eng.OrderBlock declares a pe.Profile field. NOTHING here calls pe.*:
//         build_zone_frame leaves every profile feature off, so the engine never
//         creates a Profile. Kept a narrow type edge by the allowlist in
//         tests/test_pine_library_import_permissions.py and enforced by
//         tests/test_pine_context_library_contract.py.
//   u   — smc_lib_atr for the OB size filter. A real call; public library.
import preuss_steffen/smc_core_types/5 as ct
import preuss_steffen/smc_utils/4 as u
import preuss_steffen/smc_draw/3 as d
import preuss_steffen/smc_profile_engine/4 as pe
import preuss_steffen/smc_engine_private/5 as eng

// ─────────────────────────────────────────────────────────────────────────────
// Rule-layer thresholds.
//
// These are REAL constants, not comments, and they carry the exact names used in
// tests/fixtures/smc_context_golden.json._meta.thresholds. That is deliberate:
// tests/test_pine_context_library_contract.py parses the declarations below and
// asserts each value equals the frozen golden. Previously these numbers were
// inlined at their use-sites with the names only in comments — so the Python
// reference could move, the golden would regenerate, every Python test would stay
// green, and this library would be silently wrong. A comment cannot be checked;
// a named const can.
// ─────────────────────────────────────────────────────────────────────────────

// --- golden-checked (names must match _meta.thresholds) ---
const float PARTIAL_MIT_PCT       = 0.5   // gap filled >= 50% -> partial mitigation
const float FULL_MIT_PCT          = 1.0   // gap fully closed  -> no longer active.
                                          // Handed to the engine as the FVG fill
                                          // target, which is what makes
                                          // eng.FVG.filled mean "fully mitigated".
const float LIQ_VOID_MIN_SIZE_PCT = 2.0   // gap >= 2% of mid  -> liquidity void
const float SWEEP_DEPTH_MIN_PCT = 0.1
const float SWEEP_DEPTH_STOP_HUNT_PCT = 0.3
const int   SWEEP_RECLAIM_MAX_BARS = 5
const float SWEEP_VOLUME_RATIO_MIN = 1.2
const float IMBALANCE_SIG_THRESHOLD = 0.3
const float PROXIMITY_NEAR_PCT = 1.0
const int   CLUSTER_STRONG_COUNT = 3

// --- Pine-only (Column B spec, no Python golden) ---
const int   ZONE_BIAS_COUNT_DELTA = 2     // |bull-bear count| >= 2 -> directional bias
                                          // (ImbalanceFrame.zone_bias over FVGs,
                                          //  ZoneFrame.ob_bias over order blocks)

// --- Zone-frame OB size filter — mirrors SMC_Long_Dip_Suite.pine lines 904-906 ---
const int   ATR_LEN_MAIN          = 50    // u.smc_lib_atr length. NOT 200 — that is
                                          // an unrelated engine-internal impulse ATR.
const float OB_FILTER_MIN_MULT    = 0.5   // min block size = atr * 0.5
const float OB_FILTER_MAX_MULT    = 2.5   // max block size = atr * 2.5
const int   SWEEP_TYPE_STOP_HUNT  = 1
const int   SWEEP_TYPE_GRAB       = 2
const int   SWEEP_TYPE_INDUCEMENT = 3
const int   SESSION_ASIA          = 1
const int   SESSION_LONDON        = 2
const int   SESSION_NY_AM         = 3
const int   SESSION_NY_PM         = 4
const int   CONTEXT_BIAS_MIN_VOTES = 2
const int   OPENING_RANGE_MINUTES = 30

// ── Frame types ──────────────────────────────────────────────────────────────

// Per-bar structure state. Prices are `na` (never 0) when unavailable; anchor
// bar indices are `na` when there is no pivot. `trend` persists between events.
//
// Confirmed-bar only: the frame is rebuilt when barstate.isconfirmed and held
// between ticks, so every field describes the last CLOSED bar. The engine's
// BOS/CHoCH alerts fire intrabar and can vanish before the close, which is why
// the Suite gates the same alerts through resolve_confirmed_signal.
export type StructureFrame
    int    trend             // +1 bullish, -1 bearish, 0 unknown (no confirmed event yet)
    bool   bos               // a BOS fired on this confirmed bar (either side)
    bool   choch             // a CHoCH fired on this confirmed bar (either side)
    int    dir_event         // +1 bullish event, -1 bearish event, 0 none (this bar)
    float  resistance        // active trailing swing high level
    float  support           // active trailing swing low level
    int    resistance_x      // bar_index anchor of the last swing-high pivot
    int    support_x         // bar_index anchor of the last swing-low pivot
    int    bars_since_event  // age: confirmed bars since the last BOS/CHoCH (na until first)
    bool   fresh             // event age within the freshness window

// Per-bar imbalance state (FVG lifecycle + BPR + liquidity void). Prices `na`
// when inactive; counts default 0. `state`: 0 NONE, 1 FVG_BULL, 2 FVG_BEAR,
// 3 BPR, 4 LIQ_VOID (precedence BPR > VOID > FVG_BULL > FVG_BEAR > NONE).
export type ImbalanceFrame
    bool   bull_fvg_active
    bool   bear_fvg_active
    float  bull_fvg_top
    float  bull_fvg_bottom
    float  bear_fvg_top
    float  bear_fvg_bottom
    float  bull_fvg_mit_pct  // engine fill_current_ratio of the newest active bull FVG
    float  bear_fvg_mit_pct
    bool   bull_fvg_partial  // mit_pct >= 0.5
    bool   bear_fvg_partial
    int    bull_fvg_count    // active bull FVGs
    int    bear_fvg_count
    bool   bpr_active
    int    bpr_dir           // +1 bull, -1 bear, 0 none
    float  bpr_top
    float  bpr_bottom
    bool   liq_void_active
    float  liq_void_top
    float  liq_void_bottom
    int    zone_bias         // +1 bull, -1 bear, 0 neutral (FVG count-delta rule)
    int    state             // composite code, see above

// Per-bar order-block state (eng.track_obs lifecycle). Direction-neutral: the v2
// engine bus carries only the product's best *bull* OB, this frame reports both
// sides. Prices/ids are `na` when that side has no active OB; counts default 0.
// `state`: 0 NONE, 1 OB_BULL, 2 OB_BEAR, 3 OB_BOTH (precedence BOTH > BULL >
// BEAR > NONE). `*_new` / `*_broken` are edges — true only on the event bar.
// `ob_bias` is the OB count delta and is NOT ImbalanceFrame.zone_bias (FVGs).
export type ZoneFrame
    bool   bull_ob_active
    bool   bear_ob_active
    int    bull_ob_id        // newest active bull OB (engine id)
    int    bear_ob_id
    float  bull_ob_top
    float  bull_ob_bottom
    float  bear_ob_top
    float  bear_ob_bottom
    int    bull_ob_count     // active bull OBs
    int    bear_ob_count
    bool   bull_ob_new       // an OB was confirmed on this confirmed bar
    bool   bear_ob_new
    bool   bull_ob_broken    // an OB broke on this confirmed bar
    bool   bear_ob_broken
    int    ob_bias           // +1 bull, -1 bear, 0 neutral (OB count-delta rule)
    int    state             // composite code, see above

// Recent confirmed liquidity sweep. Direction is +1 bullish (sell-side
// liquidity taken), -1 bearish (buy-side taken), 0 none/ambiguous. Sweep type:
// 0 NONE, 1 STOP_HUNT, 2 LIQUIDITY_GRAB, 3 INDUCEMENT.
export type SweepFrame
    bool   recent_bull_sweep
    bool   recent_bear_sweep
    int    sweep_type
    int    direction
    float  zone_top
    float  zone_bottom
    bool   reclaim_active
    int    liquidity_taken_direction // +1 buy-side, -1 sell-side, 0 none/both
    float  depth_pct
    float  volume_ratio
    int    quality_score
    int    bars_since_event
    bool   fresh

// Confirmed pivot-cluster liquidity pools. Buy-side levels sit above price,
// sell-side levels below it. Magnet direction: +1 UP, -1 DOWN, 0 NONE.
export type PoolFrame
    float  buy_side_level
    float  sell_side_level
    int    buy_side_strength
    int    sell_side_strength
    float  proximity_pct
    int    cluster_density
    int    untested_buy_pools
    int    untested_sell_pools
    float  imbalance
    int    magnet_direction
    int    quality_score

// Session state evaluated in each market's own IANA timezone. Session code:
// 0 NONE, 1 ASIA, 2 LONDON, 3 NY_AM, 4 NY_PM. NY wins the London overlap,
// matching the Python session-context precedence.
export type SessionFrame
    int    session_code
    bool   in_killzone
    bool   mss_bull
    bool   mss_bear
    int    structure_state
    bool   fvg_bull_active
    bool   fvg_bear_active
    bool   bpr_active
    float  range_top
    float  range_bottom
    float  mean
    float  vwap
    float  target_bull
    float  target_bear
    bool   opening_range_active
    float  opening_range_top
    float  opening_range_bottom
    int    direction_bias
    int    context_score
    // Session-level MSS: the confirmed close crossing the PREVIOUS session's
    // extreme (one-bar pulse). Distinct from mss_bull/mss_bear above, which
    // are structure events (BOS/CHoCH) while a session is active — those are
    // already derivable from the structure channels. Parity target is
    // SMC_Session_Context.pine's "Session MSS Bull/Bear Confirmed".
    bool   prev_session_break_bull
    bool   prev_session_break_bear

// One confirmed-bar aggregate. Nested frames retain domain provenance; the
// aggregate adds only an explicit six-domain vote and availability summary.
export type ContextFrame
    StructureFrame structure
    ImbalanceFrame imbalance
    ZoneFrame zone
    SweepFrame sweep
    PoolFrame pool
    SessionFrame session
    int    bias
    int    directional_score
    int    available_domains
    int    quality_score

// ── Private helpers (defined before the exported builders) ───────────────────

// Newest active FVG in a buffer, by id. Returns [found, top, bottom, mit_pct].
// The engine already keeps only unfilled FVGs in the active buffer; `not filled`
// is a defensive guard. `mit_pct` is the engine's fill_current_ratio.
_newest_active_fvg(array<eng.FVG> arr) =>
    int   best_id = na
    float top     = na
    float bottom  = na
    float mit     = na
    if not na(arr)
        for f in arr
            if not f.filled
                if na(best_id) or f.id > best_id
                    best_id := f.id
                    top     := f.left_top.price
                    bottom  := f.right_bottom.price
                    mit     := f.fill_current_ratio
    [not na(best_id), top, bottom, mit]

// First active FVG whose gap is at least `min_pct`% of `mid`. Returns
// [found, top, bottom]. Mirrors the golden "any FVG >= 2% of mid" void rule.
_first_void(array<eng.FVG> arr, float mid, float min_pct) =>
    bool  found  = false
    float top    = na
    float bottom = na
    if not na(arr) and mid > 0
        for f in arr
            if not f.filled
                float gap_pct = (f.left_top.price - f.right_bottom.price) / mid * 100.0
                if gap_pct >= min_pct
                    found  := true
                    top    := f.left_top.price
                    bottom := f.right_bottom.price
                    break
    [found, top, bottom]

// Newest active order block in a buffer, by id. Returns [found, id, top, bottom].
// The engine removes broken blocks from the tracking buffer inside update_broken,
// so every element here is active by construction — no `broken` guard needed.
// Geometry is the raw left_top/right_bottom: build_zone_frame keeps every profile
// feature off, so no value-area/POC edge alignment is ever applied.
_newest_active_ob(array<eng.OrderBlock> arr) =>
    int   best_id = na
    float top     = na
    float bottom  = na
    if not na(arr)
        for b in arr
            if not na(b)
                if na(best_id) or b.id > best_id
                    best_id := b.id
                    top     := b.left_top.price
                    bottom  := b.right_bottom.price
    [not na(best_id), best_id, top, bottom]

// Project the engine's OB lifecycle onto a ZoneFrame. Takes the structure tuple
// rather than calling eng.detect_structure itself: track_obs must be fed the SAME
// structure instance a caller already has, and this seam lets the aggregated
// build_context_frame() detect structure ONCE instead of spinning up a second
// detector per frame.
_build_zone(simple int swing_len, float hh, float swing_low, bool bull_bos, bool bull_choch, bool bear_bos, bool bear_choch, simple int atr_len, float min_mult, float max_mult) =>
    float atr       = u.smc_lib_atr(atr_len)
    float min_block = atr * min_mult   // OB_FILTER_MIN_MULT
    float max_block = atr * max_mult   // OB_FILTER_MAX_MULT

    // Profile features stay at their `false` defaults, so the engine never builds
    // a pe.Profile — that keeps the pe import a pure type-resolution edge.
    [active_bull, active_bear, new_bull, new_bear, _broken_bull, _broken_bear, broken_new_bull, broken_new_bear, _disc_bull, _disc_bear] = eng.track_obs(swing_len, hh, swing_low, bull_bos, bull_choch, bear_bos, bear_choch, min_block, max_block)

    [has_bull, bull_id, bull_top, bull_bottom] = _newest_active_ob(active_bull)
    [has_bear, bear_id, bear_top, bear_bottom] = _newest_active_ob(active_bear)

    int bull_count = na(active_bull) ? 0 : array.size(active_bull)
    int bear_count = na(active_bear) ? 0 : array.size(active_bear)

    // Edges, not latches: new_* is a per-bar local in the engine (na unless an OB
    // confirmed this bar); broken_new_* is cleared at the top of every update_broken.
    bool bull_new = not na(new_bull)
    bool bear_new = not na(new_bear)
    bool bull_brk = not na(broken_new_bull) and array.size(broken_new_bull) > 0
    bool bear_brk = not na(broken_new_bear) and array.size(broken_new_bear) > 0

    int count_delta = bull_count - bear_count
    int ob_bias     = count_delta >= ZONE_BIAS_COUNT_DELTA ? 1 : count_delta <= -ZONE_BIAS_COUNT_DELTA ? -1 : 0

    // Composite precedence: OB_BOTH > OB_BULL > OB_BEAR > NONE.
    int state = has_bull and has_bear ? 3 : has_bull ? 1 : has_bear ? 2 : 0

    ZoneFrame current = ZoneFrame.new(has_bull, has_bear, has_bull ? bull_id : na, has_bear ? bear_id : na, has_bull ? bull_top : na, has_bull ? bull_bottom : na, has_bear ? bear_top : na, has_bear ? bear_bottom : na, bull_count, bear_count, bull_new, bear_new, bull_brk, bear_brk, ob_bias, state)
    var ZoneFrame published = ZoneFrame.new(false, false, na, na, na, na, na, na, 0, 0, false, false, false, false, 0, 0)
    if barstate.isconfirmed
        published := current
    published

// ── Exported builders ────────────────────────────────────────────────────────

// Build the structure frame from the engine's per-bar structure detector.
// `swing_len` is the engine pivot length; `fresh_max_bars` bounds the freshness
// window (bars since the last BOS/CHoCH that still count as "fresh").
export build_structure_frame(simple int swing_len = 50, simple int fresh_max_bars = 20) =>
    // detect_structure carries `var` and `ta.*` state, so it must run on EVERY
    // tick. Never move this call inside the barstate guard below: a conditional
    // call desyncs the engine's internal swing/pivot history.
    [trend, bull_bos, bull_choch, top_x, _top_y, _trail_up_x, trail_up, hh, bear_bos, bear_choch, btm_x, _btm_y, _trail_dn_x, trail_dn, swing_low, _hhll_x] = eng.detect_structure(swing_len)

    // Pivot availability, taken from the engine's own swing output instead of
    // inferred from sentinels. detect_pivot starts at `var x = 0` and
    // `var trail_y = high`, so top_x/btm_x read 0 and trail_up/trail_dn read the
    // running high/low extreme before any swing exists — neither is a pivot, and
    // the frame contract says those are `na`. `x == 0` cannot be the tell: the
    // earliest real pivot lands on bar_index 0 (detect_swings anchors a swing at
    // `bar_index - swing_len`), so a bar-index test would discard a true pivot.
    var bool have_up_pivot = false
    var bool have_dn_pivot = false

    var int bars_since = na

    // The published frame. Built on confirmed bars only; between ticks the
    // caller keeps the last confirmed frame. detect_structure raises its
    // BOS/CHoCH alerts intrabar — the Suite wraps the very same alerts in
    // resolve_confirmed_signal for this reason — so an ungated frame repaints:
    // a cross that reverts before the close would surface as bos = true,
    // dir_event = ±1, bars_since_event = 0, and then vanish at the close.
    var StructureFrame published = StructureFrame.new(0, false, false, 0, na, na, na, na, na, false)

    if barstate.isconfirmed
        if not na(hh)
            have_up_pivot := true
        if not na(swing_low)
            have_dn_pivot := true

        bool bos_now   = bull_bos or bear_bos
        bool choch_now = bull_choch or bear_choch
        bool event_now = bos_now or choch_now
        int  dir_event = (bull_bos or bull_choch) ? 1 : (bear_bos or bear_choch) ? -1 : 0

        if event_now
            bars_since := 0
        else if not na(bars_since)
            bars_since := bars_since + 1

        bool fresh = not na(bars_since) and bars_since <= fresh_max_bars

        // `var trend = 1` in the engine is a bootstrap literal, not a reading:
        // it only ever moves on a CHoCH, so before the first confirmed event
        // every symbol reports "bullish". Publish 0 (unknown) until an event has
        // either confirmed the direction (BOS) or set it (CHoCH).
        bool structure_available = not na(bars_since)

        published := StructureFrame.new(structure_available ? trend : 0, bos_now, choch_now, dir_event, have_up_pivot ? trail_up : na, have_dn_pivot ? trail_dn : na, have_up_pivot ? top_x : na, have_dn_pivot ? btm_x : na, bars_since, fresh)

    published

// Build the imbalance frame (FVG lifecycle + BPR + liquidity void) from the
// engine's object-based FVG tracker. Detection is the engine's; the rule layer
// (mitigation/BPR/void/zone-bias/state) matches the golden parity contract.
export build_imbalance_frame(float size_threshold = 0.0) =>
    // The engine is asked for TERMINAL, WICK-measured fills. Both arguments are
    // the parity contract, not preferences:
    //
    //   fill_target_ratio = FULL_MIT_PCT. The engine retires an FVG once price
    //   reaches `top - ratio*size`, so the previous 0.5 default retired it at the
    //   gap MIDPOINT. Everything downstream read a truncated population:
    //   bull_fvg_partial was unreachable (past 50% the FVG had already left the
    //   active buffer), and count / BPR / liq_void / zone_bias / state were all
    //   computed from it. Deliberately NOT a parameter — a caller lowering it
    //   would silently break the golden contract while every test stayed green.
    //
    //   fill_mode = HIGHLOW. The golden measures mitigation from the wick
    //   (`top - last_low` for bull, `last_high - bottom` for bear). The engine
    //   defaults to CLOSE, which reports ~0% for a bar that wicked deep into the
    //   gap and closed back above it. Referencing ct.* here is fine: only `pe` is
    //   a type-resolution-only import (see the allowlist in
    //   tests/test_pine_library_import_permissions.py).
    [_bull_alert, _bear_alert, active_bull, active_bear, _new_bull, _new_bear, _bull_filled, _bear_filled, _bull_filled_new, _bear_filled_new, _bull_disc, _bear_disc] = eng.fvgs_objects(fill_mode = ct.LevelBreakMode.HIGHLOW, fill_target_ratio = FULL_MIT_PCT, size_threshold = size_threshold)

    [has_bull, bull_top, bull_bottom, bull_mit] = _newest_active_fvg(active_bull)
    [has_bear, bear_top, bear_bottom, bear_mit] = _newest_active_fvg(active_bear)

    int bull_count = na(active_bull) ? 0 : array.size(active_bull)
    int bear_count = na(active_bear) ? 0 : array.size(active_bear)

    bool bull_partial = has_bull and not na(bull_mit) and bull_mit >= PARTIAL_MIT_PCT
    bool bear_partial = has_bear and not na(bear_mit) and bear_mit >= PARTIAL_MIT_PCT

    // BPR: overlapping active bull + bear FVG.
    bool  bpr_active = false
    int   bpr_dir    = 0
    float bpr_top    = na
    float bpr_bottom = na
    if has_bull and has_bear
        float ot = math.min(bull_top, bear_top)
        float ob = math.max(bull_bottom, bear_bottom)
        if ot > ob
            bpr_active := true
            bpr_top    := ot
            bpr_bottom := ob
            bpr_dir    := close > (ot + ob) / 2.0 ? 1 : -1

    // Liquidity void: any active FVG >= 2% of mid-price (bull scanned first).
    float mid = (high + low) / 2.0
    [void_bull, void_bull_top, void_bull_bot] = _first_void(active_bull, mid, LIQ_VOID_MIN_SIZE_PCT)
    [void_bear, void_bear_top, void_bear_bot] = _first_void(active_bear, mid, LIQ_VOID_MIN_SIZE_PCT)
    bool  void_active = void_bull or void_bear
    float void_top    = void_bull ? void_bull_top : void_bear ? void_bear_top : na
    float void_bottom = void_bull ? void_bull_bot : void_bear ? void_bear_bot : na

    // Zone bias from active FVG count delta.
    int count_delta = bull_count - bear_count
    int zone_bias   = count_delta >= ZONE_BIAS_COUNT_DELTA ? 1 : count_delta <= -ZONE_BIAS_COUNT_DELTA ? -1 : 0

    // Composite precedence: BPR > VOID > FVG_BULL > FVG_BEAR > NONE.
    int state = bpr_active ? 3 : void_active ? 4 : has_bull ? 1 : has_bear ? 2 : 0

    ImbalanceFrame current = ImbalanceFrame.new(has_bull, has_bear, has_bull ? bull_top : na, has_bull ? bull_bottom : na, has_bear ? bear_top : na, has_bear ? bear_bottom : na, has_bull ? bull_mit : na, has_bear ? bear_mit : na, bull_partial, bear_partial, bull_count, bear_count, bpr_active, bpr_dir, bpr_top, bpr_bottom, void_active, void_top, void_bottom, zone_bias, state)
    var ImbalanceFrame published = ImbalanceFrame.new(false, false, na, na, na, na, na, na, false, false, 0, 0, false, 0, na, na, false, na, na, 0, 0)
    if barstate.isconfirmed
        published := current
    published

// Build the zone frame (order-block lifecycle) from the engine's OB tracker.
// Standalone convenience facade: it detects structure once and feeds that same
// instance into track_obs, exactly as SMC_Long_Dip_Suite does (line 1740).
//
// NOTE for the aggregated build_context_frame(): do NOT call this next to
// build_structure_frame() — each public builder runs its own eng.detect_structure,
// so calling both spins up two independent structure state machines. The aggregator
// must detect once and call the private _build_zone seam directly.
//
// `swing_len` is the engine pivot length; `atr_len` / `min_mult` / `max_mult` are
// the ATR size filter. Their defaults MUST repeat ATR_LEN_MAIN /
// OB_FILTER_MIN_MULT / OB_FILTER_MAX_MULT as literals: Pine rejects a const as a
// parameter default — "The default value cannot be a function, variable or
// calculation" (CE10132) — so naming them here does not compile. The consts stay
// the documented source of these numbers; test_export_defaults_are_literals and
// test_zone_filter_defaults_match_their_consts keep the two in lockstep.
export build_zone_frame(simple int swing_len = 50, simple int atr_len = 50, float min_mult = 0.5, float max_mult = 2.5) =>
    [_trend, bull_bos, bull_choch, _top_x, _top_y, _trail_up_x, _trail_up, hh, bear_bos, bear_choch, _btm_x, _btm_y, _trail_dn_x, _trail_dn, swing_low, _hhll_x] = eng.detect_structure(swing_len)
    _build_zone(swing_len, hh, swing_low, bull_bos, bull_choch, bear_bos, bear_choch, atr_len, min_mult, max_mult)

// Add or reinforce one confirmed pivot cluster. Equal levels are compared by
// percentage so the tolerance is stable across instruments with different
// price scales. Arrays are bounded; oldest weak candidates fall off first.
_pool_add(array<float> levels, array<int> strengths, float candidate, float tolerance_pct, int max_levels) =>
    int match_idx = na
    float best_distance = na
    int size = array.size(levels)
    if size > 0
        for i = 0 to size - 1
            float existing = array.get(levels, i)
            float denominator = math.max(math.abs(existing), syminfo.mintick)
            float distance_pct = math.abs(candidate - existing) / denominator * 100.0
            if distance_pct <= tolerance_pct and (na(best_distance) or distance_pct < best_distance)
                match_idx := i
                best_distance := distance_pct
    if not na(match_idx)
        int old_strength = array.get(strengths, match_idx)
        int new_strength = math.min(5, old_strength + 1)
        float old_level = array.get(levels, match_idx)
        float merged_level = (old_level * old_strength + candidate) / (old_strength + 1)
        array.set(levels, match_idx, merged_level)
        array.set(strengths, match_idx, new_strength)
    else
        array.unshift(levels, candidate)
        array.unshift(strengths, 1)
        if array.size(levels) > max_levels
            array.pop(levels)
            array.pop(strengths)
    true

// A pool stops being "untested" after price trades through it. Removal happens
// before newly confirmed pivots are added, so a pivot is not consumed by the
// same confirmation bar that makes it observable.
_pool_remove_taken(array<float> levels, array<int> strengths, bool buy_side, float bar_high, float bar_low) =>
    int i = array.size(levels) - 1
    while i >= 0
        float level = array.get(levels, i)
        bool taken = buy_side ? bar_high >= level : bar_low <= level
        if taken
            array.remove(levels, i)
            array.remove(strengths, i)
        i -= 1
    true

_nearest_pool(array<float> levels, array<int> strengths, float price, bool buy_side) =>
    float selected_level = na
    int selected_strength = 0
    float selected_distance = na
    int size = array.size(levels)
    if size > 0
        for i = 0 to size - 1
            float level = array.get(levels, i)
            bool correct_side = buy_side ? level > price : level < price
            float distance = math.abs(level - price)
            if correct_side and (na(selected_distance) or distance < selected_distance)
                selected_level := level
                selected_strength := array.get(strengths, i)
                selected_distance := distance
    [selected_level, selected_strength]

// Stateful sweep seam shared by the live builder and the source-derived R3
// runtime fixture. Keeping injected bar/pivot inputs here lets TradingView
// execute deterministic vectors without shipping a test-only public API.
_build_sweep_from_inputs(float bar_high, float bar_low, float bar_close, float bar_volume, float average_volume, float pivot_high, float pivot_low, bool confirmed, simple int fresh_max_bars) =>
    var float reference_high = na
    var float reference_low = na
    var bool event_bull = false
    var bool event_bear = false
    var int event_type = 0
    var int event_direction = 0
    var int event_taken_direction = 0
    var float event_zone_top = na
    var float event_zone_bottom = na
    var float event_depth_pct = 0.0
    var float event_volume_ratio = 0.0
    var float event_reference_high = na
    var float event_reference_low = na
    var bool event_reclaimed = false
    var int event_age = na
    var SweepFrame published = SweepFrame.new(false, false, 0, 0, na, na, false, 0, 0.0, 0.0, 0, na, false)

    if confirmed
        bool bull_taken = not na(reference_low) and bar_low < reference_low and (na(bar_low[1]) or bar_low[1] >= reference_low)
        bool bear_taken = not na(reference_high) and bar_high > reference_high and (na(bar_high[1]) or bar_high[1] <= reference_high)

        if bull_taken or bear_taken
            float bull_depth = bull_taken and reference_low != 0.0 ? (reference_low - bar_low) / math.abs(reference_low) * 100.0 : 0.0
            float bear_depth = bear_taken and reference_high != 0.0 ? (bar_high - reference_high) / math.abs(reference_high) * 100.0 : 0.0
            float depth = math.max(bull_depth, bear_depth)
            float volume_ratio = not na(average_volume) and average_volume > 0 and not na(bar_volume) ? bar_volume / average_volume : 0.0
            int sweep_type = depth >= SWEEP_DEPTH_STOP_HUNT_PCT and volume_ratio >= SWEEP_VOLUME_RATIO_MIN ? SWEEP_TYPE_STOP_HUNT :
                 volume_ratio >= SWEEP_VOLUME_RATIO_MIN ? SWEEP_TYPE_GRAB : SWEEP_TYPE_INDUCEMENT

            event_bull := bull_taken
            event_bear := bear_taken
            event_type := sweep_type
            event_direction := bull_taken and not bear_taken ? 1 : bear_taken and not bull_taken ? -1 : 0
            event_taken_direction := bull_taken and not bear_taken ? -1 : bear_taken and not bull_taken ? 1 : 0
            event_zone_top := bear_taken ? bar_high : reference_low
            event_zone_bottom := bull_taken ? bar_low : reference_high
            event_depth_pct := depth
            event_volume_ratio := volume_ratio
            event_reference_high := reference_high
            event_reference_low := reference_low
            event_age := 0
            event_reclaimed := (not bull_taken or bar_close > reference_low) and (not bear_taken or bar_close < reference_high)
        else if not na(event_age)
            event_age += 1
            if not event_reclaimed and event_age <= SWEEP_RECLAIM_MAX_BARS
                event_reclaimed := (not event_bull or bar_close > event_reference_low) and (not event_bear or bar_close < event_reference_high)

        bool fresh = not na(event_age) and event_age <= fresh_max_bars
        int quality = 0
        if fresh
            quality += event_bull or event_bear ? 1 : 0
            quality += event_type == SWEEP_TYPE_STOP_HUNT or event_type == SWEEP_TYPE_GRAB ? 1 : 0
            quality += event_reclaimed ? 1 : 0
            quality += event_depth_pct >= SWEEP_DEPTH_MIN_PCT ? 1 : 0
            quality += event_volume_ratio >= SWEEP_VOLUME_RATIO_MIN ? 1 : 0

        published := fresh ?
             SweepFrame.new(event_bull, event_bear, event_type, event_direction, event_zone_top, event_zone_bottom, event_reclaimed, event_taken_direction, event_depth_pct, event_volume_ratio, math.min(quality, 5), event_age, true) :
             SweepFrame.new(false, false, 0, 0, na, na, false, 0, 0.0, 0.0, 0, event_age, false)

        // Install newly confirmed pivots only after testing the current bar
        // against the prior reference.
        if not na(pivot_high)
            reference_high := pivot_high
        if not na(pivot_low)
            reference_low := pivot_low

    published

// Detect and score recent sweep events from confirmed pivots and chart OHLCV.
// The scoring ladder is golden-parity; pivot/depth/reclaim detection is the
// explicitly documented Pine-only Column-B rule.
export build_sweep_frame(simple int pivot_len = 5, simple int volume_len = 20, simple int fresh_max_bars = 20) =>
    float pivot_high = ta.pivothigh(high, pivot_len, pivot_len)
    float pivot_low = ta.pivotlow(low, pivot_len, pivot_len)
    float average_volume = ta.sma(volume, volume_len)
    _build_sweep_from_inputs(high, low, close, volume, average_volume, pivot_high, pivot_low, barstate.isconfirmed, fresh_max_bars)

// Stateful pool seam shared by the live builder and the source-derived R3
// runtime fixture.
_build_pool_from_inputs(float bar_high, float bar_low, float bar_close, float pivot_high, float pivot_low, bool confirmed, float tolerance_pct, simple int max_levels) =>
    var array<float> buy_levels = array.new<float>()
    var array<int> buy_strengths = array.new<int>()
    var array<float> sell_levels = array.new<float>()
    var array<int> sell_strengths = array.new<int>()
    var PoolFrame published = PoolFrame.new(na, na, 0, 0, 0.0, 0, 0, 0, 0.0, 0, 0)

    if confirmed
        _pool_remove_taken(buy_levels, buy_strengths, true, bar_high, bar_low)
        _pool_remove_taken(sell_levels, sell_strengths, false, bar_high, bar_low)

        if not na(pivot_high) and pivot_high > bar_close
            _pool_add(buy_levels, buy_strengths, pivot_high, tolerance_pct, max_levels)
        if not na(pivot_low) and pivot_low < bar_close
            _pool_add(sell_levels, sell_strengths, pivot_low, tolerance_pct, max_levels)

        [buy_level, buy_strength] = _nearest_pool(buy_levels, buy_strengths, bar_close, true)
        [sell_level, sell_strength] = _nearest_pool(sell_levels, sell_strengths, bar_close, false)
        int untested_buy = array.size(buy_levels)
        int untested_sell = array.size(sell_levels)
        int total_buy = buy_strength + untested_buy
        int total_sell = sell_strength + untested_sell
        int total = total_buy + total_sell
        float raw_imbalance = total > 0 ? float(total_buy - total_sell) / total : 0.0
        float pool_imbalance = math.round(raw_imbalance * 10000.0) / 10000.0
        int magnet = pool_imbalance >= IMBALANCE_SIG_THRESHOLD ? 1 : pool_imbalance <= -IMBALANCE_SIG_THRESHOLD ? -1 : 0

        float buy_proximity = not na(buy_level) and bar_close != 0.0 ? math.abs(buy_level - bar_close) / math.abs(bar_close) * 100.0 : na
        float sell_proximity = not na(sell_level) and bar_close != 0.0 ? math.abs(sell_level - bar_close) / math.abs(bar_close) * 100.0 : na
        float proximity = na(buy_proximity) ? nz(sell_proximity, 0.0) : na(sell_proximity) ? buy_proximity : math.min(buy_proximity, sell_proximity)
        proximity := math.round(proximity * 10000.0) / 10000.0
        int cluster_density = math.min(5, math.max(buy_strength, sell_strength))

        int quality = 0
        quality += not na(buy_level) or not na(sell_level) ? 1 : 0
        quality += buy_strength >= CLUSTER_STRONG_COUNT or sell_strength >= CLUSTER_STRONG_COUNT ? 1 : 0
        quality += proximity > 0 and proximity <= PROXIMITY_NEAR_PCT ? 1 : 0
        quality += cluster_density >= CLUSTER_STRONG_COUNT ? 1 : 0
        quality += math.abs(pool_imbalance) >= IMBALANCE_SIG_THRESHOLD ? 1 : 0

        published := PoolFrame.new(buy_level, sell_level, buy_strength, sell_strength, proximity, cluster_density, untested_buy, untested_sell, pool_imbalance, magnet, math.min(quality, 5))

    published

// Detect equal-pivot clusters and project the exact golden pool rule layer.
export build_pool_frame(simple int pivot_len = 5, float tolerance_pct = 0.1, simple int max_levels = 20) =>
    float pivot_high = ta.pivothigh(high, pivot_len, pivot_len)
    float pivot_low = ta.pivotlow(low, pivot_len, pivot_len)
    _build_pool_from_inputs(high, low, close, pivot_high, pivot_low, barstate.isconfirmed, tolerance_pct, max_levels)

// Stateful session seam. The live wrapper owns IANA-zone classification; this
// helper owns the range, VWAP, scoring, and reset behavior replayed by the R3
// fixture.
_build_session_from_inputs(StructureFrame structure, ImbalanceFrame imbalance, int bar_time, float bar_high, float bar_low, float bar_close, float bar_hlc3, float bar_volume, int session_code, bool in_killzone, bool confirmed) =>
    var int active_session = 0
    var int session_started_at = na
    var float session_top = na
    var float session_bottom = na
    // The finished extremes of the most recently COMPLETED session, stashed at
    // both exit transitions (active -> outside, active -> other session)
    // before the running range resets — mirroring SMC_Session_Context.pine.
    var float previous_session_top = na
    var float previous_session_bottom = na
    var float opening_top = na
    var float opening_bottom = na
    var float cumulative_pv = 0.0
    var float cumulative_volume = 0.0
    var SessionFrame published = SessionFrame.new(0, false, false, false, 0, false, false, false, na, na, na, na, na, na, false, na, na, 0, 0, false, false)

    if confirmed
        bool session_changed = session_code != active_session
        if session_code == 0
            if active_session != 0 and not na(session_top)
                previous_session_top := session_top
                previous_session_bottom := session_bottom
            active_session := 0
            session_started_at := na
            session_top := na
            session_bottom := na
            opening_top := na
            opening_bottom := na
            cumulative_pv := 0.0
            cumulative_volume := 0.0
            published := SessionFrame.new(0, false, false, false, 0, false, false, false, na, na, na, na, na, na, false, na, na, 0, 0, false, false)
        else
            if session_changed
                if active_session != 0 and not na(session_top)
                    previous_session_top := session_top
                    previous_session_bottom := session_bottom
                active_session := session_code
                session_started_at := bar_time
                session_top := bar_high
                session_bottom := bar_low
                opening_top := bar_high
                opening_bottom := bar_low
                cumulative_pv := 0.0
                cumulative_volume := 0.0
            else
                session_top := math.max(session_top, bar_high)
                session_bottom := math.min(session_bottom, bar_low)

            bool opening_active = not na(session_started_at) and bar_time - session_started_at < OPENING_RANGE_MINUTES * 60000
            if opening_active
                opening_top := math.max(opening_top, bar_high)
                opening_bottom := math.min(opening_bottom, bar_low)

            if not na(bar_volume) and bar_volume > 0
                cumulative_pv += bar_hlc3 * bar_volume
                cumulative_volume += bar_volume

            bool mss_bull = (structure.bos or structure.choch) and structure.dir_event == 1
            bool mss_bear = (structure.bos or structure.choch) and structure.dir_event == -1
            int bull_score = (mss_bull ? 2 : 0) + (imbalance.bull_fvg_active ? 1 : 0) + (not na(session_top) ? 1 : 0)
            int bear_score = (mss_bear ? 2 : 0) + (imbalance.bear_fvg_active ? 1 : 0) + (not na(session_bottom) ? 1 : 0)
            int direction_bias = bull_score > bear_score ? 1 : bear_score > bull_score ? -1 : 0

            int context_score = 1
            context_score += in_killzone ? 1 : 0
            context_score += mss_bull or mss_bear ? 1 : 0
            context_score += imbalance.bull_fvg_active or imbalance.bear_fvg_active ? 1 : 0
            context_score += direction_bias != 0 ? 1 : 0
            context_score += structure.trend != 0 ? 1 : 0
            context_score += imbalance.bpr_active ? 1 : 0

            // One-bar pulse, evaluated on the first bar of a session too —
            // the previous session's extreme is broken most often right at
            // the open, and SMC_Session_Context.pine fires there as well.
            bool prev_break_bull = not na(previous_session_top) and bar_close > previous_session_top and bar_close[1] <= previous_session_top
            bool prev_break_bear = not na(previous_session_bottom) and bar_close < previous_session_bottom and bar_close[1] >= previous_session_bottom

            float session_mean = (session_top + session_bottom) / 2.0
            float session_vwap = cumulative_volume > 0 ? cumulative_pv / cumulative_volume : na
            published := SessionFrame.new(session_code, in_killzone, mss_bull, mss_bear, structure.trend, imbalance.bull_fvg_active, imbalance.bear_fvg_active, imbalance.bpr_active, session_top, session_bottom, session_mean, session_vwap, session_top, session_bottom, opening_active, opening_top, opening_bottom, direction_bias, math.min(context_score, 7), prev_break_bull, prev_break_bear)

    published

// Compose session-local ranges and the Python-golden scoring layer. Trading
// windows are evaluated in their own zones; US and European DST are therefore
// independent by construction.
export build_session_frame(StructureFrame structure, ImbalanceFrame imbalance) =>
    bool intraday = timeframe.isintraday
    bool asia_active = intraday and not na(time(timeframe.period, "0900-1700:1234567", "Asia/Tokyo"))
    bool london_active = intraday and not na(time(timeframe.period, "0800-1630:1234567", "Europe/London"))
    bool ny_am_active = intraday and not na(time(timeframe.period, "0930-1300:1234567", "America/New_York"))
    bool ny_pm_active = intraday and not na(time(timeframe.period, "1300-1600:1234567", "America/New_York"))
    int session_code = ny_pm_active ? SESSION_NY_PM : ny_am_active ? SESSION_NY_AM : london_active ? SESSION_LONDON : asia_active ? SESSION_ASIA : 0

    bool asia_kz = intraday and not na(time(timeframe.period, "0900-1300:1234567", "Asia/Tokyo"))
    bool london_kz = intraday and not na(time(timeframe.period, "0800-1100:1234567", "Europe/London"))
    bool ny_kz = intraday and not na(time(timeframe.period, "0930-1200:1234567", "America/New_York"))
    bool in_killzone = asia_kz or london_kz or ny_kz
    _build_session_from_inputs(structure, imbalance, time, high, low, close, hlc3, volume, session_code, in_killzone, barstate.isconfirmed)

// Aggregate seam shared by the live builder and the source-derived R3 runtime
// fixture. All voting and availability semantics live in exactly one place.
_aggregate_context(StructureFrame structure, ImbalanceFrame imbalance, ZoneFrame zone, SweepFrame sweep, PoolFrame pool, SessionFrame session) =>
    int structure_vote = structure.trend
    int imbalance_vote = imbalance.zone_bias
    int zone_vote = zone.ob_bias
    int sweep_vote = sweep.fresh ? sweep.direction : 0
    int pool_vote = pool.magnet_direction
    int session_vote = session.direction_bias
    int directional_score = structure_vote + imbalance_vote + zone_vote + sweep_vote + pool_vote + session_vote
    int bias = directional_score >= CONTEXT_BIAS_MIN_VOTES ? 1 : directional_score <= -CONTEXT_BIAS_MIN_VOTES ? -1 : 0

    bool structure_available = structure.trend != 0 or not na(structure.support) or not na(structure.resistance)
    bool imbalance_available = imbalance.state != 0
    bool zone_available = zone.state != 0
    bool sweep_available = sweep.fresh
    bool pool_available = not na(pool.buy_side_level) or not na(pool.sell_side_level)
    bool session_available = session.session_code != 0
    int available_domains = (structure_available ? 1 : 0) + (imbalance_available ? 1 : 0) + (zone_available ? 1 : 0) + (sweep_available ? 1 : 0) + (pool_available ? 1 : 0) + (session_available ? 1 : 0)

    float quality_sum = (structure_available ? (structure.fresh ? 100.0 : 50.0) : 0.0) +
         (imbalance_available ? 100.0 : 0.0) + (zone_available ? 100.0 : 0.0) +
         (sweep_available ? sweep.quality_score * 20.0 : 0.0) +
         (pool_available ? pool.quality_score * 20.0 : 0.0) +
         (session_available ? session.context_score / 7.0 * 100.0 : 0.0)
    int quality_score = available_domains > 0 ? int(math.round(quality_sum / available_domains)) : 0

    ContextFrame.new(structure, imbalance, zone, sweep, pool, session, bias, directional_score, available_domains, quality_score)

// Complete confirmed-bar aggregate. detect_structure runs exactly once here and
// the same tuple feeds both StructureFrame and ZoneFrame.
export build_context_frame(simple int swing_len = 50, simple int fresh_max_bars = 20, float fvg_size_threshold = 0.0, simple int atr_len = 50, float ob_min_mult = 0.5, float ob_max_mult = 2.5, simple int sweep_pivot_len = 5, simple int sweep_volume_len = 20, simple int pool_pivot_len = 5, float pool_tolerance_pct = 0.1, simple int pool_max_levels = 20) =>
    [trend, bull_bos, bull_choch, top_x, _top_y, _trail_up_x, trail_up, hh, bear_bos, bear_choch, btm_x, _btm_y, _trail_dn_x, trail_dn, swing_low, _hhll_x] = eng.detect_structure(swing_len)

    var bool have_up_pivot = false
    var bool have_dn_pivot = false
    var int bars_since = na
    var StructureFrame structure = StructureFrame.new(0, false, false, 0, na, na, na, na, na, false)

    if barstate.isconfirmed
        if not na(hh)
            have_up_pivot := true
        if not na(swing_low)
            have_dn_pivot := true
        bool bos_now = bull_bos or bear_bos
        bool choch_now = bull_choch or bear_choch
        bool event_now = bos_now or choch_now
        int dir_event = (bull_bos or bull_choch) ? 1 : (bear_bos or bear_choch) ? -1 : 0
        if event_now
            bars_since := 0
        else if not na(bars_since)
            bars_since += 1
        bool fresh = not na(bars_since) and bars_since <= fresh_max_bars
        structure := StructureFrame.new(not na(bars_since) ? trend : 0, bos_now, choch_now, dir_event, have_up_pivot ? trail_up : na, have_dn_pivot ? trail_dn : na, have_up_pivot ? top_x : na, have_dn_pivot ? btm_x : na, bars_since, fresh)

    ZoneFrame zone = _build_zone(swing_len, hh, swing_low, bull_bos, bull_choch, bear_bos, bear_choch, atr_len, ob_min_mult, ob_max_mult)
    ImbalanceFrame imbalance = build_imbalance_frame(fvg_size_threshold)
    SweepFrame sweep = build_sweep_frame(sweep_pivot_len, sweep_volume_len, fresh_max_bars)
    PoolFrame pool = build_pool_frame(pool_pivot_len, pool_tolerance_pct, pool_max_levels)
    SessionFrame session_frame = build_session_frame(structure, imbalance)

    _aggregate_context(structure, imbalance, zone, sweep, pool, session_frame)

Overview

SMC Context Engine Private derives StructureFrame using engine primitives for BOS, CHoCH, trend and S-R levels. ImbalanceFrame tracks FVG lifecycle, BPR overlap, liquidity void and zone-bias. ZoneFrame manages order-block lifecycle. Session classification uses IANA zones and composes all domains without extra structure detection for dependent frames. Detection follows engine SSOT with golden parity contract to Python reference.

Details

Published by

skipp-dev on GitHub

Code credit

preuss_steffen, as written in the script's header

Type

Library, Pine Script v6

Draws

Library (no plots)

Category

SMC / ICT / price action

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

AuthLib

by alboogycOdR
6 stars

AuthLib is a Pine Script utility library providing access control and state management structures for technical indicators. It checks callers against a hardcoded array of authorized TradingView usernames before executing operations. The library exports user-de

Library v5 SMC / ICT / price actionSessions & time
Updated 7 months ago · None stated GitHub repository of AuthLib

FVGDetectorLibrary

by alvinng214
2 stars

This Pine Script library detects and plots Smart Money Concept Fair Value Gaps (FVGs) for both demand and supply zones directly on the price chart. It exports a detection function that accepts configurable filter thresholds ranging from standard three-bar imba

Library v5 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · None stated GitHub repository of FVGDetectorLibrary

SFA FlowLiquidity v1

by genesisgrubio39
1 stars

The SFA FlowLiquidity library computes structural flow and liquidity events from price data. It detects sweep ups and downs with associated scores, identifies liquidity levels, and applies session and regime filters. Persistent arrays track liquidity prices, b

Library v6 SMC / ICT / price actionSupport & resistance
Updated 10 months ago · None stated GitHub repository of SFA FlowLiquidity v1

FunctionLinearRegression

by ricardosantos79
261 stars

This Pine Script v5 library provides functions to calculate and plot linear regression channels using sample point arrays. It evaluates paired X and Y arrays to calculate the regression slope, intercept, fitted values, maximum deviation, minimum deviation, and

Library v5 Trend
Updated 4 years ago · None stated GitHub repository of FunctionLinearRegression

PositionSizing

by iamrichardD
57 stars

This Pine Script v6 library calculates trade position sizes and provides risk management functions for external scripts. It computes position sizes from account equity, percentage risk, and stop-loss distance, and offers dynamic sizing adjustments based on vol

Library v6
Updated 1 year ago · Apache-2.0 GitHub repository of PositionSizing

Chart Patterns

by deepentropy
13 stars

Chart Patterns is a Pine Script v6 library designed to scan for and render technical chart patterns directly on the price chart. It defines custom user-defined types to configure scanning parameters, such as Zigzag pivot offsets, error ratios, flat ratios, and

Library v6 Repaints
Updated 6 months ago · MIT GitHub repository of Chart Patterns

More libraries

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers