Library · Pine Script v6 · Library

SMC Utils

by skipp-dev

SMC Utils is a Pine Script library providing helper functions for Smart Money Concepts indicators and strategies.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code282
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

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

smc_utils.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

//@version=6
library("smc_utils", overlay = true)

import preuss_steffen/smc_core_types/5 as ct

// --------------------------------------------------
// Generic helpers
// --------------------------------------------------

export in_range(float value, float max, float min = 0.0) =>
    min <= value and value <= max

export in_range(float max1, float min1, float max2, float min2) =>
    min2 <= min1 and min1 <= max2 and min2 <= max1 and max1 <= max2

export range_overlap(float max1, float min1, float max2, float min2) =>
    min1 < max2 and max1 > min2

export auto_ltf_timeframe(int chart_seconds) =>
    chart_seconds <= 60 ? '' :
     chart_seconds <= 180 ? '1' :
     chart_seconds <= 300 ? '1' :
     chart_seconds <= 900 ? '3' :
     chart_seconds <= 1800 ? '5' :
     chart_seconds <= 3600 ? '15' :
     chart_seconds <= 14400 ? '30' :
     chart_seconds <= 86400 ? '60' :
     chart_seconds <= 604800 ? '240' :
     '1D'

export trend_text(int trend_value) =>
    trend_value > 0 ? 'Bullish' : trend_value < 0 ? 'Bearish' : 'Neutral'

export trend_text_or_na(float trend_value) =>
    na(trend_value) or trend_value == 0 ? 'n/a' : trend_value > 0 ? 'Bullish' : 'Bearish'

export format_level(float value) =>
    na(value) ? 'n/a' : str.tostring(value, format.mintick)

export prefer_level(float primary_level, float fallback_level) =>
    not na(primary_level) ? primary_level : fallback_level

export edge(bool condition) =>
    condition and not condition[1]

export scaled_marker_gap(int base_gap, bool auto_scale, int chart_tf_seconds, int reference_tf_seconds, float max_scale) =>
    scale_base = reference_tf_seconds > 0 ? reference_tf_seconds : 180
    ratio = scale_base > 0 and chart_tf_seconds > 0 ? chart_tf_seconds / scale_base : 1.0
    bounded_scale = auto_scale ? math.min(math.max(math.sqrt(ratio), 1.0), max_scale) : 1.0
    int(math.round(base_gap * bounded_scale))

export ltf_stats(float[] opens, float[] closes, float[] volumes) =>
    sample_count = opens.size()
    bull_count = 0
    bear_count = 0
    bull_volume = 0.0
    bear_volume = 0.0

    if sample_count > 0
        for i = 0 to sample_count - 1
            open_value = opens.get(i)
            close_value = closes.get(i)
            volume_value = i < volumes.size() ? volumes.get(i) : 0.0
            if close_value > open_value
                bull_count += 1
                bull_volume += volume_value
            else if close_value < open_value
                bear_count += 1
                bear_volume += volume_value

    total_volume = bull_volume + bear_volume
    bull_share = sample_count > 0 ? bull_count / sample_count : na
    volume_delta = total_volume > 0 ? (bull_volume - bear_volume) / total_volume : na
    [sample_count, bull_share, volume_delta]

export weighted_avg4(float a, float b, float c, float d, float wa = 1.0, float wb = 1.0, float wc = 1.0, float wd = 1.0) =>
    float wsum = wa + wb + wc + wd
    wsum > 0 ? (a * wa + b * wb + c * wc + d * wd) / wsum : na

export lr_slope_pct(series float src, simple int len) =>
    float lr_now = ta.linreg(src, len, 0)
    float lr_prev = ta.linreg(src, len, 1)
    float base = math.max(math.abs(src), syminfo.mintick)
    100.0 * (lr_now - lr_prev) / base

export sd_lr2_pct(series float src, simple int len) =>
    float lr0 = ta.linreg(src, len, 0)
    float lr1 = ta.linreg(src, len, 1)
    float lr_slope = lr0 - lr1
    float denom = math.max(math.abs(src), syminfo.mintick)
    not na(lr_slope[1]) ? 100.0 * (lr_slope - lr_slope[1]) / denom : na

export qema_like(series float src, simple int len) =>
    float ema1 = ta.ema(src, len)
    float ema2 = ta.ema(ema1, len)
    float ema3 = ta.ema(ema2, len)
    float ema4 = ta.ema(ema3, len)
    float ema5 = ta.ema(ema4, len)
    5.0 * ema1 - 10.0 * ema2 + 10.0 * ema3 - 5.0 * ema4 + ema5

export external_trend_gate(simple string symbol, simple int fast_len, simple int slow_len) =>
    float ext_close = request.security(symbol, timeframe.period, close,
         gaps = barmerge.gaps_off,
         lookahead = barmerge.lookahead_off,
         ignore_invalid_symbol = true,
         calc_bars_count = 300)

    float ext_close_sparse = request.security(symbol, timeframe.period, close,
         gaps = barmerge.gaps_on,
         lookahead = barmerge.lookahead_off,
         ignore_invalid_symbol = true,
         calc_bars_count = 300)

    float ext_fast = ta.ema(ext_close_sparse, fast_len)
    float ext_slow = ta.ema(ext_close_sparse, slow_len)

    bool ext_missing = na(ext_close) or na(ext_fast) or na(ext_slow)
    [ext_missing, not ext_missing and ext_close > ext_fast and ext_fast > ext_slow]

export external_breadth_gate(simple string symbol, simple string mode, simple int len) =>
    float ext_breadth = request.security(symbol, timeframe.period, close,
         gaps = barmerge.gaps_off,
         lookahead = barmerge.lookahead_off,
         ignore_invalid_symbol = true,
         calc_bars_count = 100)

    float ext_breadth_sparse = request.security(symbol, timeframe.period, close,
         gaps = barmerge.gaps_on,
         lookahead = barmerge.lookahead_off,
         ignore_invalid_symbol = true,
         calc_bars_count = 100)

    float ext_ema = ta.ema(ext_breadth_sparse, len)

    bool breadth_missing = na(ext_breadth) or (mode == 'Above EMA' and na(ext_ema))
    bool breadth_pass = mode == 'Above EMA' ? ext_breadth > ext_ema : mode == 'Rising' ? ext_breadth > ext_breadth[1] : ext_breadth > 0
    [breadth_missing, not breadth_missing and breadth_pass]

export normalize_csv_token(string s) =>
    str.upper(str.replace_all(s, ' ', ''))

export csv_has_token(string csv, string token) =>
    string haystack = ',' + normalize_csv_token(csv) + ','
    string needle = ',' + normalize_csv_token(token) + ','
    str.contains(haystack, needle)

export csv_has_symbol_token(string csv, string ticker, string tickerid) =>
    csv_has_token(csv, ticker) or csv_has_token(csv, tickerid)

export universe_status_code(bool universe_initialized, bool ticker_in_universe, bool producer_context_partial) =>
    universe_initialized ? ticker_in_universe ? producer_context_partial ? 2 : 3 : 1 : 0

export universe_status_badge(int status_code) =>
    status_code == 3 ? 'UNIVERSE: VERIFIED' : status_code == 2 ? 'UNIVERSE: PARTIAL' : status_code == 1 ? 'UNIVERSE: NOT SCANNED' : 'UNIVERSE: UNINITIALIZED'

export universe_status_banner(int status_code) =>
    status_code == 3 ? 'This symbol is part of the latest scanned universe. Full producer context is available.' : status_code == 2 ? 'This symbol has partial universe coverage. Core SMC logic runs, but some producer-based context may be missing.' : status_code == 1 ? 'This symbol is outside the latest scanned universe. Core SMC logic still runs, but producer-based context is unavailable.' : 'Universe data is not loaded. Core chart logic may run, but producer-based context cannot be validated.'

export universe_status_dashboard_text(int status_code) =>
    status_code == 3 ? 'Status: Verified\nThis symbol is included in the latest scanned universe.\nAll available producer-based context can be used.' : status_code == 2 ? 'Status: Partial\nThis symbol is only partially covered by the latest universe snapshot.\nSome enriched context fields may be unavailable or incomplete.' : status_code == 1 ? 'Status: Not Scanned\nThis symbol was not included in the latest scanned universe.\nCore SMC chart logic remains active.\nProducer-based context is not available for this symbol.' : 'Status: Uninitialized\nUniverse membership could not be verified.\nProducer-based context is not loaded or not ready.\nUse caution until universe data is available.'

export universe_status_feature_matrix(int status_code) =>
    status_code == 3 ? 'SMC Structure: Available\nMarket Structure Logic: Available\nProducer Context: Available\nNews Context: Available\nEarnings Context: Available\nMicrostructure Profile: Available\nMacro Regime Context: Available' : status_code == 2 ? 'SMC Structure: Available\nMarket Structure Logic: Available\nProducer Context: Limited\nNews Context: Limited\nEarnings Context: Limited\nMicrostructure Profile: Limited\nMacro Regime Context: Available' : status_code == 1 ? 'SMC Structure: Available\nMarket Structure Logic: Available\nProducer Context: Unavailable\nUniverse Membership: Not Verified\nNews Context: Unavailable\nEarnings Context: Unavailable\nMicrostructure Profile: Unavailable\nMacro Regime Context: Available' : 'SMC Structure: Available\nMarket Structure Logic: Available\nProducer Context: Not loaded\nUniverse Membership: Not Verified\nNews Context: Not verified\nEarnings Context: Not verified\nMicrostructure Profile: Not verified\nMacro Regime Context: Available'

export universe_status_strategy_text(int status_code) =>
    status_code == 1 ? 'Strategy entries are disabled because this symbol is outside the scanned universe.' : status_code == 0 ? 'Strategy entries are disabled because universe data is not available.' : ''

export htf_fvg_key(int left_time, int right_time, float top, float bottom) =>
    str.format('{0}|{1}|{2}|{3}', left_time, right_time, format_level(top), format_level(bottom))

export push_capped_label(label[] buffer, label new_label, int max_keep) =>
    if not na(new_label)
        if max_keep <= 0
            label.delete(new_label)
        else
            buffer.push(new_label)
            while buffer.size() > max_keep
                label.delete(buffer.shift())

export trim_label_history(label[] history, int max_keep) =>
    if max_keep <= 0
        for i = history.size() - 1 to 0
            label.delete(history.get(i))
        history.clear()
    else
        while history.size() > max_keep
            label.delete(history.shift())

// --------------------------------------------------
// Embedded helpers (extracted from Core Engine)
// --------------------------------------------------

export smc_lib_atr(simple int length) =>
    float atr_value = ta.atr(length)
    float tr_cum = ta.cum(ta.tr(true))
    if bar_index < length
        atr_value := tr_cum / (bar_index + 1)
    atr_value

export smc_lib_ehma(float source, simple int length) =>
    int half_length = math.max(1, int(math.round(length / 2.0)))
    int sqrt_length = math.max(1, int(math.round(math.sqrt(length))))
    ta.ema(2 * ta.ema(source, half_length) - ta.ema(source, length), sqrt_length)

export smc_lib_thma(float source, simple int length) =>
    int len_sixth = math.max(1, int(math.round(length / 6.0)))
    int len_quarter = math.max(1, int(math.round(length / 4.0)))
    int len_half = math.max(1, int(math.round(length / 2.0)))
    ta.wma(ta.wma(source, len_sixth) * 3 - ta.wma(source, len_quarter) - ta.wma(source, len_half), len_half)

export smc_lib_zlema(float source, simple int length) =>
    // Zero Lag EMA (Ehlers/Way): EMA of de-lagged source — 2*src - src[lag]
    int lag = math.max(0, int(math.round((length - 1) / 2.0)))
    ta.ema(2 * source - source[lag], length)

export smc_lib_get_ma(simple ct.SmcLibMovingAverage select_ma = ct.SmcLibMovingAverage.SMA, float source, simple int length) =>
    switch select_ma
        ct.SmcLibMovingAverage.EMA => ta.ema(source, length)
        ct.SmcLibMovingAverage.RMA => ta.rma(source, length)
        ct.SmcLibMovingAverage.WMA => ta.wma(source, length)
        ct.SmcLibMovingAverage.VWMA => ta.vwma(source, length)
        ct.SmcLibMovingAverage.HMA => ta.hma(source, length)
        ct.SmcLibMovingAverage.EHMA => smc_lib_ehma(source, length)
        ct.SmcLibMovingAverage.THMA => smc_lib_thma(source, length)
        ct.SmcLibMovingAverage.ZLEMA => smc_lib_zlema(source, length)
        => ta.sma(source, length)

export smc_lib_bb(float source, simple int length, float mult, simple ct.SmcLibMovingAverage select_ma) =>
    float base = smc_lib_get_ma(select_ma, source, length)
    float dev = mult * ta.stdev(source, length)
    [base, base + dev, base - dev]

export smc_lib_dmi(simple int di_length = 17, simple int adx_smoothing = 14) =>
    ta.dmi(di_length, adx_smoothing)

export smc_lib_detect_divergence(float osc, simple int pivot_confirmation_bars_left = 5, simple int pivot_confirmation_bars_right = 3, simple int pivot_min_x_distance = 5, simple int pivot_max_x_distance = 50, series float ref_low = low, series float ref_high = high, simple int pivot_min_y_distance_len = 14, simple float pivot_min_y_distance_dev_mult = 0.1, simple float pivot_min_y_distance_atr_mult = 0.1) =>
    bool osc_pivot_l = not na(ta.pivotlow(osc, pivot_confirmation_bars_left, pivot_confirmation_bars_right))
    bool osc_pivot_h = not na(ta.pivothigh(osc, pivot_confirmation_bars_left, pivot_confirmation_bars_right))
    float pivot_osc_value = osc[pivot_confirmation_bars_right]
    float osc_dev_value = nz(ta.dev(pivot_osc_value, pivot_min_y_distance_len), 0.0) * pivot_min_y_distance_dev_mult
    float price_atr_value = smc_lib_atr(pivot_min_y_distance_len) * pivot_min_y_distance_atr_mult
    var int osc_prev_l_idx = na
    var float osc_prev_l_value = na
    var float price_prev_l = na
    var int osc_prev_h_idx = na
    var float osc_prev_h_value = na
    var float price_prev_h = na

    if barstate.isconfirmed and (osc_pivot_h or osc_pivot_l)
        float prev_pivot_value = osc_pivot_h ? osc_prev_h_value : osc_prev_l_value
        float prev_pivot_price = osc_pivot_h ? price_prev_h : price_prev_l
        float new_pivot_value = osc[pivot_confirmation_bars_right]
        float new_pivot_price = osc_pivot_h ? ref_high[pivot_confirmation_bars_right] : ref_low[pivot_confirmation_bars_right]
        int new_pivot_distance = osc_pivot_h ? bar_index - osc_prev_h_idx : bar_index - osc_prev_l_idx
        bool has_prev_pivot = osc_pivot_h ? not na(osc_prev_h_idx) and not na(prev_pivot_value) and not na(prev_pivot_price) : not na(osc_prev_l_idx) and not na(prev_pivot_value) and not na(prev_pivot_price)

        if osc_pivot_h
            osc_prev_h_idx := bar_index
            osc_prev_h_value := new_pivot_value
            price_prev_h := new_pivot_price
        if osc_pivot_l
            osc_prev_l_idx := bar_index
            osc_prev_l_value := new_pivot_value
            price_prev_l := new_pivot_price

        int mode = has_prev_pivot ? new_pivot_value < prev_pivot_value - osc_dev_value ? -1 : new_pivot_value > prev_pivot_value + osc_dev_value ? 1 : 0 : 0
        int mode_p = has_prev_pivot ? new_pivot_price < prev_pivot_price - price_atr_value ? -1 : new_pivot_price > prev_pivot_price + price_atr_value ? 1 : 0 : 0
        bool x_distance_in_range = has_prev_pivot and new_pivot_distance >= pivot_min_x_distance and new_pivot_distance <= pivot_max_x_distance

        bool strong_bull = osc_pivot_l and x_distance_in_range and mode == 1 and mode_p == -1
        bool medium_bull = osc_pivot_l and x_distance_in_range and mode == 1 and mode_p == 0
        bool weak_bull = osc_pivot_l and x_distance_in_range and mode == 0 and mode_p == -1
        bool hidden_bull = osc_pivot_l and x_distance_in_range and mode == -1 and mode_p == 1
        bool strong_bear = osc_pivot_h and x_distance_in_range and mode == -1 and mode_p == 1
        bool medium_bear = osc_pivot_h and x_distance_in_range and mode == -1 and mode_p == 0
        bool weak_bear = osc_pivot_h and x_distance_in_range and mode == 0 and mode_p == 1
        bool hidden_bear = osc_pivot_h and x_distance_in_range and mode == 1 and mode_p == -1
        [strong_bull, strong_bear, medium_bull, medium_bear, weak_bull, weak_bear, hidden_bull, hidden_bear, osc_pivot_h, osc_pivot_l]
    else
        [false, false, false, false, false, false, false, false, osc_pivot_h, osc_pivot_l]

Overview

SMC Utils features functions for value range checks, range overlap determination, lower-timeframe selection, formatting price levels and trend states, dynamic marker gap scaling, and calculating lower-timeframe candle statistics and volume delta. Additionally, it offers linear regression slope percentage utilities. As a library, it produces no standalone visual output or trade signals on its own.

Details

Published by

skipp-dev on GitHub

Type

Library, Pine Script v6

Draws

Library (no plots)

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

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

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

WebhookMessageLibrary

by TradersPost
6 stars

WebhookMessageLibrary is a helper library designed to construct structured JSON payload messages for automated trading integrations like TradersPost. It defines custom exported types and constants for trade actions, market sentiments, quantities, limit prices,

Library v5
Updated 1 year ago · None stated GitHub repository of WebhookMessageLibrary

PineDraw

by 345-strategies
5 stars

PineDraw is a Pine Script v6 library that provides object primitives for drawing and updating price levels on the chart. It enables indicators and strategies to maintain lines, boxes, and labels in place by updating existing objects instead of deleting and rec

Library v6 Support & resistance
Updated 2 days ago · MPL-2.0 GitHub repository of PineDraw

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