feat(v4+): make all inputs per-profile when enabled (ATR period, multiplier, filter toggles, gates) and wire into logic
This commit is contained in:
@@ -56,41 +56,77 @@ showGatedMarkers = input.bool(true, "Show gated markers (triangles)")
|
||||
useProfiles = input.bool(false, "Use per-timeframe profiles")
|
||||
|
||||
grpI = "Profile: Intraday (<60m)"
|
||||
pI_atrPeriod = input.int(10, "ATR Period", minval=1, group=grpI)
|
||||
pI_multiplier = input.float(3.0, "Multiplier", minval=0.1, step=0.1, group=grpI)
|
||||
pI_confirmBars = input.int(2, "Confirm bars", minval=1, group=grpI)
|
||||
pI_bufferATR = input.float(0.15, "Buffer (×ATR)", minval=0.0, step=0.05, group=grpI)
|
||||
pI_smoothLen = input.int(1, "Smoothing EMA", minval=0, group=grpI)
|
||||
pI_useAdxFilt = input.bool(false, "Use ADX/DMI filter", group=grpI)
|
||||
pI_adxLength = input.int(18, "ADX Length", minval=1, group=grpI)
|
||||
pI_minAdx = input.float(20.0, "Min ADX", minval=1.0, step=0.5, group=grpI)
|
||||
pI_useMTFConf = input.bool(false, "Use higher timeframe confirmation", group=grpI)
|
||||
pI_higherTF = input.timeframe("", "Higher timeframe (e.g. 60, 1D)", group=grpI)
|
||||
pI_sessionStr = input.session("0000-2400", "Active session", group=grpI)
|
||||
pI_cooldownBars = input.int(0, "Cooldown bars after flip", minval=0, group=grpI)
|
||||
pI_alertsClose = input.bool(true, "Alerts/labels on bar close only", group=grpI)
|
||||
pI_useTrendMA = input.bool(false, "Use EMA trend filter", group=grpI)
|
||||
pI_chopLength = input.int(14, "CHOP Length", minval=2, group=grpI)
|
||||
pI_maxChop = input.float(55.0, "Max CHOP", minval=0.0, maxval=100.0, step=0.5, group=grpI)
|
||||
pI_useChopFilt = input.bool(false, "Use Choppiness filter", group=grpI)
|
||||
pI_useRetest = input.bool(false, "Require retest after flip", group=grpI)
|
||||
pI_retestWindow = input.int(3, "Retest window (bars)", minval=1, group=grpI)
|
||||
pI_retestTolATR = input.float(0.20, "Retest tol (×ATR)", minval=0.0, step=0.05, group=grpI)
|
||||
pI_useBodyFilter = input.bool(false, "Min candle body fraction", group=grpI)
|
||||
pI_minBodyFrac = input.float(0.35, "Min body fraction", minval=0.0, maxval=1.0, step=0.05, group=grpI)
|
||||
pI_trendMALen = input.int(100, "EMA trend length", minval=1, group=grpI)
|
||||
|
||||
grpH = "Profile: 1–4h"
|
||||
pH_atrPeriod = input.int(10, "ATR Period", minval=1, group=grpH)
|
||||
pH_multiplier = input.float(3.0, "Multiplier", minval=0.1, step=0.1, group=grpH)
|
||||
pH_confirmBars = input.int(2, "Confirm bars", minval=1, group=grpH)
|
||||
pH_bufferATR = input.float(0.10, "Buffer (×ATR)", minval=0.0, step=0.05, group=grpH)
|
||||
pH_smoothLen = input.int(1, "Smoothing EMA", minval=0, group=grpH)
|
||||
pH_useAdxFilt = input.bool(false, "Use ADX/DMI filter", group=grpH)
|
||||
pH_adxLength = input.int(20, "ADX Length", minval=1, group=grpH)
|
||||
pH_minAdx = input.float(19.0, "Min ADX", minval=1.0, step=0.5, group=grpH)
|
||||
pH_useMTFConf = input.bool(false, "Use higher timeframe confirmation", group=grpH)
|
||||
pH_higherTF = input.timeframe("", "Higher timeframe (e.g. 60, 1D)", group=grpH)
|
||||
pH_sessionStr = input.session("0000-2400", "Active session", group=grpH)
|
||||
pH_cooldownBars = input.int(0, "Cooldown bars after flip", minval=0, group=grpH)
|
||||
pH_alertsClose = input.bool(true, "Alerts/labels on bar close only", group=grpH)
|
||||
pH_useTrendMA = input.bool(false, "Use EMA trend filter", group=grpH)
|
||||
pH_chopLength = input.int(14, "CHOP Length", minval=2, group=grpH)
|
||||
pH_maxChop = input.float(57.0, "Max CHOP", minval=0.0, maxval=100.0, step=0.5, group=grpH)
|
||||
pH_useChopFilt = input.bool(false, "Use Choppiness filter", group=grpH)
|
||||
pH_useRetest = input.bool(false, "Require retest after flip", group=grpH)
|
||||
pH_retestWindow = input.int(3, "Retest window (bars)", minval=1, group=grpH)
|
||||
pH_retestTolATR = input.float(0.20, "Retest tol (×ATR)", minval=0.0, step=0.05, group=grpH)
|
||||
pH_useBodyFilter = input.bool(false, "Min candle body fraction", group=grpH)
|
||||
pH_minBodyFrac = input.float(0.30, "Min body fraction", minval=0.0, maxval=1.0, step=0.05, group=grpH)
|
||||
pH_trendMALen = input.int(200, "EMA trend length", minval=1, group=grpH)
|
||||
|
||||
grpD = "Profile: 1D+"
|
||||
pD_atrPeriod = input.int(10, "ATR Period", minval=1, group=grpD)
|
||||
pD_multiplier = input.float(3.0, "Multiplier", minval=0.1, step=0.1, group=grpD)
|
||||
pD_confirmBars = input.int(1, "Confirm bars", minval=1, group=grpD)
|
||||
pD_bufferATR = input.float(0.10, "Buffer (×ATR)", minval=0.0, step=0.05, group=grpD)
|
||||
pD_smoothLen = input.int(0, "Smoothing EMA", minval=0, group=grpD)
|
||||
pD_useAdxFilt = input.bool(false, "Use ADX/DMI filter", group=grpD)
|
||||
pD_adxLength = input.int(14, "ADX Length", minval=1, group=grpD)
|
||||
pD_minAdx = input.float(18.0, "Min ADX", minval=1.0, step=0.5, group=grpD)
|
||||
pD_useMTFConf = input.bool(false, "Use higher timeframe confirmation", group=grpD)
|
||||
pD_higherTF = input.timeframe("", "Higher timeframe (e.g. 60, 1D)", group=grpD)
|
||||
pD_sessionStr = input.session("0000-2400", "Active session", group=grpD)
|
||||
pD_cooldownBars = input.int(0, "Cooldown bars after flip", minval=0, group=grpD)
|
||||
pD_alertsClose = input.bool(true, "Alerts/labels on bar close only", group=grpD)
|
||||
pD_useTrendMA = input.bool(false, "Use EMA trend filter", group=grpD)
|
||||
pD_chopLength = input.int(14, "CHOP Length", minval=2, group=grpD)
|
||||
pD_maxChop = input.float(60.0, "Max CHOP", minval=0.0, maxval=100.0, step=0.5, group=grpD)
|
||||
pD_useChopFilt = input.bool(false, "Use Choppiness filter", group=grpD)
|
||||
pD_useRetest = input.bool(false, "Require retest after flip", group=grpD)
|
||||
pD_retestWindow = input.int(2, "Retest window (bars)", minval=1, group=grpD)
|
||||
pD_retestTolATR = input.float(0.20, "Retest tol (×ATR)", minval=0.0, step=0.05, group=grpD)
|
||||
pD_useBodyFilter = input.bool(false, "Min candle body fraction", group=grpD)
|
||||
pD_minBodyFrac = input.float(0.30, "Min body fraction", minval=0.0, maxval=1.0, step=0.05, group=grpD)
|
||||
pD_trendMALen = input.int(200, "EMA trend length", minval=1, group=grpD)
|
||||
|
||||
@@ -103,12 +139,24 @@ isHto4h = tfSec >= 60 * 60 and tfSec <= 4 * 60 * 60
|
||||
aConfirmBars = useProfiles ? (isIntraday ? pI_confirmBars : isHto4h ? pH_confirmBars : pD_confirmBars) : confirmBars
|
||||
aBufferATR = useProfiles ? (isIntraday ? pI_bufferATR : isHto4h ? pH_bufferATR : pD_bufferATR) : bufferATR
|
||||
aSmoothLen = useProfiles ? (isIntraday ? pI_smoothLen : isHto4h ? pH_smoothLen : pD_smoothLen) : smoothLen
|
||||
aAtrPeriod = useProfiles ? (isIntraday ? pI_atrPeriod : isHto4h ? pH_atrPeriod : pD_atrPeriod) : atrPeriod
|
||||
aMultiplier = useProfiles ? (isIntraday ? pI_multiplier : isHto4h ? pH_multiplier : pD_multiplier) : multiplier
|
||||
aAdxLength = useProfiles ? (isIntraday ? pI_adxLength : isHto4h ? pH_adxLength : pD_adxLength) : adxLength
|
||||
aMinAdx = useProfiles ? (isIntraday ? pI_minAdx : isHto4h ? pH_minAdx : pD_minAdx) : minAdx
|
||||
aUseAdxFilt = useProfiles ? (isIntraday ? pI_useAdxFilt : isHto4h ? pH_useAdxFilt : pD_useAdxFilt) : useAdxFilt
|
||||
aUseMTFConf = useProfiles ? (isIntraday ? pI_useMTFConf : isHto4h ? pH_useMTFConf : pD_useMTFConf) : useMTFConf
|
||||
aHigherTF = useProfiles ? (isIntraday ? pI_higherTF : isHto4h ? pH_higherTF : pD_higherTF) : higherTF
|
||||
aSessionStr = useProfiles ? (isIntraday ? pI_sessionStr : isHto4h ? pH_sessionStr : pD_sessionStr) : sessionStr
|
||||
aCooldownBars = useProfiles ? (isIntraday ? pI_cooldownBars : isHto4h ? pH_cooldownBars : pD_cooldownBars) : cooldownBars
|
||||
aAlertsClose = useProfiles ? (isIntraday ? pI_alertsClose : isHto4h ? pH_alertsClose : pD_alertsClose) : alertsCloseOnly
|
||||
aUseTrendMA = useProfiles ? (isIntraday ? pI_useTrendMA : isHto4h ? pH_useTrendMA : pD_useTrendMA) : useTrendMA
|
||||
aChopLength = useProfiles ? (isIntraday ? pI_chopLength : isHto4h ? pH_chopLength : pD_chopLength) : chopLength
|
||||
aMaxChop = useProfiles ? (isIntraday ? pI_maxChop : isHto4h ? pH_maxChop : pD_maxChop) : maxChop
|
||||
aUseChopFilt = useProfiles ? (isIntraday ? pI_useChopFilt : isHto4h ? pH_useChopFilt : pD_useChopFilt) : useChopFilt
|
||||
aUseRetest = useProfiles ? (isIntraday ? pI_useRetest : isHto4h ? pH_useRetest : pD_useRetest) : useRetest
|
||||
aRetestWindow = useProfiles ? (isIntraday ? pI_retestWindow : isHto4h ? pH_retestWindow : pD_retestWindow) : retestWindow
|
||||
aRetestTolATR = useProfiles ? (isIntraday ? pI_retestTolATR : isHto4h ? pH_retestTolATR : pD_retestTolATR) : retestTolATR
|
||||
aUseBodyFilter= useProfiles ? (isIntraday ? pI_useBodyFilter: isHto4h ? pH_useBodyFilter: pD_useBodyFilter) : useBodyFilter
|
||||
aMinBodyFrac = useProfiles ? (isIntraday ? pI_minBodyFrac : isHto4h ? pH_minBodyFrac : pD_minBodyFrac) : minBodyFrac
|
||||
aTrendMALen = useProfiles ? (isIntraday ? pI_trendMALen : isHto4h ? pH_trendMALen : pD_trendMALen) : trendMALen
|
||||
|
||||
@@ -136,12 +184,12 @@ atr_custom(len) =>
|
||||
tr = math.max(tr1, math.max(tr2, tr3))
|
||||
ta.rma(tr, len)
|
||||
|
||||
atr = atr_custom(atrPeriod)
|
||||
atr = atr_custom(aAtrPeriod)
|
||||
srcBase = (calcH + calcL) / 2.0
|
||||
src = aSmoothLen > 0 ? ta.ema(srcBase, aSmoothLen) : srcBase
|
||||
|
||||
up = src - (multiplier * atr)
|
||||
dn = src + (multiplier * atr)
|
||||
up = src - (aMultiplier * atr)
|
||||
dn = src + (aMultiplier * atr)
|
||||
|
||||
var float up1 = na
|
||||
var float dn1 = na
|
||||
@@ -183,7 +231,7 @@ flipDn = trend == -1 and nz(trend[1], 1) == 1
|
||||
// Track bars since last flip (for cooldown)
|
||||
var int barsSinceFlip = 100000
|
||||
barsSinceFlip := (flipUp or flipDn) ? 0 : nz(barsSinceFlip[1], 100000) + 1
|
||||
cooldownOk = barsSinceFlip >= cooldownBars
|
||||
cooldownOk = barsSinceFlip >= aCooldownBars
|
||||
|
||||
// Choppiness Index (on selected source)
|
||||
chop_tr(len) =>
|
||||
@@ -201,24 +249,24 @@ f_chop(len) =>
|
||||
100 * math.log10(sumTR / denom) / math.log10(len)
|
||||
|
||||
chop = f_chop(aChopLength)
|
||||
chopOk = not useChopFilt or (chop <= aMaxChop)
|
||||
chopOk = not aUseChopFilt or (chop <= aMaxChop)
|
||||
|
||||
// Retest-after-flip gate
|
||||
var bool retestSatisfied = true
|
||||
if flipUp or flipDn
|
||||
retestSatisfied := not useRetest ? true : false
|
||||
retestSatisfied := not aUseRetest ? true : false
|
||||
|
||||
retestTouchLong = (trend == 1) and (barsSinceFlip > 0) and (barsSinceFlip <= aRetestWindow) and (calcL <= supertrend + aRetestTolATR * atr)
|
||||
retestTouchShort = (trend == -1) and (barsSinceFlip > 0) and (barsSinceFlip <= aRetestWindow) and (calcH >= supertrend - aRetestTolATR * atr)
|
||||
if useRetest and not retestSatisfied and (retestTouchLong or retestTouchShort)
|
||||
if aUseRetest and not retestSatisfied and (retestTouchLong or retestTouchShort)
|
||||
retestSatisfied := true
|
||||
|
||||
retestOk = not useRetest or retestSatisfied
|
||||
retestOk = not aUseRetest or retestSatisfied
|
||||
|
||||
// Minimum body fraction gate
|
||||
barRange = math.max(calcH - calcL, syminfo.mintick)
|
||||
barBody = math.abs(calcC - calcO)
|
||||
bodyOk = not useBodyFilter or (barBody / barRange >= aMinBodyFrac)
|
||||
bodyOk = not aUseBodyFilter or (barBody / barRange >= aMinBodyFrac)
|
||||
|
||||
// =============================
|
||||
// Optional ADX/DMI filter (self-contained implementation on selected source)
|
||||
@@ -242,7 +290,7 @@ f_adx(len) =>
|
||||
[adxVal, pdi, mdi]
|
||||
|
||||
[adx, diPlus, diMinus] = f_adx(aAdxLength)
|
||||
adxOk = not useAdxFilt or (adx >= aMinAdx and ((trend == 1 and diPlus > diMinus) or (trend == -1 and diMinus > diPlus)))
|
||||
adxOk = not aUseAdxFilt or (adx >= aMinAdx and ((trend == 1 and diPlus > diMinus) or (trend == -1 and diMinus > diPlus)))
|
||||
|
||||
// =============================
|
||||
// Optional higher timeframe confirmation
|
||||
@@ -279,16 +327,16 @@ f_dir(_atrLen, _mult, _confBars, _bufAtr) =>
|
||||
_sellCount := 0
|
||||
_trend
|
||||
|
||||
htfDir = useMTFConf and (higherTF != "") ? request.security(syminfo.tickerid, higherTF, f_dir(atrPeriod, multiplier, confirmBars, bufferATR), lookahead=barmerge.lookahead_off) : na
|
||||
mtfOk = not useMTFConf or (trend == htfDir)
|
||||
htfDir = aUseMTFConf and (aHigherTF != "") ? request.security(syminfo.tickerid, aHigherTF, f_dir(aAtrPeriod, aMultiplier, aConfirmBars, aBufferATR), lookahead=barmerge.lookahead_off) : na
|
||||
mtfOk = not aUseMTFConf or (trend == htfDir)
|
||||
|
||||
// =============================
|
||||
// Session & trend filters and final gating
|
||||
// Session & trend filters and final gating (per-profile when enabled)
|
||||
// =============================
|
||||
inSession = not na(time(timeframe.period, sessionStr))
|
||||
inSession = not na(time(timeframe.period, aSessionStr))
|
||||
emaTrend = ta.ema(calcC, aTrendMALen)
|
||||
trendMAOk = not useTrendMA or ((trend == 1 and calcC >= emaTrend) or (trend == -1 and calcC <= emaTrend))
|
||||
barOk = not alertsCloseOnly or barstate.isconfirmed
|
||||
trendMAOk = not aUseTrendMA or ((trend == 1 and calcC >= emaTrend) or (trend == -1 and calcC <= emaTrend))
|
||||
barOk = not aAlertsClose or barstate.isconfirmed
|
||||
signalsOk = adxOk and mtfOk and inSession and cooldownOk and trendMAOk and chopOk and retestOk and bodyOk and barOk
|
||||
|
||||
// =============================
|
||||
|
||||
Reference in New Issue
Block a user