-
-
Notifications
You must be signed in to change notification settings - Fork 20
Description
//@Version=6,
strategy(title=johnnys_Scalp_Control)''
title=johnnys_Scalp_Control
shorttitle="10m, 15m, No-Chop, Volume-Aware")
overlay=true,
pyramiding=2,
calc_on_every_tick=false,
calc_on_order_fills=true,
initial_capital=10000,
commission_type=strategy.commission.percent,
commission_value=0.05,
slippage=1,
max_labels_count=500,
max_lines_count=500
//====================== Presets & Controls ======================
groupPresets = "Presets"
assetPreset = input.string("Auto", "Asset Preset", options=["Auto", "XRP", "XLM", "ADA", "Custom"], group=groupPresets)
controller = input.string("10m", "Controller (manual)", options=["10m", "15m"], group=groupPresets)
useKillzones = input.bool(true, "Limit to Killzones (London/NY overlap bias)", group=groupPresets)
// Exchange/asset multipliers (spread/ATR tuning)
spreadMult = switch assetPreset
"XRP" => 1.00
"XLM" => 1.10
"ADA" => 1.15
=> 1.00
//====================== Filters & Signal Params =================
groupFilters = "Filters"
adxLen = input.int(14, "ADX Len (15m Gate)", group=groupFilters)
adxMin = input.float(18.0, "Min ADX (trendiness)", step=0.5, group=groupFilters)
chopLen = input.int(14, "Choppiness Len (15m Gate)", group=groupFilters)
chopMax = input.float(45.0, "Max Choppiness (≤ trending)", step=0.5, group=groupFilters)
groupSig = "Signals"
rsiLen = input.int(14, "RSI Len", group=groupSig)
rsiOB = input.int(72, "RSI Overbought", group=groupSig)
rsiOS = input.int(28, "RSI Oversold", group=groupSig)
kLen = input.int(20, "Keltner EMA Len", group=groupSig)
kMult = input.float(1.6, "Keltner ATR Mult", step=0.1, group=groupSig)
groupVol = "Volume Flow"
vLen = input.int(20, "Vol MA Len", group=groupVol)
cmfLen = input.int(20, "CMF Len", group=groupVol)
mfiLen = input.int(14, "MFI Len", group=groupVol)
cmfMin = input.float(0.06, "CMF Min |±|", step=0.01, group=groupVol)
mfiMin = input.int(55, "MFI Long Min", group=groupVol)
mfiMax = input.int(45, "MFI Short Max", group=groupVol)
vSpikeK = input.float(1.25, "Volume Spike = v > K × vMA", step=0.05, group=groupVol)
groupRisk = "Risk / Exits"
atrLen = input.int(14, "ATR Len", group=groupRisk)
slMult = input.float(1.25, "Stop = ATR ×", step=0.05, group=groupRisk)
tpMult = input.float(1.85, "Target = ATR ×", step=0.05, group=groupRisk)
trailOn = input.bool(true, "Arm Trailing after RR≥1", group=groupRisk)
trailATR = input.float(1.00, "Trail = ATR ×", step=0.05, group=groupRisk)
cooldownBars = input.int(6, "Cooldown bars after exit", minval=0, group=groupRisk)
dailyStop = input.float(3.0, "Daily Loss Stop (%)", step=0.1, group=groupRisk)
groupViz = "Visuals"
show10Bands = input.bool(true, "Show 10m Keltner", group=groupViz)
showMarks = input.bool(true, "Mark Entries", group=groupViz)
//====================== Utility Functions ======================
choppiness(o, h, l, c, _len) =>
tr = math.max(h - l, math.max(math.abs(h - nz(c[1], c)), math.abs(l - nz(c[1], c))))
sumTr = ta.sum(tr, _len)
hh = ta.highest(h, _len)
ll = ta.lowest(l, _len)
100 * math.log10(sumTr / math.max(hh - ll, syminfo.mintick)) / math.log10(_len)
rejectionCandles(o, h, l, c) =>
body = math.abs(c - o)
upper = h - math.max(o, c)
lower = math.min(o, c) - l
bull = c > o and lower > body * 1.2 and upper <= body * 0.8
bear = o > c and upper > body * 1.2 and lower <= body * 0.8
[bull, bear]
// Killzones (London 10:00–13:00 UTC & NY 13:30–17:00 UTC approximations)
inSession(sess) =>
not useKillzones or not na(time(timeframe.period, sess))
sessLondon = "1000-1300"
sessNY = "1330-1700"
sessionOK = inSession(sessLondon) or inSession(sessNY)
//====================== 15m “No-Chop” Gate =====================
adx15 = request.security(syminfo.tickerid, "15", ta.adx(adxLen), barmerge.gaps_off, barmerge.lookahead_off)
chop15 = request.security(syminfo.tickerid, "15", choppiness(open, high, low, close, chopLen), barmerge.gaps_off, barmerge.lookahead_off)
gateOK = adx15 >= adxMin and chop15 <= chopMax
//====================== Volume Flow ============================
cmf10 = request.security(syminfo.tickerid, "10", ta.cmf(high, low, close, volume, cmfLen), barmerge.gaps_off, barmerge.lookahead_off)
cmf15 = request.security(syminfo.tickerid, "15", ta.cmf(high, low, close, volume, cmfLen), barmerge.gaps_off, barmerge.lookahead_off)
mfi10 = request.security(syminfo.tickerid, "10", ta.mfi(high, low, close, volume, mfiLen), barmerge.gaps_off, barmerge.lookahead_off)
mfi15 = request.security(syminfo.tickerid, "15", ta.mfi(high, low, close, volume, mfiLen), barmerge.gaps_off, barmerge.lookahead_off)
v10 = request.security(syminfo.tickerid, "10", volume, barmerge.gaps_off, barmerge.lookahead_off)
vma10 = request.security(syminfo.tickerid, "10", ta.sma(volume, vLen), barmerge.gaps_off, barmerge.lookahead_off)
vSpike10 = v10 > vma10 * vSpikeK
v15 = request.security(syminfo.tickerid, "15", volume, barmerge.gaps_off, barmerge.lookahead_off)
vma15 = request.security(syminfo.tickerid, "15", ta.sma(volume, vLen), barmerge.gaps_off, barmerge.lookahead_off)
vSpike15 = v15 > vma15 * vSpikeK
//====================== 10m Engine =============================
o10 = request.security(syminfo.tickerid, "10", open, barmerge.gaps_off, barmerge.lookahead_off)
h10 = request.security(syminfo.tickerid, "10", high, barmerge.gaps_off, barmerge.lookahead_off)
l10 = request.security(syminfo.tickerid, "10", low, barmerge.gaps_off, barmerge.lookahead_off)
c10 = request.security(syminfo.tickerid, "10", close, barmerge.gaps_off, barmerge.lookahead_off)
ema10 = request.security(syminfo.tickerid, "10", ta.ema(close, kLen), barmerge.gaps_off, barmerge.lookahead_off)
atr10 = request.security(syminfo.tickerid, "10", ta.atr(atrLen), barmerge.gaps_off, barmerge.lookahead_off)
kHi10 = ema10 + kMult * atr10
kLo10 = ema10 - kMult * atr10
rsi10 = request.security(syminfo.tickerid, "10", ta.rsi(close, rsiLen), barmerge.gaps_off, barmerge.lookahead_off)
[bBull10, bBear10] = request.security(syminfo.tickerid, "10", rejectionCandles(open, high, low, close), barmerge.gaps_off, barmerge.lookahead_off)
conf10 = request.security(syminfo.tickerid, "10", barstate.isconfirmed, barmerge.gaps_off, barmerge.lookahead_off)
long10 = conf10 and gateOK and sessionOK and (c10 <= kLo10) and (rsi10 <= rsiOS) and bBull10 and vSpike10 and cmf10 >= cmfMin and (mfi10 >= mfiMin or mfi15 >= mfiMin)
short10 = conf10 and gateOK and sessionOK and (c10 >= kHi10) and (rsi10 >= rsiOB) and bBear10 and vSpike10 and cmf10 <= -cmfMin and (mfi10 <= mfiMax or mfi15 <= mfiMax)
//====================== 15m Engine =============================
o15 = request.security(syminfo.tickerid, "15", open, barmerge.gaps_off, barmerge.lookahead_off)
h15 = request.security(syminfo.tickerid, "15", high, barmerge.gaps_off, barmerge.lookahead_off)
l15 = request.security(syminfo.tickerid, "15", low, barmerge.gaps_off, barmerge.lookahead_off)
c15 = request.security(syminfo.tickerid, "15", close, barmerge.gaps_off, barmerge.lookahead_off)
ema15 = request.security(syminfo.tickerid, "15", ta.ema(close, kLen), barmerge.gaps_off, barmerge.lookahead_off)
atr15 = request.security(syminfo.tickerid, "15", ta.atr(atrLen), barmerge.gaps_off, barmerge.lookahead_off)
kHi15 = ema15 + kMult * atr15
kLo15 = ema15 - kMult * atr15
rsi15 = request.security(syminfo.tickerid, "15", ta.rsi(close, rsiLen), barmerge.gaps_off, barmerge.lookahead_off)
[bBull15, bBear15] = request.security(syminfo.tickerid, "15", rejectionCandles(open, high, low, close), barmerge.gaps_off, barmerge.lookahead_off)
conf15 = request.security(syminfo.tickerid, "15", barstate.isconfirmed, barmerge.gaps_off, barmerge.lookahead_off)
long15 = conf15 and gateOK and sessionOK and (c15 <= kLo15) and (rsi15 <= rsiOS) and bBull15 and vSpike15 and cmf15 >= cmfMin and mfi15 >= mfiMin
short15 = conf15 and gateOK and sessionOK and (c15 >= kHi15) and (rsi15 >= rsiOB) and bBear15 and vSpike15 and cmf15 <= -cmfMin and mfi15 <= mfiMax
//====================== Routing & Cooldown =====================
fire10 = controller == "10m"
fire15 = controller == "15m"
flatExit = strategy.position_size == 0 and strategy.position_size[1] != 0
cooldownActive = ta.barssince(flatExit) <= cooldownBars
//====================== Orders & Risk ==========================
lossTicks10 = math.round(atr10 * slMult * spreadMult / syminfo.mintick)
profitTicks10 = math.round(atr10 * tpMult * spreadMult / syminfo.mintick)
lossTicks15 = math.round(atr15 * slMult * spreadMult / syminfo.mintick)
profitTicks15 = math.round(atr15 * tpMult * spreadMult / syminfo.mintick)
// Daily kill-switch (percent of start-of-day equity)
var float dayStartEq = strategy.equity
newDay = ta.change(time("D")) != 0
if newDay
dayStartEq := strategy.equity
dayDrawdownPct = 100 * (dayStartEq - strategy.equity) / math.max(dayStartEq, 1)
dailyBlock = dayDrawdownPct >= dailyStop
canEnter = not cooldownActive and not dailyBlock
// 10m orders
if fire10 and canEnter and long10
strategy.entry("L10", strategy.long, comment="10m Long")
strategy.exit("XL10", from_entry="L10", loss=lossTicks10, profit=profitTicks10)
if fire10 and canEnter and short10
strategy.entry("S10", strategy.short, comment="10m Short")
strategy.exit("XS10", from_entry="S10", loss=lossTicks10, profit=profitTicks10)
// 15m orders
if fire15 and canEnter and long15
strategy.entry("L15", strategy.long, comment="15m Long")
strategy.exit("XL15", from_entry="L15", loss=lossTicks15, profit=profitTicks15)
if fire15 and canEnter and short15
strategy.entry("S15", strategy.short, comment="15m Short")
strategy.exit("XS15", from_entry="S15", loss=lossTicks15, profit=profitTicks15)
// Optional trailing (arms once RR ≥ 1, using current ATR)
if trailOn and strategy.position_size != 0
avg = strategy.position_avg_price
rrOK = not na(avg) and (math.abs(close - avg) >= ta.atr(atrLen) * slMult)
if rrOK
trailTicks = math.round(ta.atr(atrLen) * trailATR / syminfo.mintick)
strategy.exit(id="TRAIL", trail_points=trailTicks, trail_offset=trailTicks)
//====================== Visuals & Alerts =======================
if show10Bands
plot(kHi10, "10m Keltner Hi", color=color.new(color.red, 0))
plot(ema10, "10m Keltner Mid", color=color.new(color.gray, 0))
plot(kLo10, "10m Keltner Lo", color=color.new(color.green, 0))
if showMarks
plotchar(fire10 and long10, title="L10", char="▲", location=location.belowbar)
plotchar(fire10 and short10, title="S10", char="▼", location=location.abovebar)
plotchar(fire15 and long15, title="L15", char="▲", location=location.belowbar)
plotchar(fire15 and short15, title="S15", char="▼", location=location.abovebar)
alertcondition(fire10 and long10, title="10m Long", message="10m Long")
alertcondition(fire10 and short10, title="10m Short", message="10m Short")
alertcondition(fire15 and long15, title="15m Long", message="15m Long")
alertcondition(fire15 and short15, title="15m Short", message="15m Short")
alertcondition(dailyBlock, title="Daily Stop Hit", message="Daily loss stop triggered — entries blocked.")