//@version=6
strategy(
title = "Crypto TA Pro v6 - Long Term Hold Companion",
shorttitle = "CTAPv6-Hold",
overlay = true,
precision = 8,
max_bars_back = 2000,
max_labels_count = 300,
max_lines_count = 300,
max_boxes_count = 200,
pyramiding = 3,
initial_capital = 100,
default_qty_type = strategy.percent_of_equity,
default_qty_value = 33.33,
commission_type = strategy.commission.percent,
commission_value = 0.05,
slippage = 1,
margin_long = 100,
margin_short = 100
)
// -----------------------------------------------------------------------------
// CTAPv6 Long Term Hold Companion
// Purpose: accumulation/hold research, not quick-turn day trading.
// Difference from day version: no intraday max-time auto-exit, broader HTF filters,
// slower entries, optional DCA-style pyramiding, trend invalidation exits.
// Research only. Not financial advice.
//
// RESEARCH ASSUMPTIONS — not live-execution defaults:
// commission = 0.05% per side (Coinbase Advanced Trade maker rate; taker is ~0.08%).
// Adjust for your actual fee tier. Long-hold strategies should target maker fills;
// this value reflects a realistic accumulation scenario. Does not model rebates.
// slippage = 1 tick (TradingView "points"). For crypto tick = smallest price increment.
// Real market-order slippage on non-trivial size is typically larger; 1-tick understates cost.
// initial_capital = $100 (research-scale; NOT representative of live notional).
// default_qty_value = 33.33% of equity per trade (1/3-position sizing for long-hold legs).
// This represents a three-tranche accumulation model (initial + 2 DCA adds = full position).
// Use conservative sizing (5-15% per tranche) for live; 33.33% per leg is research-mode only.
// Live sizing must be set by the operator based on account size, risk tolerance, and exchange rules.
// Trade frequency: this script generates approximately 10-30 trades per year on daily/weekly charts.
// Backtest statistical significance is LOW at this frequency. Require at minimum 5+ years of
// history for any research conclusion and 30+ completed trades before considering promotion to
// paper or live. Short backtest windows (< 2 years) are insufficient for long-hold strategy research.
// These values affect absolute P&L and drawdown numbers but not signal timing or direction.
// -----------------------------------------------------------------------------
const string G_EXEC = "01. Execution"
const string G_ACC = "02. Accumulation Logic"
const string G_SR = "03. Support / Resistance"
const string G_REGIME = "03b. Multi-TF Regime Filter"
const string G_RISK = "04. Long-Hold Risk"
const string G_VIS = "05. Visuals"
startTime = input.time(1704067200000, "Research start", group = G_EXEC) // 2024-01-01 UTC
endTime = input.time(1893455940000, "Research end", group = G_EXEC) // 2029-12-31 UTC
confirmedOnly = input.bool(true, "Only act after confirmed bar close", group = G_EXEC)
allowShortResearch = input.bool(false, "Allow short research", group = G_EXEC)
fastLen = input.int(50, "Fast trend EMA", minval = 5, maxval = 500, group = G_ACC)
slowLen = input.int(200, "Slow trend EMA", minval = 20, maxval = 1000, group = G_ACC)
htfTf = input.timeframe("W", "Higher timeframe", group = G_ACC)
rsiLen = input.int(14, "RSI length", minval = 2, maxval = 100, group = G_ACC)
rsiAccumulationMax = input.float(45.0, "Accumulation RSI max", minval = 1, maxval = 70, group = G_ACC)
rsiDistributionMin = input.float(75.0, "Distribution RSI min", minval = 50, maxval = 99, group = G_ACC)
useDCA = input.bool(true, "Allow additional accumulation entries", group = G_ACC)
minBarsBetweenAdds = input.int(20, "Minimum bars between adds", minval = 1, maxval = 500, group = G_ACC)
// HTF repaint guard: true (default) uses prior confirmed HTF bar (lookahead_on + [1]); false uses live
// in-progress HTF bar (lookahead_off). Leave true for any live-order or replay fidelity use.
nonRepaintHTF = input.bool(true, "Use confirmed HTF values (repaint-safe)", group = G_ACC)
showSR = input.bool(true, "Show long-hold S/R zones", group = G_SR)
srLeft = input.int(10, "Pivot left", minval = 2, maxval = 100, group = G_SR)
srRight = input.int(10, "Pivot right", minval = 2, maxval = 100, group = G_SR)
srAtrMult = input.float(0.50, "Zone ATR thickness", minval = 0.01, maxval = 10, group = G_SR)
srExtend = input.int(500, "Zone extension bars", minval = 50, maxval = 1000, group = G_SR)
enableMtfRegime = input.bool(true, "Enable multi-TF regime cascade", group = G_REGIME)
ema4hLen = input.int(50, "4H EMA length", minval = 10, maxval = 200, group = G_REGIME)
emaDailyFastLen = input.int(50, "Daily fast EMA length", minval = 10, maxval = 200, group = G_REGIME)
emaDailySlowLen = input.int(200, "Daily slow EMA length", minval = 50, maxval = 500, group = G_REGIME)
mtfTransitionSize = input.float(0.5, "Position size multiplier during regime transition", minval = 0.1, maxval = 1.0, step = 0.1, group = G_REGIME)
reduceForWeekend = input.bool(true, "Reduce position 50% on Friday UTC close", group = G_RISK)
atrLen = input.int(14, "ATR length", minval = 1, group = G_RISK)
catastrophicStopAtr = input.float(6.0, "Catastrophic stop ATR", minval = 1.0, step = 0.25, group = G_RISK)
// Freeze semantics: true (default) captures ATR and catastrophic stop level on the entry bar so the
// stop zone does not drift as ATR evolves mid-hold. The zone reference stays anchored to entry-bar
// risk. Reset to na on position close so each accumulation leg starts from a clean baseline.
freezeRiskAtEntry = input.bool(true, "Freeze catastrophic stop ATR at entry", group = G_RISK)
exitOnSlowTrendBreak = input.bool(true, "Exit on slow trend break", group = G_RISK)
exitOnHTFBear = input.bool(true, "Exit on HTF bear regime", group = G_RISK)
usePartialDistribution = input.bool(false, "Use RSI distribution exit", group = G_RISK)
showDashboard = input.bool(true, "Show dashboard", group = G_VIS)
showZones = input.bool(true, "Show accumulation/distribution zones", group = G_VIS)
f_dema(series float src, simple int length) =>
float ema1 = ta.ema(src, length)
float ema2 = ta.ema(ema1, length)
2.0 * ema1 - ema2
f_add_box(array<box> arr, box b, int maxCount) =>
array.push(arr, b)
if array.size(arr) > maxCount
box.delete(array.shift(arr))
inRange = time >= startTime and time <= endTime
signalGate = not confirmedOnly or barstate.isconfirmed
fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
// HTF confirmation semantics:
// htfSlowNonRepaint — lookahead_on + [1]: prior confirmed HTF close; no intra-bar repaint (default).
// htfSlowRepainting — lookahead_off: live current HTF bar value; updates intra-bar (repaints on replay).
// nonRepaintHTF toggle selects which is used. Prefer confirmed variant for all live and replay analysis.
htfSlowNonRepaint = request.security(syminfo.tickerid, htfTf, ta.ema(close, slowLen)[1], barmerge.gaps_off, barmerge.lookahead_on)
htfSlowRepainting = request.security(syminfo.tickerid, htfTf, ta.ema(close, slowLen), barmerge.gaps_off, barmerge.lookahead_off)
htfSlow = nonRepaintHTF ? htfSlowNonRepaint : htfSlowRepainting
rsi = ta.rsi(close, rsiLen)
atr = ta.atr(atrLen)
// Multi-TF regime cascade (Bug 14 fix): 4H + Daily EMA alignment prevents slow single-EMA lag.
// lookahead_off on all HTF requests to avoid future-bar leakage in backtests.
ema4h = request.security(syminfo.tickerid, "240", ta.ema(close, ema4hLen), lookahead = barmerge.lookahead_off)
ema1dFast = request.security(syminfo.tickerid, "1D", ta.ema(close, emaDailyFastLen), lookahead = barmerge.lookahead_off)
ema1dSlow = request.security(syminfo.tickerid, "1D", ta.ema(close, emaDailySlowLen), lookahead = barmerge.lookahead_off)
mtfBull = enableMtfRegime ? (ema4h > ema1dFast and ema1dFast > ema1dSlow) : true
mtfBear = enableMtfRegime ? (ema4h < ema1dFast and ema1dFast < ema1dSlow) : false
mtfTransition = enableMtfRegime ? (not mtfBull and not mtfBear) : false
mtfSizeMultiplier = mtfTransition ? mtfTransitionSize : 1.0
trendBull = close > slow and fast > slow and slow >= slow[1]
trendBear = close < slow and fast < slow and slow <= slow[1]
htfBull = close > htfSlow and htfSlow >= htfSlow[1]
htfBear = close < htfSlow and htfSlow <= htfSlow[1]
pLow = ta.pivotlow(low, srLeft, srRight)
pHigh = ta.pivothigh(high, srLeft, srRight)
var float supportTop = na
var float supportBottom = na
var float resistanceTop = na
var float resistanceBottom = na
var array<box> sBoxes = array.new<box>()
var array<box> rBoxes = array.new<box>()
buf = atr * srAtrMult
if showSR and not na(pLow)
supportTop := pLow + buf
supportBottom := pLow - buf
box b = box.new(bar_index - srRight, supportTop, bar_index + srExtend, supportBottom, border_color = color.new(color.green, 25), bgcolor = color.new(color.green, 90), text = "HOLD SUPPORT", text_color = color.new(color.green, 0), text_size = size.tiny, text_halign = text.align_center, text_valign = text.align_center)
f_add_box(sBoxes, b, 20)
if showSR and not na(pHigh)
resistanceTop := pHigh + buf
resistanceBottom := pHigh - buf
box b = box.new(bar_index - srRight, resistanceTop, bar_index + srExtend, resistanceBottom, border_color = color.new(color.red, 25), bgcolor = color.new(color.red, 90), text = "HOLD RESIST", text_color = color.new(color.red, 0), text_size = size.tiny, text_halign = text.align_center, text_valign = text.align_center)
f_add_box(rBoxes, b, 20)
if showSR
if array.size(sBoxes) > 0
for i = 0 to array.size(sBoxes) - 1
box.set_right(array.get(sBoxes, i), bar_index + srExtend)
if array.size(rBoxes) > 0
for i = 0 to array.size(rBoxes) - 1
box.set_right(array.get(rBoxes, i), bar_index + srExtend)
nearSupport = not na(supportTop) and low <= supportTop and high >= supportBottom
nearResistance = not na(resistanceBottom) and high >= resistanceBottom and low <= resistanceTop
var int lastAddBar = na
canAdd = na(lastAddBar) or bar_index - lastAddBar >= minBarsBetweenAdds
baseLong = inRange and signalGate and trendBull and htfBull and rsi <= rsiAccumulationMax and nearSupport
addLong = useDCA and strategy.position_size > 0 and baseLong and canAdd
firstLong = strategy.position_size == 0 and baseLong
// Entry fill semantics: signal fires on bar *close* (gated by barstate.isconfirmed via signalGate).
// TradingView fills strategy.entry at the *open of the next bar* (market order, 1-tick slippage default).
// nearSupport zone check uses close vs. zone bounds; the actual fill is next-bar open, which may be
// slightly outside the zone. Pyramiding adds at most once per minBarsBetweenAdds bars.
// mtfSizeMultiplier scales the 33.33% base tranche down to mtfTransitionSize when the regime is in
// transition (neither full bull nor full bear across the 4H and Daily EMA cascade).
entryQty = strategy.equity * 0.3333 * mtfSizeMultiplier / close
if firstLong or addLong
strategy.entry("L-HOLD", strategy.long, qty = entryQty, comment = firstLong ? "Initial accumulation" : "DCA accumulation")
lastAddBar := bar_index
shortResearch = allowShortResearch and inRange and signalGate and trendBear and htfBear and rsi >= rsiDistributionMin and nearResistance and strategy.position_size == 0
if shortResearch
strategy.entry("S-HOLD", strategy.short, qty = entryQty, comment = "Long-hold short research")
// === RISK ENGINE (freeze-at-entry) ===
var float entryAtr = na
var float frozenStop = na
newLongPosition = strategy.position_size > 0 and strategy.position_size[1] <= 0
closedPosition = strategy.position_size == 0 and strategy.position_size[1] != 0
if newLongPosition
entryAtr := atr
if closedPosition
entryAtr := na
frozenStop := na
riskAtr = freezeRiskAtEntry ? nz(entryAtr, atr) : atr
avg = strategy.position_avg_price
longCatStop = avg - riskAtr * catastrophicStopAtr
shortCatStop = avg + riskAtr * catastrophicStopAtr
// Capture stop level on entry bar so zone reference does not drift with ATR during a long hold.
if newLongPosition
frozenStop := longCatStop
if strategy.position_size > 0
strategy.exit("XL Catastrophic", "L-HOLD", stop = longCatStop, comment_loss = "Catastrophic stop")
if strategy.position_size < 0
strategy.exit("XS Catastrophic", "S-HOLD", stop = shortCatStop, comment_loss = "Catastrophic stop")
longTrendExit = strategy.position_size > 0 and ((exitOnSlowTrendBreak and close < slow) or (exitOnHTFBear and htfBear) or (usePartialDistribution and rsi >= rsiDistributionMin))
shortTrendExit = strategy.position_size < 0 and ((exitOnSlowTrendBreak and close > slow) or (exitOnHTFBear and htfBull) or (usePartialDistribution and rsi <= rsiAccumulationMax))
if longTrendExit
strategy.close("L-HOLD", comment = "Hold invalidated")
if shortTrendExit
strategy.close("S-HOLD", comment = "Short hold invalidated")
if not inRange and strategy.position_size != 0
strategy.close_all(comment = "Outside research range")
// Weekend de-risking (Bug 15 fix): reduce to 50% on Friday UTC 22:00 close to avoid
// weekend funding-rate spikes on leveraged derivatives. No-op for spot unless explicitly enabled.
isFridayUtc22 = dayofweek(time, "UTC") == dayofweek.friday and hour(time, "UTC") >= 22
if strategy.position_size > 0 and reduceForWeekend and isFridayUtc22
strategy.close("L-HOLD", qty_percent = 50, comment = "WEEKEND_DERISK")
plot(fast, "Fast EMA", color = color.new(color.aqua, 0), linewidth = 2)
plot(slow, "Slow EMA", color = color.new(color.orange, 0), linewidth = 2)
plot(htfSlow, "HTF Slow EMA", color = color.new(color.purple, 0), linewidth = 2)
// Catastrophic stop displayed as frozen entry-bar level when freezeRiskAtEntry is on; live ATR-derived level otherwise.
float plotLongStop = strategy.position_size > 0 ? (freezeRiskAtEntry and not na(frozenStop) ? frozenStop : longCatStop) : na
plot(plotLongStop, "Long catastrophic stop", color = color.new(color.red, 0), style = plot.style_linebr)
plot(strategy.position_size < 0 ? shortCatStop : na, "Short catastrophic stop", color = color.new(color.red, 0), style = plot.style_linebr)
plotshape(firstLong, title = "Initial accumulation", location = location.belowbar, style = shape.labelup, text = "ACC", color = color.new(color.green, 0), textcolor = color.white, size = size.tiny)
plotshape(addLong, title = "Add accumulation", location = location.belowbar, style = shape.labelup, text = "ADD", color = color.new(color.teal, 0), textcolor = color.white, size = size.tiny)
plotshape(longTrendExit, title = "Hold exit", location = location.abovebar, style = shape.labeldown, text = "EXIT", color = color.new(color.orange, 0), textcolor = color.white, size = size.tiny)
bgcolor(showZones and trendBull and htfBull ? color.new(color.green, 94) : showZones and trendBear and htfBear ? color.new(color.red, 95) : na)
var table dash = table.new(position.top_right, 2, 7, border_width = 1)
if showDashboard and barstate.islast
table.cell(dash, 0, 0, "CTAPv6 Hold", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 0, strategy.position_size > 0 ? "Long" : strategy.position_size < 0 ? "Short" : "Flat", text_color = color.white, bgcolor = strategy.position_size > 0 ? color.new(color.green, 25) : strategy.position_size < 0 ? color.new(color.red, 25) : color.new(color.gray, 25))
table.cell(dash, 0, 1, "Trend", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 1, trendBull ? "Bull" : trendBear ? "Bear" : "Neutral", text_color = color.white, bgcolor = trendBull ? color.new(color.green, 25) : trendBear ? color.new(color.red, 25) : color.new(color.gray, 25))
table.cell(dash, 0, 2, "HTF", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 2, htfBull ? "HTF Bull" : htfBear ? "HTF Bear" : "HTF Neutral", text_color = color.white, bgcolor = htfBull ? color.new(color.green, 25) : htfBear ? color.new(color.red, 25) : color.new(color.gray, 25))
table.cell(dash, 0, 3, "RSI", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 3, str.tostring(rsi, "#.##"), text_color = color.white, bgcolor = rsi <= rsiAccumulationMax ? color.new(color.green, 25) : rsi >= rsiDistributionMin ? color.new(color.red, 25) : color.new(color.gray, 25))
table.cell(dash, 0, 4, "S/R", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 4, nearSupport ? "Near support" : nearResistance ? "Near resistance" : "Neutral", text_color = color.white, bgcolor = nearSupport ? color.new(color.green, 25) : nearResistance ? color.new(color.red, 25) : color.new(color.gray, 25))
table.cell(dash, 0, 5, "Adds", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 5, useDCA ? "DCA On" : "One entry", text_color = color.white, bgcolor = color.new(color.gray, 25))
table.cell(dash, 0, 6, "Range", text_color = color.white, bgcolor = color.new(color.black, 0))
table.cell(dash, 1, 6, inRange ? "ON" : "OFF", text_color = color.white, bgcolor = inRange ? color.new(color.green, 25) : color.new(color.red, 25))
Overview
Crypto TA Pro Long Term Hold Companion uses fast and slow trend EMAs, a weekly higher timeframe filter, an RSI accumulation threshold and a multi-timeframe regime cascade. Up to three entries can be added with a minimum bar gap, and positions can be cut around weekends. Exits include a wide ATR-based stop, a slow trend break and a bearish higher timeframe regime. It also draws support and resistance zones.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Research start
time
1704067200000
startTime
Research end
time
1893455940000
endTime
Only act after confirmed bar close
bool
true
confirmedOnly
Allow short research
bool
false
allowShortResearch
Fast trend EMA
int
50
fastLen
Slow trend EMA
int
200
slowLen
Higher timeframe
timeframe
W
htfTf
RSI length
int
14
rsiLen
Accumulation RSI max
float
45.0
rsiAccumulationMax
Distribution RSI min
float
75.0
rsiDistributionMin
Allow additional accumulation entries
bool
true
useDCA
Minimum bars between adds
int
20
minBarsBetweenAdds
Show all 32 settings
Setting
Type
Default
Variable
Use confirmed HTF values (repaint-safe)
bool
true
nonRepaintHTF
Show long-hold S/R zones
bool
true
showSR
Pivot left
int
10
srLeft
Pivot right
int
10
srRight
Zone ATR thickness
float
0.50
srAtrMult
Zone extension bars
int
500
srExtend
Enable multi-TF regime cascade
bool
true
enableMtfRegime
4H EMA length
int
50
ema4hLen
Daily fast EMA length
int
50
emaDailyFastLen
Daily slow EMA length
int
200
emaDailySlowLen
Position size multiplier during regime transition
float
0.5
mtfTransitionSize
Reduce position 50% on Friday UTC close
bool
true
reduceForWeekend
ATR length
int
14
atrLen
Catastrophic stop ATR
float
6.0
catastrophicStopAtr
Freeze catastrophic stop ATR at entry
bool
true
freezeRiskAtEntry
Exit on slow trend break
bool
true
exitOnSlowTrendBreak
Exit on HTF bear regime
bool
true
exitOnHTFBear
Use RSI distribution exit
bool
false
usePartialDistribution
Show dashboard
bool
true
showDashboard
Show accumulation/distribution zones
bool
true
showZones
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.
This trend-following strategy executes a classic Donchian channel breakout system designed to cross-validate backtest results against external framework benchmarks. It triggers a long entry when the closing price breaks above the highest high of the prior entr
Omar Edited WF is a trading strategy overlay built around Williams Fractals and multiple Exponential Moving Averages. It detects high and low fractal pivot points over configurable lookback periods alongside three adjustable EMA trendlines. Long entries are ma
This strategy calculates dynamic support and resistance levels using pivot highs and lows alongside a custom smoothed trend filter and a 3-period EMA. It identifies entry opportunities when price breaks out relative to the trend filter and short-term EMA cross
The Chained Linear Regression Reversal Strategy plots linear regression channels with upper and lower standard deviation bands directly on the price chart across a user-defined time window. When price breaks out of the static channel for a confirmed number of
This overlay strategy plots a two-timeframe Ichimoku setup on the price chart, with Tenkan, Kijun, Chikou, and Kumo from a base higher timeframe and an optional highest-timeframe cloud. It allows two entries in the same direction and uses ATR-based trailing st
This strategy combines three Exponential Moving Averages with Williams Fractals to execute trend-following or mean-reverting trades. It analyzes price structure using short, medium, and long-term EMAs along with fractal highs and lows. Entries trigger when fra