Markets & data

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

3 min read

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.

$SPX Daily • Martin Luk Triggers v1.2.0 ADR Floor: 0.5% | AVWAP Active

The Anatomy of the Setup: Volatility Contraction & Momentum

… The strategy rests on three foundational pillars:

  1. 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.

  2. 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.

  3. 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);
Not investment advice. Posts on this site are educational and record my own experiments. Do your own research before trading.