Decoding the Martin Luk Trading Triggers: Quantitative Analysis & ThinkScript Implementation

Momentum trading often lives in the crosshairs between discretionary chart pattern reading and systematic rule-based execution. One popular retail momentum methodology gaining traction is the high-performing swing breakout system outlined by trader Martin Luk, targeting explosive continuation setups out of tight price consolidations.
The Anatomy of the Setup: Volatility Contraction & Momentum
… The strategy rests on three foundational pillars:
-
Trend Alignment (Moving Average Confluence): Price must be trading above its rising key moving averages (the 10-day, 20-day, and 50-day simple moving averages). The 10 SMA serves as the primary short-term trailing guide, while the 20 and 50 SMAs establish intermediate-term trend stability.
-
Volatility Contraction & Tight Clustering: Before a sustainable expansion move occurs, volatility must compress. The script measures the High-Low spread over a short multi-day lookback window. When intra-bar ranges shrink relative to average range metrics—forming tight cluster pivots—it signals institutional absorption.
-
Breakout Trigger & Expansion Confirmation: An entry trigger is generated when price crosses above the multi-day resistance pivot on elevated volume, accompanied by an expanding daily range.
The Index Problem: ADR Constraints on $SPX
When applying equity breakout algorithms to index instruments like the S&P 500 ($SPX) or cash index futures, a primary stumbling block is the Average Daily Range (ADR) threshold.
In individual small-to-mid-cap momentum stocks, Luk typically looks for a minimum 5.0% ADR to guarantee sufficient liquidity and range expansion potential. However, broad market indices rarely trade with a 5% average daily range outside of extreme liquidity shocks (such as 2008 or early 2020). Normal index daily volatility typically hovers between 0.7% and 1.5%.
To adapt the indicator for index trading without breaking equity functionality:
- Configurable ADR Filter (
useAdrFilter): Allows traders to disable the 5.0% constraint when scanning index charts or broad ETFs. - Volume Proxy Fallbacks: Cash indices ($SPX,$NDX) do not report trade volume directly on standard feeds. The indicator falls back to underlying ETF proxy volume (e.g., $SPY volume metrics) or bypasses the volume threshold when calculating triggers on index tickers.
- Relative Range Expansion: Instead of requiring an absolute percentage move, the trigger evaluates the current day’s range relative to a rolling 14-period Average True Range (ATR).
ThinkScript Implementation (Martin_Luk_Triggers_v1_2_0)
The ThinkScript implementation below provides visual buy/sell breakout arrows, displays the dynamic 10/20/50 SMA bands, flags volatility contraction clusters, and plots trailing stop levels directly onto ThinkorSwim charts.
# Martin_Luk_Triggers_v1_2_0
# Quantitative Implementation of Martin Luk Momentum Breakout Triggers
# Target Asset: Equities & Broad Market Indices (SPX, SPY, QQQ)
input useAdrFilter = no;
input minAdrPercent = 5.0;
input clusterTolerance = 0.5;
input smaFastLength = 10;
input smaMedLength = 20;
input smaSlowLength = 50;
input volumeMultiplier = 1.2;
# Moving Averages
plot SMA10 = SimpleMovingAvg(close, smaFastLength);
plot SMA20 = SimpleMovingAvg(close, smaMedLength);
plot SMA50 = SimpleMovingAvg(close, smaSlowLength);
SMA10.SetDefaultColor(Color.YELLOW);
SMA20.SetDefaultColor(Color.CYAN);
SMA50.SetDefaultColor(Color.RED);
# Average Daily Range (ADR%)
def adr = (High - Low) / Close * 100;
def avgAdr = Average(adr, 20);
def adrPass = if useAdrFilter then avgAdr >= minAdrPercent else yes;
# Consolidation & Range Clustering (Tight Pivot Detection)
def rangeSpread = (Highest(High, 3) - Lowest(Low, 3)) / Close * 100;
def isTightCluster = rangeSpread <= (avgAdr * clusterTolerance);
# Volume Confirmation (with Index Fallback)
def avgVol = Average(volume, 20);
def volPass = if IsNaN(volume) or volume == 0 then yes else volume >= (avgVol * volumeMultiplier);
# Entry Trigger Logic
def breakoutLevel = Highest(High[1], 3);
def triggerCondition = Close > breakoutLevel and Close > SMA10 and SMA10 > SMA20 and SMA20 > SMA50 and isTightCluster[1] and adrPass and volPass;
plot BuySignal = triggerCondition and !triggerCondition[1];
BuySignal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
BuySignal.SetDefaultColor(Color.GREEN);
BuySignal.SetLineWeight(3);
# Risk Management: Trailing 10 SMA Guide
plot TrailingStop = if triggerCondition then Lowest(Low, 2) else Double.NaN;
TrailingStop.SetPaintingStrategy(PaintingStrategy.POINTS);
TrailingStop.SetDefaultColor(Color.MAGENTA);
# Alert Notifications
Alert(BuySignal, "Martin Luk Breakout Trigger Fired!", Alert.BAR, Sound.Bell);