Last updated: 23 mars 2026

Ready-to-use Pine Scripts built around the frameworks in the Tactics section. Each script runs on TradingView — paste into the Pine Editor, add to chart, done. New to Pine Script? Start with the Pine Script Guide first.

Market Structure
BTC Dominance Zones
Chart: CRYPTOCAP:BTC.D

Colours the BTC Dominance chart background by market phase — Bear, Early Bull, Mid Bull, Altcoin Season and Extreme. Draws labelled zone boundaries and a phase label on the last bar. Read alongside the BTC Dominance article for full context on how to interpret each zone.

BTC Dominance Zones script output
Click to zoom
Above 60% — Bear
50–60% — Early Bull
45–50% — Mid Bull
38–45% — Altcoin Season
Below 38% — Extreme
//@version=5
indicator("BTC Dominance Zones", overlay=true)

h100 = hline(100, "", color=color.new(color.red, 100))
h60 = hline(60, "60pct - Bear", color=color.red, linestyle=hline.style_dashed, linewidth=1)
h50 = hline(50, "50pct - Early Bull", color=color.orange, linestyle=hline.style_dashed, linewidth=1)
h45 = hline(45, "45pct - Mid Bull", color=color.yellow, linestyle=hline.style_dashed, linewidth=1)
h38 = hline(38, "38pct - Altcoin Season", color=color.green, linestyle=hline.style_dashed, linewidth=1)
h0 = hline(0, "", color=color.new(color.purple, 100))

fill(h100, h60, color=color.new(color.red, 80))
fill(h60, h50, color=color.new(color.orange, 80))
fill(h50, h45, color=color.new(color.yellow, 75))
fill(h45, h38, color=color.new(color.green, 75))
fill(h38, h0, color=color.new(color.purple, 75))

if barstate.islast
    label.new(bar_index, 80, "Bear", style=label.style_label_left, color=color.new(color.red, 60), textcolor=color.white, size=size.normal)
    label.new(bar_index, 55, "Early Bull", style=label.style_label_left, color=color.new(color.orange, 60), textcolor=color.white, size=size.normal)
    label.new(bar_index, 47, "Mid Bull", style=label.style_label_left, color=color.new(color.yellow, 60), textcolor=color.black, size=size.normal)
    label.new(bar_index, 41, "Altcoin Season", style=label.style_label_left, color=color.new(color.green, 60), textcolor=color.white, size=size.normal)
    label.new(bar_index, 19, "Extreme Altcoin / Top Risk", style=label.style_label_left, color=color.new(color.purple, 60), textcolor=color.white, size=size.normal)

Paste into a plain text editor first, then into TradingView Pine Editor. Add to chart on CRYPTOCAP:BTC.D.

Market Structure
True BTC Dominance — Stablecoin-Adjusted
Chart: CRYPTOCAP:BTC.D

Standard BTC Dominance includes stablecoins in the total market cap — meaning BTC.D falls not only when capital rotates into alts, but also simply because USDT and USDC keep growing. This script corrects for that distortion by subtracting USDT and USDC market cap from the denominator.

The result: two lines plotted simultaneously — adjusted (orange, zone-coloured) and standard (grey) — with the gap between them filled in blue. When the gap widens, stablecoin dominance is rising: capital is parked defensively, not rotating into alts. Read alongside the BTC Dominance and Sector Rotation articles.

True BTC Dominance stablecoin-adjusted vs standard BTC.D - TradingView screenshot
Click to zoom
Above 60% — Bear
50–60% — Early Bull
45–50% — Mid Bull
38–45% — Altcoin Season
Below 38% — Extreme
Blue fill — stablecoin gap
//@version=5
indicator("True BTC Dominance - Stablecoin-Adjusted", overlay=false, max_bars_back=500)

// Fetch market cap data
btc_mc   = request.security("CRYPTOCAP:BTC",   timeframe.period, close)
total_mc = request.security("CRYPTOCAP:TOTAL", timeframe.period, close)
usdt_mc  = request.security("CRYPTOCAP:USDT",  timeframe.period, close)
usdc_mc  = request.security("CRYPTOCAP:USDC",  timeframe.period, close)

// Adjusted dominance - total market cap minus USDT and USDC
adj_total = total_mc - usdt_mc - usdc_mc
adj_dom   = btc_mc / adj_total * 100

// Standard BTC.D for comparison
std_dom = request.security("CRYPTOCAP:BTC.D", timeframe.period, close)

// Zone colour for adjusted line (same thresholds as BTC Dominance Zones)
adj_col = adj_dom > 60 ? color.new(#cc3333, 0) :
          adj_dom > 50 ? color.new(#cc8833, 0) :
          adj_dom > 45 ? color.new(#aaaa33, 0) :
          adj_dom > 38 ? color.new(#33aa55, 0) :
                         color.new(#8833aa, 0)

// Plots
p_adj = plot(adj_dom, title="Adjusted BTC.D (excl. USDT+USDC)", color=adj_col, linewidth=2)
p_std = plot(std_dom, title="Standard BTC.D",                   color=color.new(color.gray, 40), linewidth=1)

// Fill between the two lines - gap represents stablecoin dominance
fill(p_adj, p_std, color=color.new(color.blue, 88), title="Stablecoin Gap")

// Zone reference lines
hline(60, "60% - Bear",           color=color.new(color.red,    50), linestyle=hline.style_dashed, linewidth=1)
hline(50, "50% - Early Bull",     color=color.new(color.orange, 50), linestyle=hline.style_dashed, linewidth=1)
hline(45, "45% - Mid Bull",       color=color.new(color.yellow, 50), linestyle=hline.style_dashed, linewidth=1)
hline(38, "38% - Altcoin Season", color=color.new(color.green,  50), linestyle=hline.style_dashed, linewidth=1)

// Current value labels on last bar
if barstate.islast
    label.new(bar_index + 2, adj_dom,
              "Adj " + str.tostring(math.round(adj_dom, 1)) + "%",
              style=label.style_label_left, color=color.new(color.orange, 20),
              textcolor=color.white, size=size.small)
    label.new(bar_index + 2, std_dom,
              "Std " + str.tostring(math.round(std_dom, 1)) + "%",
              style=label.style_label_left, color=color.new(color.gray, 30),
              textcolor=color.white, size=size.small)

Paste into a plain text editor first, then into TradingView Pine Editor. Add to chart on CRYPTOCAP:BTC.D.

Momentum
Speedometer — Kinetic Energy Momentum
Chart: Any chart · Any TF

Most momentum indicators measure where price is — RSI, MACD, stochastics all tell you whether price is high or low relative to history. The Speedometer measures something different: how fast the move is going and whether that speed is increasing or collapsing. Think of it like a car’s rev counter — a bright bar means the engine is revving up, a faded bar means it’s running out of fuel. It combines rate-of-change (velocity) with relative volume, squaring the velocity so that conviction moves register strongly while low-volume noise stays small. The result is normalised to ±100 so it reads consistently across any instrument and timeframe. The key edge is divergence: when price makes a new high but the Speedometer does not, the move is already burning out before the chart shows it.

Speedometer Kinetic Energy Momentum on BTC daily chart
Click to zoom
Parameter tuning
Use case Velocity Vol SMA Smoothing Norm Signal Pivot
Scalping (5–15 min) 5 20 2 50 2 3
Intraday (1H–4H) 10 20 5 100 3 5
Swing trading (Daily+) 14 20 7 200 3 8

Intraday settings are the defaults. Shorter velocity + lower normalisation = faster response but more noise. Longer pivot strength = fewer divergence signals but higher quality.

Bright green — engine revving up, safe to follow the move
Faded green — upside energy burning out, caution
Bright red — selling pressure building, move has conviction
Faded red — downside energy burning out, potential bounce
Yellow line — signal line, crossovers confirm direction change
d Bearish div — price new high, Speedometer lower high: move is bluffing
d Bullish div — price new low, Speedometer higher low: sellers losing grip
//@version=5
indicator("Speedometer - Kinetic Energy Momentum", shorttitle="KEM", overlay=false)

// ─── Inputs ───────────────────────────────────────────────────────────────────
vel_len    = input.int(10,  "Velocity length (bars)",  minval=2,  group="Calculation")
vol_len    = input.int(20,  "Volume SMA length",        minval=5,  group="Calculation")
smooth_len = input.int(5,   "EMA smoothing",            minval=1,  group="Calculation")
norm_len   = input.int(100, "Normalisation lookback",   minval=20, group="Calculation")
sig_len    = input.int(3,   "Signal line (EMA)",        minval=1,  group="Calculation")
pivot_len  = input.int(5,   "Pivot strength (bars)",    minval=2,  group="Divergence")

// ─── Velocity & relative volume ──────────────────────────────────────────────
// Velocity: rate of price change over N bars (%)
velocity  = ta.roc(close, vel_len)

// Relative volume: how active is this bar vs the rolling average?
rel_vol   = volume / ta.sma(volume, vol_len)

// ─── Kinetic Energy ───────────────────────────────────────────────────────────
// KE = rel_volume x velocity^2  — sign preserved for direction
// Squaring velocity amplifies large moves; volume acts as a noise filter.
// A fast move on low volume is weak signal. Fast move + high volume = real energy.
ke_raw    = rel_vol * velocity * math.abs(velocity)

// Light EMA smoothing to reduce bar-to-bar noise
ke_smooth = ta.ema(ke_raw, smooth_len)

// ─── Normalisation to +/-100 ─────────────────────────────────────────────────
// Scales against the max absolute reading in the lookback window
// so the indicator reads consistently across instruments and timeframes.
ke_max    = ta.highest(math.abs(ke_smooth), norm_len)
ke_norm   = ke_max != 0 ? (ke_smooth / ke_max) * 100 : 0.0

// ─── Signal line & acceleration state ────────────────────────────────────────
signal    = ta.ema(ke_norm, sig_len)

// Accelerating: bar is moving away from zero (energy building)
// Decelerating: bar is moving toward zero (energy fading)
accel_up  = ke_norm > ke_norm[1] and ke_norm > 0
accel_dn  = ke_norm < ke_norm[1] and ke_norm < 0

// ─── Colour logic ─────────────────────────────────────────────────────────────
// Bright colour = accelerating  |  Faded colour = decelerating
c_bull_strong = color.new(#00e676, 0)
c_bull_weak   = color.new(#00e676, 60)
c_bear_strong = color.new(#ff1744, 0)
c_bear_weak   = color.new(#ff1744, 60)

bar_col = ke_norm >= 0 ? (accel_up ? c_bull_strong : c_bull_weak) : (accel_dn ? c_bear_strong : c_bear_weak)

// ─── Plots ────────────────────────────────────────────────────────────────────
hline(  0, "Zero", color=color.new(color.gray, 30), linewidth=1)
hline( 60, "+60",  color=color.new(color.gray, 70), linestyle=hline.style_dashed)
hline(-60, "-60",  color=color.new(color.gray, 70), linestyle=hline.style_dashed)

plot(ke_norm, "KEM",    style=plot.style_histogram, color=bar_col, linewidth=2)
plot(signal,  "Signal", color=color.new(color.yellow, 20), linewidth=1)

// ─── Divergence detection via pivot swing points ──────────────────────────────
// Uses confirmed swing highs/lows — only fires when price and KEM disagree
// at real turning points, not on every new N-bar extreme.
//
// Bear divergence: price makes a higher swing high, KEM makes a lower swing high
// Bull divergence: price makes a lower swing low,  KEM makes a higher swing low
//
// Pivots are confirmed pivot_len bars after the swing point, so the label
// appears pivot_len bars to the right of the actual peak/trough.

ph = ta.pivothigh(close,   pivot_len, pivot_len)  // confirmed swing high in price
pl = ta.pivotlow(close,    pivot_len, pivot_len)  // confirmed swing low  in price
kh = ta.pivothigh(ke_norm, pivot_len, pivot_len)  // confirmed swing high in KEM
kl = ta.pivotlow(ke_norm,  pivot_len, pivot_len)  // confirmed swing low  in KEM

var float last_ph = na
var float last_kh = na
var float last_pl = na
var float last_kl = na

bear_div = false
bull_div = false

if not na(ph)
    if not na(last_ph) and ph > last_ph and not na(last_kh) and kh < last_kh
        bear_div := true   // higher price high, lower KEM high
    last_ph := ph

if not na(kh)
    last_kh := kh

if not na(pl)
    if not na(last_pl) and pl < last_pl and not na(last_kl) and kl > last_kl
        bull_div := true   // lower price low, higher KEM low
    last_pl := pl

if not na(kl)
    last_kl := kl

plotshape(bear_div, "Bearish divergence", style=shape.labeldown, location=location.top,    color=color.new(#ff1744, 20), text="div", textcolor=color.white, size=size.small)
plotshape(bull_div, "Bullish divergence", style=shape.labelup,   location=location.bottom, color=color.new(#00e676, 20), text="div", textcolor=color.white, size=size.small)

Paste into a plain text editor first, then into TradingView Pine Editor. Works on any chart and any timeframe.

Multi-Timeframe
MA Confluence — Multi-TF
Chart: Any chart · Any TF

Most traders already use moving averages. The problem is that a bullish setup on the 1H chart means nothing if the daily is pointed the other way. This indicator solves that by showing the MA alignment across all your timeframes simultaneously — in a single compact table that sits quietly in the corner of your chart. The signal logic is deliberately simple: Bullish when price is above the fast MA and the fast MA is above the slow MA. Bearish when the opposite is true. Neutral everything else. No black box, no proprietary weighting — just the same MA logic applied across 6 timeframes at once. The sweet spot is when 3 or more timeframes agree. That is confluence.

MA Confluence Multi-TF indicator table on TradingView
Click to zoom
Bullish — price above fast MA, fast above slow
Bearish — price below fast MA, fast below slow
Neutral — MAs tangled or price between them
Blue line — Fast MA (current TF)
Orange line — Slow MA (current TF)
//@version=5
indicator("MA Confluence - Multi-TF", shorttitle="MA Conf", overlay=true)

// ── INPUTS ─────────────────────────────────────────────────────────────────────
fast_len = input.int(20,  "Fast MA period", minval=1)
slow_len = input.int(200, "Slow MA period", minval=1)
use_ema  = input.bool(false, "Use EMA  (default: SMA)")
show_mas = input.bool(true,  "Show MA lines on chart (current TF)")

// Timeframe toggles + selectors (checkbox and TF picker on same line)
tf1_on = input.bool(true, "", inline="tf1", group="Timeframes")
tf1    = input.timeframe("2",   "2min",  inline="tf1", group="Timeframes")
tf2_on = input.bool(true, "", inline="tf2", group="Timeframes")
tf2    = input.timeframe("5",   "5min",  inline="tf2", group="Timeframes")
tf3_on = input.bool(true, "", inline="tf3", group="Timeframes")
tf3    = input.timeframe("15",  "15min", inline="tf3", group="Timeframes")
tf4_on = input.bool(true, "", inline="tf4", group="Timeframes")
tf4    = input.timeframe("60",  "1H",    inline="tf4", group="Timeframes")
tf5_on = input.bool(true, "", inline="tf5", group="Timeframes")
tf5    = input.timeframe("240", "4H",    inline="tf5", group="Timeframes")
tf6_on = input.bool(true, "", inline="tf6", group="Timeframes")
tf6    = input.timeframe("D",   "D",     inline="tf6", group="Timeframes")

// ── MA FUNCTIONS ───────────────────────────────────────────────────────────────
f_fast() => use_ema ? ta.ema(close, fast_len) : ta.sma(close, fast_len)
f_slow() => use_ema ? ta.ema(close, slow_len) : ta.sma(close, slow_len)

// ── DATA (request.security must be called unconditionally) ─────────────────────
[c1, ma1f, ma1s] = request.security(syminfo.tickerid, tf1, [close, f_fast(), f_slow()])
[c2, ma2f, ma2s] = request.security(syminfo.tickerid, tf2, [close, f_fast(), f_slow()])
[c3, ma3f, ma3s] = request.security(syminfo.tickerid, tf3, [close, f_fast(), f_slow()])
[c4, ma4f, ma4s] = request.security(syminfo.tickerid, tf4, [close, f_fast(), f_slow()])
[c5, ma5f, ma5s] = request.security(syminfo.tickerid, tf5, [close, f_fast(), f_slow()])
[c6, ma6f, ma6s] = request.security(syminfo.tickerid, tf6, [close, f_fast(), f_slow()])

// ── SIGNAL LOGIC ───────────────────────────────────────────────────────────────
// Bullish:  price > fast MA > slow MA
// Bearish:  price < fast MA < slow MA
// Neutral:  anything else
sig(c, f, s) => c > f and f > s ? 1 : c < f and f < s ? -1 : 0

s1 = sig(c1, ma1f, ma1s)
s2 = sig(c2, ma2f, ma2s)
s3 = sig(c3, ma3f, ma3s)
s4 = sig(c4, ma4f, ma4s)
s5 = sig(c5, ma5f, ma5s)
s6 = sig(c6, ma6f, ma6s)

// ── MA LINES (current chart TF only) ──────────────────────────────────────────
// Mid-tone blue and orange — visible on both dark and light chart themes
plot(show_mas ? f_fast() : na, "Fast MA", color=color.new(#5599dd, 0), linewidth=1)
plot(show_mas ? f_slow() : na, "Slow MA", color=color.new(#dd9944, 0), linewidth=2)

// ── COLOURS ────────────────────────────────────────────────────────────────────
c_bull_bg = #77cc77
c_bull_fg = #004400
c_bear_bg = #ee7777
c_bear_fg = #440000
c_neut_bg = #4a4a4a
c_neut_fg = #eeeeee
c_off_bg  = #2a2a2a
c_off_fg  = #444444
c_head_bg = #1a1a2e
c_head_fg = #888899

sig_bg(s)  => s ==  1 ? c_bull_bg : s == -1 ? c_bear_bg : c_neut_bg
sig_fg(s)  => s ==  1 ? c_bull_fg : s == -1 ? c_bear_fg : c_neut_fg
sig_txt(s) => s ==  1 ? "Bullish"  : s == -1 ? "Bearish"  : "Neutral"

// Human-readable TF labels
tf_label(tf) => tf == "1" ? "1min" : tf == "2" ? "2min" : tf == "3" ? "3min" : tf == "5" ? "5min" : tf == "10" ? "10min" : tf == "15" ? "15min" : tf == "30" ? "30min" : tf == "60" ? "1H" : tf == "120" ? "2H" : tf == "240" ? "4H" : tf == "D" ? "Daily" : tf == "W" ? "Weekly" : tf

// ── TABLE ──────────────────────────────────────────────────────────────────────
if barstate.islast
    var table tbl = table.new(position.top_right, 2, 7,
      bgcolor=color.new(#111118, 0),
      border_color=color.new(#2a2a40, 0), border_width=1,
      frame_color=color.new(#2a2a40, 0), frame_width=1)

    // Header row
    ma_type = use_ema ? "EMA" : "SMA"
    header  = ma_type + str.tostring(fast_len) + " / " + ma_type + str.tostring(slow_len)
    table.cell(tbl, 0, 0, "TF",   bgcolor=c_head_bg, text_color=c_head_fg, text_size=size.small, text_halign=text.align_left)
    table.cell(tbl, 1, 0, header, bgcolor=c_head_bg, text_color=c_head_fg, text_size=size.small)

    // Data rows
    tfs  = array.from(tf1,    tf2,    tf3,    tf4,    tf5,    tf6)
    sigs = array.from(s1,     s2,     s3,     s4,     s5,     s6)
    ons  = array.from(tf1_on, tf2_on, tf3_on, tf4_on, tf5_on, tf6_on)

    for i = 0 to 5
        on  = array.get(ons,  i)
        s   = array.get(sigs, i)
        tf  = array.get(tfs,  i)
        bg  = on ? sig_bg(s)  : c_off_bg
        fg  = on ? sig_fg(s)  : c_off_fg
        txt = on ? sig_txt(s) : "--"
        table.cell(tbl, 0, i + 1, tf_label(tf), bgcolor=c_head_bg, text_color=c_head_fg, text_size=size.small, text_halign=text.align_left)
        table.cell(tbl, 1, i + 1, txt,           bgcolor=bg,        text_color=fg,        text_size=size.small)

Paste into a plain text editor first, then into TradingView Pine Editor. Works on any chart and any timeframe.

More scripts added regularly. Each one is built to complement a Tactics article.

TradFiDefi

Advanced market analysis at the intersection of traditional finance and blockchain technology.

Explore
About
Connect
Follow on X Read on Substack
Cookie settings | Affiliate disclosure
Rulla till toppen
Curated Resources
Affiliate disclosure ↗
Exchanges
Hyperliquid Decentralised

The leading on-chain perpetual exchange. No KYC, deep liquidity, native token incentives.

Open account →
Bybit Centralised

Broad market access, strong derivatives offering and competitive fees for active traders.

Open account →
Apex Protocol Decentralised

ZK-rollup perpetuals with cross-margin portfolio system and multi-chain deposit support.

Open account →
Lighter Decentralised

Fully on-chain verifiable order book. Zero trading fees and up to 252 API keys per account.

Open account →
Wallets
Ledger Hardware

The market-leading hardware wallet. Cold storage for long-term holdings with broad asset support.

View devices →
Trezor Hardware

Open-source hardware wallet with a strong security track record. Good alternative to Ledger.

View devices →
Tangem Hardware

Card-format hardware wallet. No seed phrase by design — a different security model worth understanding.

View devices →
Services
Glassnode On-Chain Data

The gold standard for Bitcoin and Ethereum on-chain analytics. MVRV, SOPR, LTH supply and more.

Explore platform →
TradingView Charting

The industry standard for crypto charting. Cross-asset coverage, custom indicators and alerts.

Start charting →
CoinGlass Derivatives Data

Open interest, funding rates and liquidation maps across all major exchanges in one dashboard.

Explore platform →