Why Multi-Timeframe Analysis Matters
Looking at a single timeframe is like checking the weather by sticking your hand out the window. You know whether it is raining right now, but you have no idea if a storm front is approaching or if the sun is about to break through. Multi-timeframe analysis solves this by giving you the broader context that a single chart cannot provide.
The idea is straightforward: a pullback on the 15-minute chart might be a trend continuation on the 4-hour chart. A breakout on the 1-hour might be noise on the daily. When the trend direction aligns across multiple timeframes, the probability of a trade working out increases meaningfully. When they disagree, you should be cautious or stay flat.
I built a dashboard that shows trend direction across five timeframes simultaneously, using EMA position, RSI level, and SuperTrend direction as the trend signals. It sits in the corner of my chart and tells me at a glance whether the timeframes agree. Here is how I built it with Claude Code, including the prompts that worked and the ones that did not.
What You Will Build
A TradingView Pine Script v5 indicator that displays a table on your chart showing:
- Five configurable timeframes (default: 15m, 1H, 4H, 1D, 1W)
- For each timeframe: EMA trend (price vs. 50 EMA), RSI zone (bullish/neutral/bearish), and SuperTrend direction
- Color-coded cells: green for bullish, red for bearish, gray for neutral
- An overall alignment score that tells you how many timeframes agree
Prerequisites
- A free or paid TradingView account
- Claude Code installed
- Basic familiarity with TradingView’s Pine Editor
Prompt Iteration 1: The Naive Attempt
My first prompt to Claude Code was broad and underspecified:
Build a Pine Script v5 indicator that shows trend direction across multiple timeframes in a table on the chart. Use EMAs, RSI, and SuperTrend. Color code it green for bullish and red for bearish.
Claude Code produced a working script on the first try, but it had problems. The table was enormous — it used size.large for all cells and covered a quarter of the chart. It hardcoded the timeframes to 5m, 15m, 1H, 4H, and 1D with no way to change them. The SuperTrend calculation used request.security() with lookahead=barmerge.lookahead_on, which introduces future data leakage — a common mistake that AI models make with Pine Script’s security function. The RSI thresholds were fixed at 70/30, which is too aggressive for a trend dashboard where you want zone classification rather than overbought/oversold signals.
The code ran without errors, which is the deceptive part. If you did not know about the lookahead issue, you would think the dashboard was working correctly. It would even look accurate on historical data — because it was cheating by peeking at future bars.
Prompt Iteration 2: Fixing the Issues
I gave Claude Code a much more specific follow-up:
Rewrite the multi-timeframe dashboard with these fixes:
- Remove lookahead from all request.security() calls — use barmerge.lookahead_off
- Make all five timeframes configurable via input.timeframe()
- Use size.small for table cells and position the table in the top-right corner
- RSI zones should be: above 55 = bullish, below 45 = bearish, between = neutral
- Add an alignment row at the bottom that counts how many timeframes are bullish vs bearish
- SuperTrend parameters: ATR period 10, multiplier 3.0, both configurable
- EMA period should be configurable, default 50
This produced a much better result. The lookahead issue was fixed. The table sizing was reasonable. But the alignment calculation had a bug — it was counting neutral readings as bullish, inflating the bullish count. One more round of prompting fixed it.
Prompt Iteration 3: Final Polish
Fix the alignment calculation — neutral readings should not count as bullish or bearish. Also add column headers to the table (Timeframe, EMA, RSI, SuperTrend) and show the alignment as “4/5 Bullish” or “2/5 Bearish” format. Use a tooltip on each cell explaining the value.
This produced the final version below.
The Full Pine Script v5 Code
//@version=5
indicator("MTF Trend Dashboard", overlay=true)
// ─── Inputs ───────────────────────────────────────────
tf1 = input.timeframe("15", "Timeframe 1")
tf2 = input.timeframe("60", "Timeframe 2")
tf3 = input.timeframe("240", "Timeframe 3")
tf4 = input.timeframe("D", "Timeframe 4")
tf5 = input.timeframe("W", "Timeframe 5")
emaPeriod = input.int(50, "EMA Period", minval=5, maxval=200)
rsiPeriod = input.int(14, "RSI Period", minval=2, maxval=50)
rsiBullish = input.float(55.0, "RSI Bullish Threshold", minval=50, maxval=80)
rsiBearish = input.float(45.0, "RSI Bearish Threshold", minval=20, maxval=50)
atrPeriod = input.int(10, "SuperTrend ATR Period", minval=1, maxval=50)
stMultiplier = input.float(3.0, "SuperTrend Multiplier", minval=0.5, maxval=10.0, step=0.1)
tablePos = input.string("Top Right", "Table Position",
options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"])
// ─── Helper: Table Position ──────────────────────────
getPos(p) =>
switch p
"Top Right" => position.top_right
"Top Left" => position.top_left
"Bottom Right" => position.bottom_right
"Bottom Left" => position.bottom_left
// ─── Helper: Trend Calculations ──────────────────────
calcEmaSignal(src, len) =>
emaVal = ta.ema(src, len)
src > emaVal ? 1 : -1
calcRsiSignal(src, len, bullTh, bearTh) =>
rsiVal = ta.rsi(src, len)
rsiVal > bullTh ? 1 : rsiVal < bearTh ? -1 : 0
calcSuperTrendSignal(atrLen, mult) =>
atr = ta.atr(atrLen)
up = hl2 - (mult * atr)
dn = hl2 + (mult * atr)
var float finalUp = na
var float finalDn = na
var int dir = 1
finalUp := na(finalUp[1]) ? up : up > finalUp[1] ? up : close[1] > finalUp[1] ? up : finalUp[1]
finalDn := na(finalDn[1]) ? dn : dn < finalDn[1] ? dn : close[1] < finalDn[1] ? dn : finalDn[1]
dir := close > finalDn[1] ? 1 : close < finalUp[1] ? -1 : nz(dir[1], 1)
dir
// ─── Fetch Signals Per Timeframe ─────────────────────
fetchSignals(tf) =>
emaSig = request.security(syminfo.tickerid, tf,
calcEmaSignal(close, emaPeriod), lookahead=barmerge.lookahead_off)
rsiSig = request.security(syminfo.tickerid, tf,
calcRsiSignal(close, rsiPeriod, rsiBullish, rsiBearish), lookahead=barmerge.lookahead_off)
stSig = request.security(syminfo.tickerid, tf,
calcSuperTrendSignal(atrPeriod, stMultiplier), lookahead=barmerge.lookahead_off)
[emaSig, rsiSig, stSig]
[ema1, rsi1, st1] = fetchSignals(tf1)
[ema2, rsi2, st2] = fetchSignals(tf2)
[ema3, rsi3, st3] = fetchSignals(tf3)
[ema4, rsi4, st4] = fetchSignals(tf4)
[ema5, rsi5, st5] = fetchSignals(tf5)
// ─── Color Helper ────────────────────────────────────
signalColor(sig) =>
sig > 0 ? color.new(color.green, 20) :
sig < 0 ? color.new(color.red, 20) :
color.new(color.gray, 40)
signalText(sig) =>
sig > 0 ? "▲" : sig < 0 ? "▼" : "—"
// ─── Alignment Calculation ───────────────────────────
countBull(e1, r1, s1, e2, r2, s2, e3, r3, s3, e4, r4, s4, e5, r5, s5) =>
count = 0
// For each TF, count as bullish if at least 2 of 3 signals are bullish
tf1Bull = (e1 > 0 ? 1 : 0) + (r1 > 0 ? 1 : 0) + (s1 > 0 ? 1 : 0)
tf2Bull = (e2 > 0 ? 1 : 0) + (r2 > 0 ? 1 : 0) + (s2 > 0 ? 1 : 0)
tf3Bull = (e3 > 0 ? 1 : 0) + (r3 > 0 ? 1 : 0) + (s3 > 0 ? 1 : 0)
tf4Bull = (e4 > 0 ? 1 : 0) + (r4 > 0 ? 1 : 0) + (s4 > 0 ? 1 : 0)
tf5Bull = (e5 > 0 ? 1 : 0) + (r5 > 0 ? 1 : 0) + (s5 > 0 ? 1 : 0)
count := (tf1Bull >= 2 ? 1 : 0) + (tf2Bull >= 2 ? 1 : 0) + (tf3Bull >= 2 ? 1 : 0) + (tf4Bull >= 2 ? 1 : 0) + (tf5Bull >= 2 ? 1 : 0)
count
bullCount = countBull(ema1, rsi1, st1, ema2, rsi2, st2, ema3, rsi3, st3, ema4, rsi4, st4, ema5, rsi5, st5)
// ─── Draw Table ──────────────────────────────────────
var table dashboard = table.new(getPos(tablePos), 5, 7,
border_width=1, border_color=color.new(color.gray, 60),
bgcolor=color.new(color.black, 30))
if barstate.islast
// Headers
table.cell(dashboard, 0, 0, "TF", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 70))
table.cell(dashboard, 1, 0, "EMA", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 70))
table.cell(dashboard, 2, 0, "RSI", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 70))
table.cell(dashboard, 3, 0, "ST", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 70))
// Timeframe labels
tfLabels = array.from(tf1, tf2, tf3, tf4, tf5)
emaArr = array.from(ema1, ema2, ema3, ema4, ema5)
rsiArr = array.from(rsi1, rsi2, rsi3, rsi4, rsi5)
stArr = array.from(st1, st2, st3, st4, st5)
for i = 0 to 4
row = i + 1
table.cell(dashboard, 0, row, array.get(tfLabels, i),
text_color=color.white, text_size=size.small,
bgcolor=color.new(color.gray, 60))
table.cell(dashboard, 1, row, signalText(array.get(emaArr, i)),
text_color=color.white, text_size=size.small,
bgcolor=signalColor(array.get(emaArr, i)),
tooltip="EMA " + str.tostring(emaPeriod) + ": " + (array.get(emaArr, i) > 0 ? "Price above" : "Price below"))
table.cell(dashboard, 2, row, signalText(array.get(rsiArr, i)),
text_color=color.white, text_size=size.small,
bgcolor=signalColor(array.get(rsiArr, i)),
tooltip="RSI: " + (array.get(rsiArr, i) > 0 ? "Above " + str.tostring(rsiBullish) : array.get(rsiArr, i) < 0 ? "Below " + str.tostring(rsiBearish) : "Neutral zone"))
table.cell(dashboard, 3, row, signalText(array.get(stArr, i)),
text_color=color.white, text_size=size.small,
bgcolor=signalColor(array.get(stArr, i)),
tooltip="SuperTrend: " + (array.get(stArr, i) > 0 ? "Bullish" : "Bearish"))
// Alignment row
alignText = str.tostring(bullCount) + "/5 Bullish"
alignColor = bullCount >= 4 ? color.new(color.green, 20) :
bullCount <= 1 ? color.new(color.red, 20) :
color.new(color.orange, 30)
table.cell(dashboard, 0, 6, "Align", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 60))
table.cell(dashboard, 1, 6, alignText, text_color=color.white, text_size=size.small, bgcolor=alignColor, tooltip="Number of timeframes with majority bullish signals")
table.cell(dashboard, 2, 6, "", bgcolor=alignColor)
table.cell(dashboard, 3, 6, "", bgcolor=alignColor)
// ─── Alerts ──────────────────────────────────────────
alertcondition(bullCount >= 4,
"Strong Bullish Alignment",
"MTF Dashboard: 4+ timeframes are bullish")
alertcondition(bullCount <= 1,
"Strong Bearish Alignment",
"MTF Dashboard: 4+ timeframes are bearish")
Understanding the Key Design Decisions
Why Three Signals Per Timeframe
Using a single indicator per timeframe is fragile. An EMA tells you trend direction but not momentum. RSI tells you momentum but not trend structure. SuperTrend tells you the adaptive trailing stop level but can lag during fast moves. By combining all three and requiring a majority (2 out of 3) to classify a timeframe as bullish or bearish, you get a more robust reading.
In practice, the most common disagreement is RSI sitting in the neutral zone while EMA and SuperTrend agree. This still counts as a bullish or bearish timeframe because two signals agree. The dashboard correctly handles this — it does not require unanimity, just a majority.
The lookahead Trap
This is worth calling out explicitly because it is the most dangerous mistake AI models make with request.security(). When you set lookahead=barmerge.lookahead_on, the function returns the value from the end of the higher timeframe bar, even when you are partway through it. On historical data, this looks like the indicator has predictive power. In real-time, it behaves differently because the higher timeframe bar has not closed yet.
Always use barmerge.lookahead_off unless you have a specific reason not to. When reviewing AI-generated Pine Script that uses request.security(), check this parameter first.
Alignment Score Logic
The alignment score counts how many of the five timeframes have a bullish majority. A reading of 4/5 or 5/5 means strong agreement across timeframes — this is when trend-following strategies have their highest edge. A reading of 2/5 or 3/5 means the market is mixed, and you should either reduce size or wait for clarity. A reading of 0/5 or 1/5 means strong bearish alignment.
I do not trade based on the alignment score alone. I use it as a filter: if alignment is 4/5 or 5/5 bullish, I only look for long setups. If it is 0/5 or 1/5, I only look for shorts or stay flat. The 2/5 and 3/5 zone is where I do nothing.
How to Customize for Different Trading Styles
Scalpers (1m-15m Timeframes)
Change the five timeframes to 1m, 5m, 15m, 1H, and 4H. Reduce the EMA period to 20 and set RSI thresholds to 52/48 for tighter zone classification. The dashboard updates more frequently on lower timeframes, so you get faster signals — but also more noise. The alignment score becomes less reliable because lower timeframes flip faster.
Swing Traders (4H-Weekly)
Use 4H, 1D, 3D, 1W, and 1M. Increase the EMA period to 100 and keep RSI thresholds at 55/45. This setup changes slowly, which is the point — you want to identify the dominant trend and trade in that direction. The alignment score on these timeframes is more meaningful because each timeframe represents a larger sample of market behavior.
Crypto vs. Forex vs. Stocks
Crypto benefits from a higher SuperTrend multiplier (3.5-4.0) due to higher volatility. The default 3.0 works for most forex pairs and stock indices. If you notice the SuperTrend column flipping too frequently, increase the multiplier. If it is too sticky and missing trend changes, decrease it.
What the Dashboard Does Not Tell You
This dashboard answers one question: “What is the trend direction across timeframes?” It does not tell you when to enter, what your stop loss should be, or how much to risk. It is a filter, not a signal generator.
I have seen traders treat multi-timeframe alignment as a buy signal and get burned. All five timeframes can be bullish and price can still drop 5% on an intraday liquidation cascade. The alignment tells you the direction of the trend, not the timing of entries.
Use it alongside entry triggers: a pullback to a support level, a volume spike, an RSI divergence. The dashboard keeps you on the right side of the trend. Your entry trigger determines when you pull the trigger.
Claude Code vs. Writing This by Hand
The full dashboard is about 120 lines of Pine Script. Writing it by hand would take me 60-90 minutes, mostly spent on the request.security() calls and table layout. Claude Code produced a working version in three iterations over about 15 minutes of total prompting time.
The key insight from this build: Claude Code is strong at Pine Script structure and syntax but weak at knowing TradingView-specific gotchas like the lookahead parameter. You need to know what to check for. The AI generates code that compiles and runs but can be subtly wrong in ways that only matter in live trading.
My workflow: let Claude Code generate the first draft, then review every request.security() call, every var declaration, and every conditional that determines trading logic. Trust the syntax, verify the logic.
Adding It to TradingView
- Open TradingView and go to Pine Editor at the bottom
- Paste the full code and click Add to Chart
- The dashboard table appears in the top-right corner by default
- Open the indicator settings to adjust timeframes, EMA period, RSI thresholds, and SuperTrend parameters
- To set alerts, click the alert icon, select “MTF Trend Dashboard,” and choose either “Strong Bullish Alignment” or “Strong Bearish Alignment”
Start with the default settings and visually verify against the chart. If BTC has clearly been trending up on the daily and weekly, the dashboard should show those timeframes as bullish. If it does not, your parameters need adjustment.
Next Steps
- Add a trailing stop — Pair this dashboard with the Enhanced SuperTrend indicator for entries and exits that adapt to volatility.
- Automate the signals — Turn alignment alerts into trades with a Python DCA bot or a grid trading bot.
- Compare AI tools — See how Claude Code stacks up against Windsurf and Cursor for Pine Script in our AI tools comparison.