Untitled
unknown
plain_text
a year ago
12 kB
14
Indexable
//@version=6
strategy('SOL FT - HYE Combo Market [Strategy] (VWAP MR + Trend Hunter, HTF-gated, NR v2.1 — open date)', overlay = true, initial_capital = 1000, default_qty_value = 100, default_qty_type = strategy.percent_of_equity, commission_value = 0.025, calc_on_every_tick = false)
// ——— Validator gate (used by the append-only validator at the end)
var bool VALIDATOR_CAN_ENTER = true
// ── Inputs
// Mean Reversion
source = input.source(title = 'Source', defval = close, group = 'Mean Reversion Strategy Inputs')
smallcumulativePeriod = input.int(title = 'Small VWAP', defval = 8, group = 'Mean Reversion Strategy Inputs')
bigcumulativePeriod = input.int(title = 'Big VWAP', defval = 10, group = 'Mean Reversion Strategy Inputs')
meancumulativePeriod = input.int(title = 'Mean VWAP', defval = 50, group = 'Mean Reversion Strategy Inputs')
percentBelowToBuy = input.int(title = 'Percent below to buy %', defval = 2, group = 'Mean Reversion Strategy Inputs')
rsiPeriod = input.int(title = 'RSI Period', defval = 2, group = 'Mean Reversion Strategy Inputs')
rsiEmaPeriod = input.int(title = 'RSI EMA Period', defval = 5, group = 'Mean Reversion Strategy Inputs')
rsiLevelforBuy = input.int(title = 'Maximum RSI Level for Buy', defval = 30, group = 'Mean Reversion Strategy Inputs')
// Trend Hunter
slowtenkansenPeriod = input.int(9, minval = 1, title = 'Slow Tenkan Sen VWAP Len', group = 'Trend Hunter Strategy Inputs')
slowkijunsenPeriod = input.int(13, minval = 1, title = 'Slow Kijun Sen VWAP Len', group = 'Trend Hunter Strategy Inputs')
fasttenkansenPeriod = input.int(3, minval = 1, title = 'Fast Tenkan Sen VWAP Len', group = 'Trend Hunter Strategy Inputs')
fastkijunsenPeriod = input.int(7, minval = 1, title = 'Fast Kijun Sen VWAP Len', group = 'Trend Hunter Strategy Inputs')
BBlength = input.int(20, minval = 1, title = 'Bollinger Band Length', group = 'Trend Hunter Strategy Inputs')
BBmult = input.float(2.0, minval = 0.001, maxval = 50, title = 'Bollinger Band StdDev', group = 'Trend Hunter Strategy Inputs')
tsvlength = input.int(20, minval = 1, title = 'TSV Length', group = 'Trend Hunter Strategy Inputs')
tsvemaperiod = input.int(7, minval = 1, title = 'TSV EMA Length', group = 'Trend Hunter Strategy Inputs')
length = input.int(title = 'VIDYA Length', defval = 20, group = 'Trend Hunter Strategy Inputs')
src = input.source(title = 'VIDYA Source', defval = hl2, group = 'Trend Hunter Strategy Inputs')
// Risk / Regime
enableHTFTrendGate = input.bool(true, 'Enable Higher‑TF Trend Gate', group = 'Risk')
htfTF = input.string('240', 'Higher‑TF (e.g., 240 = 4h)', group = 'Risk')
enableShortTrend = input.bool(true, 'Enable Short Trend Leg in HTF Downtrend', group = 'Risk')
tHoldMax = input.int(96, 'Time Stop: max bars in trade (30m bars)', group = 'Risk')
// ── Date gate (opened by default)
inDateRange = true
// ── VIDYA (base TF)
getCMO(src_, length_) =>
mom = ta.change(src_)
upSum = math.sum(math.max(mom, 0), length_)
downSum = math.sum(-math.min(mom, 0), length_)
(upSum - downSum) / (upSum + downSum)
cmo = math.abs(getCMO(src, length))
alpha = 2 / (length + 1.0)
vidya = 0.0
vidya := src * alpha * cmo + nz(vidya[1]) * (1 - alpha * cmo)
// ── Mean Reversion calcs
tpS = (high + low + close) / 3
smallvwapValue = math.sum(tpS * volume, smallcumulativePeriod) / math.sum(volume, smallcumulativePeriod)
tpB = (high + low + close) / 3
bigvwapValue = math.sum(tpB * volume, bigcumulativePeriod) / math.sum(volume, bigcumulativePeriod)
tpM = (high + low + close) / 3
meanvwapValue = math.sum(tpM * volume, meancumulativePeriod) / math.sum(volume, meancumulativePeriod)
rsiValue = ta.rsi(source, rsiPeriod)
rsiEMA = ta.ema(rsiValue, rsiEmaPeriod)
buyMA = (100 - percentBelowToBuy) / 100.0 * bigvwapValue
inTrade = strategy.position_size != 0
notInTrade = strategy.position_size == 0
// ── MR entries/exits (prior‑bar signals, execute on bar close)
mrEnter = ta.crossunder(smallvwapValue[1], buyMA[1]) and rsiEMA[1] < rsiLevelforBuy and close[1] < meanvwapValue[1] and inDateRange and notInTrade
mrExit = close[1] > meanvwapValue[1] or not inDateRange
if barstate.isconfirmed and mrEnter and VALIDATOR_CAN_ENTER
strategy.entry('BUY-M', strategy.long)
if barstate.isconfirmed and mrExit
strategy.close('BUY-M')
// ── Trend Hunter core
sv_ten = math.sum((high + low + close) / 3 * volume, slowtenkansenPeriod) / math.sum(volume, slowtenkansenPeriod)
sv_kij = math.sum((high + low + close) / 3 * volume, slowkijunsenPeriod) / math.sum(volume, slowkijunsenPeriod)
fv_ten = math.sum((high + low + close) / 3 * volume, fasttenkansenPeriod) / math.sum(volume, fasttenkansenPeriod)
fv_kij = math.sum((high + low + close) / 3 * volume, fastkijunsenPeriod) / math.sum(volume, fastkijunsenPeriod)
slowtenkansen = math.avg(ta.lowest(sv_ten, slowtenkansenPeriod), ta.highest(sv_ten, slowtenkansenPeriod))
slowkijunsen = math.avg(ta.lowest(sv_kij, slowkijunsenPeriod), ta.highest(sv_kij, slowkijunsenPeriod))
slowleadLine = math.avg(slowtenkansen, slowkijunsen)
fasttenkansen = math.avg(ta.lowest(fv_ten, fasttenkansenPeriod), ta.highest(fv_ten, fasttenkansenPeriod))
fastkijunsen = math.avg(ta.lowest(fv_kij, fastkijunsenPeriod), ta.highest(fv_kij, fastkijunsenPeriod))
fastleadLine = math.avg(fasttenkansen, fastkijunsen)
// BB & TSV
BBleadLine = math.avg(fastleadLine, slowleadLine)
basis = ta.sma(BBleadLine, BBlength)
dev = BBmult * ta.stdev(BBleadLine, BBlength)
upper = basis + dev
lower = basis - dev
tsv = math.sum(close > close[1] ? volume * (close - close[1]) : close < close[1] ? volume * (close - close[1]) : 0, tsvlength)
tsvema = ta.ema(tsv, tsvemaperiod)
// ── Higher‑TF VIDYA slope (non‑repainting; closed HTF bars only)
vidyaHTF = request.security(syminfo.tickerid, htfTF, hl2 * (2 / (length + 1)) * math.abs((math.sum(math.max(ta.change(hl2), 0), length) - math.sum(-math.min(ta.change(hl2), 0), length)) / (math.sum(math.max(ta.change(hl2), 0), length) + math.sum(-math.min(ta.change(hl2), 0), length))), lookahead = barmerge.lookahead_off)
trendUpHTF = enableHTFTrendGate ? vidyaHTF[1] > vidyaHTF[2] : true
trendDownHTF = enableHTFTrendGate ? vidyaHTF[1] < vidyaHTF[2] : false
// ── Gates computed on prior bar
longTrendGate = trendUpHTF and tsv[1] > 0 and tsv[1] > tsvema[1] and fastleadLine[1] > fastleadLine[2] and slowleadLine[1] > slowleadLine[2]
shortTrendGate = trendDownHTF and tsv[1] < 0 and tsv[1] < tsvema[1] and fastleadLine[1] < fastleadLine[2] and slowleadLine[1] < slowleadLine[2]
// ── Entries (prior‑bar breakouts)
trendLongEnter = inDateRange and notInTrade and longTrendGate and close[1] > upper[1] and close[1] > vidya[1]
trendShortEnter = inDateRange and notInTrade and shortTrendGate and close[1] < lower[1] and close[1] < vidya[1]
if barstate.isconfirmed and trendLongEnter and VALIDATOR_CAN_ENTER
strategy.entry('BUY-T', strategy.long)
if barstate.isconfirmed and enableShortTrend and trendShortEnter and VALIDATOR_CAN_ENTER
strategy.entry('SELL-T', strategy.short)
// ── Exits (prior‑bar slope flips)
trendLongExit = fastleadLine[1] < fastleadLine[2] and slowleadLine[1] < slowleadLine[2] or not inDateRange
trendShortExit = fastleadLine[1] > fastleadLine[2] and slowleadLine[1] > slowleadLine[2] or not inDateRange
if barstate.isconfirmed and trendLongExit
strategy.close('BUY-T')
if barstate.isconfirmed and trendShortExit
strategy.close('SELL-T')
// ── Time stop (closed bars only)
var int barsInPos = 0
barsInPos := inTrade ? nz(barsInPos[1]) + 1 : 0
if barstate.isconfirmed and inTrade and barsInPos >= tHoldMax
strategy.close('BUY-M')
strategy.close('BUY-T')
strategy.close('SELL-T')
// ── Plot
plot(meanvwapValue, title = 'MEAN VWAP', linewidth = 2, color = color.new(color.yellow, 0))
// =====================================================================
// ===================== APPEND‑ONLY VALIDATOR (v4) =====================
// =====================================================================
groupV = 'Validator'
// Modes
val_mode = input.string(defval = 'Walk-Forward (IS/OOS)', title = 'Validator • Mode', options = ['Disabled', 'Walk-Forward (IS/OOS)', 'K-Fold CV (calendar)', 'K-Fold CV (bars)'], group = groupV)
// Window sizing
useMonths = input.bool(true, title = 'Use calendar months (WF only)?', group = groupV)
isLen = input.int(12, title = 'IS length (months/bars)', minval = 1, group = groupV)
oosLen = input.int(6, title = 'OOS length (months/bars)', minval = 1, group = groupV)
kFolds = input.int(6, title = 'K-Folds (2..12)', minval = 2, maxval = 12, group = groupV)
// Sharpe settings
shLen = input.int(30, title = 'Sharpe window (bars)', minval = 5, group = groupV)
annualK = input.float(365.0, title = 'Sharpe annualization factor (bars/yr)', group = groupV) // adjust per timeframe
// Visuals
paintOOS = input.bool(true, title = 'Shade OOS background', group = groupV)
showPanel = input.bool(true, title = 'Show OOS metrics panel', group = groupV)
// Calendar helpers
f_monthIndex(t) =>
year(t) * 12 + month(t)
var int firstMonth = na
firstMonth := na(firstMonth) ? f_monthIndex(time) : firstMonth
var int phaseStartMonth = na
phaseStartMonth := na(phaseStartMonth) ? f_monthIndex(time) : phaseStartMonth
monthsSinceStart = f_monthIndex(time) - phaseStartMonth
// Build IS/OOS mask
isPhaseInSample(mSince, isL, oosL) =>
block = isL + oosL
idx = mSince % block
idx < isL
inSample = val_mode == 'Walk-Forward (IS/OOS)' and useMonths ? isPhaseInSample(monthsSinceStart, isLen, oosLen) : val_mode == 'Walk-Forward (IS/OOS)' and not useMonths ? bar_index % (isLen + oosLen) < isLen : val_mode == 'K-Fold CV (calendar)' ? (f_monthIndex(time) - firstMonth) % kFolds != 0 : val_mode == 'K-Fold CV (bars)' ? bar_index % kFolds != 0 : true
outOfSample = not inSample
// Gate entries (applies from next bar, which avoids peeking in backtests)
canEnterNow = val_mode == 'Disabled' ? true : val_mode == 'Walk-Forward (IS/OOS)' ? outOfSample : val_mode == 'K-Fold CV (calendar)' ? outOfSample : val_mode == 'K-Fold CV (bars)' ? outOfSample : true
VALIDATOR_CAN_ENTER := canEnterNow
// OOS metrics (equity-based)
var float oosStartEquity = na
var float oosHiEquity = na
var float oosMaxDD = 0.0
var float oosRet = 0.0
oosBar = outOfSample
oosStartEquity := oosBar and na(oosStartEquity) ? strategy.equity : oosStartEquity
oosHiEquity := oosBar ? na(oosHiEquity) ? strategy.equity : math.max(oosHiEquity, strategy.equity) : oosHiEquity
oosRet := oosBar and not na(oosStartEquity) ? strategy.equity / oosStartEquity - 1.0 : oosRet
ddNow = oosBar and not na(oosHiEquity) ? strategy.equity / oosHiEquity - 1.0 : 0.0
oosMaxDD := oosBar ? math.min(oosMaxDD, ddNow) : oosMaxDD
// Simple per‑bar Sharpe on OOS; annualize with user factor
retSeries = oosBar and nz(strategy.equity[1]) != 0 ? (strategy.equity - strategy.equity[1]) / strategy.equity[1] : na
retMean = ta.sma(retSeries, shLen)
retStd = ta.stdev(retSeries, shLen)
oosSharpe = nz(retStd) > 0 ? retMean / retStd * math.sqrt(annualK) : na
// Optional chart shading for OOS
bgcolor(paintOOS and outOfSample and val_mode != 'Disabled' ? color.new(color.green, 92) : na)
// Minimal on‑chart panel (label)
var label panel = na
panelTxt = 'Validator — ' + val_mode + '\n' + 'OOS Return : ' + (oosRet != 0 ? str.tostring(oosRet * 100, '#.##') + '%' : '—') + '\n' + 'OOS MaxDD : ' + (oosMaxDD != 0 ? str.tostring(oosMaxDD * 100, '#.##') + '%' : '—') + '\n' + 'OOS Sharpe≈: ' + (na(oosSharpe) ? '—' : str.tostring(oosSharpe, '#.##')) + '\n' + 'Note: Gate applies next bar (no peeking).'
if showPanel
y = ta.highest(high, 100)
if na(panel)
panel := label.new(bar_index, y, panelTxt, style = label.style_label_right, textcolor = color.white, color = color.new(color.black, 0))
panel
if not na(panel)
label.set_x(panel, bar_index)
label.set_y(panel, y)
label.set_text(panel, panelTxt)
Editor is loading...
Leave a Comment