Changes: - moneyline_v11_2_indicator.pinescript: Alert format now includes SCORE:100 - parse_signal_enhanced.json: Added indicatorScore parsing (SCORE:X regex) - execute/route.ts: Added hasIndicatorScore bypass (score >= 90 bypasses quality check) - Money_Machine.json: Both Execute Trade nodes now pass indicatorScore to API Rationale: v11.2 indicator filters already optimized (2.544 PF, +51.80% return). Bot-side quality scoring was blocking proven profitable signals (e.g., quality 75). Now indicator passes SCORE:100, bot respects it and executes immediately. This completes the signal chain: Indicator (SCORE:100) → n8n parser (indicatorScore) → workflow → bot endpoint (bypass)
207 lines
9.3 KiB
Plaintext
207 lines
9.3 KiB
Plaintext
//@version=6
|
|
strategy("Money Line v11.2 STRATEGY", shorttitle="ML v11.2 Strat", overlay=true, pyramiding=0, initial_capital=1000, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
|
|
// V11.2 STRATEGY VERSION (Dec 25, 2025):
|
|
// Same logic as indicator but with entries/exits for TradingView Strategy Report
|
|
|
|
// === CORE PARAMETERS ===
|
|
atrPeriod = input.int(12, "ATR Period", minval=1, group="Core")
|
|
multiplier = input.float(3.8, "Multiplier", minval=0.1, step=0.1, group="Core")
|
|
|
|
// === SIGNAL TIMING ===
|
|
confirmBars = input.int(1, "Bars to confirm after flip", minval=0, maxval=3, group="Timing")
|
|
flipThreshold = input.float(0.20, "Flip threshold %", minval=0.0, maxval=2.0, step=0.05, group="Timing")
|
|
|
|
// === ENTRY FILTERS ===
|
|
useEntryBuffer = input.bool(true, "Require entry buffer (ATR)", group="Filters")
|
|
entryBufferATR = input.float(-0.10, "Buffer size (in ATR, negative=early)", minval=-1.0, step=0.05, group="Filters")
|
|
useAdx = input.bool(true, "Use ADX filter", group="Filters")
|
|
adxLen = input.int(16, "ADX Length", minval=1, group="Filters")
|
|
adxMin = input.int(12, "ADX minimum", minval=0, maxval=100, group="Filters")
|
|
|
|
// === RSI FILTER ===
|
|
useRsiFilter = input.bool(true, "Use RSI filter", group="RSI")
|
|
rsiLongMin = input.float(56, "RSI Long Min", minval=0, maxval=100, group="RSI")
|
|
rsiLongMax = input.float(69, "RSI Long Max", minval=0, maxval=100, group="RSI")
|
|
rsiShortMin = input.float(30, "RSI Short Min", minval=0, maxval=100, group="RSI")
|
|
rsiShortMax = input.float(70, "RSI Short Max", minval=0, maxval=100, group="RSI")
|
|
|
|
// === POSITION FILTER ===
|
|
usePricePosition = input.bool(true, "Use price position filter", group="Position")
|
|
longPosMax = input.float(85, "Long max position %", minval=0, maxval=100, group="Position")
|
|
shortPosMin = input.float(5, "Short min position %", minval=0, maxval=100, group="Position")
|
|
|
|
// === VOLUME FILTER ===
|
|
useVolumeFilter = input.bool(true, "Use volume filter", group="Volume")
|
|
volMin = input.float(0.1, "Volume min ratio", minval=0.0, step=0.1, group="Volume")
|
|
volMax = input.float(3.5, "Volume max ratio", minval=0.5, step=0.5, group="Volume")
|
|
|
|
// === EXITS ===
|
|
tpPct = input.float(1.0, "TP %", minval=0.1, maxval=10, step=0.1, group="Exits")
|
|
slPct = input.float(0.8, "SL %", minval=0.1, maxval=10, step=0.1, group="Exits")
|
|
|
|
// =============================================================================
|
|
// MONEY LINE CALCULATION
|
|
// =============================================================================
|
|
|
|
calcH = high
|
|
calcL = low
|
|
calcC = close
|
|
|
|
// ATR
|
|
tr = math.max(calcH - calcL, math.max(math.abs(calcH - calcC[1]), math.abs(calcL - calcC[1])))
|
|
atr = ta.rma(tr, atrPeriod)
|
|
src = (calcH + calcL) / 2
|
|
|
|
up = src - (multiplier * atr)
|
|
dn = src + (multiplier * atr)
|
|
|
|
var float up1 = na
|
|
var float dn1 = na
|
|
|
|
up1 := nz(up1[1], up)
|
|
dn1 := nz(dn1[1], dn)
|
|
|
|
up1 := calcC[1] > up1 ? math.max(up, up1) : up
|
|
dn1 := calcC[1] < dn1 ? math.min(dn, dn1) : dn
|
|
|
|
var int trend = 1
|
|
var float tsl = na
|
|
|
|
tsl := nz(tsl[1], up1)
|
|
|
|
// Flip threshold
|
|
thresholdAmount = tsl * (flipThreshold / 100)
|
|
|
|
// Track consecutive bars for flip confirmation
|
|
var int bullMomentumBars = 0
|
|
var int bearMomentumBars = 0
|
|
|
|
if trend == 1
|
|
tsl := math.max(up1, tsl)
|
|
if calcC < (tsl - thresholdAmount)
|
|
bearMomentumBars := bearMomentumBars + 1
|
|
bullMomentumBars := 0
|
|
else
|
|
bearMomentumBars := 0
|
|
trend := bearMomentumBars >= (confirmBars + 1) ? -1 : 1
|
|
else
|
|
tsl := math.min(dn1, tsl)
|
|
if calcC > (tsl + thresholdAmount)
|
|
bullMomentumBars := bullMomentumBars + 1
|
|
bearMomentumBars := 0
|
|
else
|
|
bullMomentumBars := 0
|
|
trend := bullMomentumBars >= (confirmBars + 1) ? 1 : -1
|
|
|
|
supertrend = tsl
|
|
|
|
// =============================================================================
|
|
// INDICATORS
|
|
// =============================================================================
|
|
|
|
// ADX
|
|
upMove = calcH - calcH[1]
|
|
downMove = calcL[1] - calcL
|
|
plusDM = (upMove > downMove and upMove > 0) ? upMove : 0.0
|
|
minusDM = (downMove > upMove and downMove > 0) ? downMove : 0.0
|
|
trADX = math.max(calcH - calcL, math.max(math.abs(calcH - calcC[1]), math.abs(calcL - calcC[1])))
|
|
atrADX = ta.rma(trADX, adxLen)
|
|
plusDMSmooth = ta.rma(plusDM, adxLen)
|
|
minusDMSmooth = ta.rma(minusDM, adxLen)
|
|
plusDI = atrADX == 0.0 ? 0.0 : 100.0 * plusDMSmooth / atrADX
|
|
minusDI = atrADX == 0.0 ? 0.0 : 100.0 * minusDMSmooth / atrADX
|
|
dx = (plusDI + minusDI == 0.0) ? 0.0 : 100.0 * math.abs(plusDI - minusDI) / (plusDI + minusDI)
|
|
adxVal = ta.rma(dx, adxLen)
|
|
|
|
// RSI
|
|
rsi14 = ta.rsi(calcC, 14)
|
|
|
|
// Volume ratio
|
|
volMA20 = ta.sma(volume, 20)
|
|
volumeRatio = volume / volMA20
|
|
|
|
// Price position in 100-bar range
|
|
highest100 = ta.highest(calcH, 100)
|
|
lowest100 = ta.lowest(calcL, 100)
|
|
priceRange = highest100 - lowest100
|
|
pricePosition = priceRange == 0 ? 50.0 : ((calcC - lowest100) / priceRange) * 100
|
|
|
|
// =============================================================================
|
|
// FILTER CHECKS
|
|
// =============================================================================
|
|
|
|
adxOk = not useAdx or (adxVal >= adxMin)
|
|
longBufferOk = not useEntryBuffer or (calcC > supertrend + entryBufferATR * atr)
|
|
shortBufferOk = not useEntryBuffer or (calcC < supertrend - entryBufferATR * atr)
|
|
rsiLongOk = not useRsiFilter or (rsi14 >= rsiLongMin and rsi14 <= rsiLongMax)
|
|
rsiShortOk = not useRsiFilter or (rsi14 >= rsiShortMin and rsi14 <= rsiShortMax)
|
|
longPositionOk = not usePricePosition or (pricePosition < longPosMax)
|
|
shortPositionOk = not usePricePosition or (pricePosition > shortPosMin)
|
|
volumeOk = not useVolumeFilter or (volumeRatio >= volMin and volumeRatio <= volMax)
|
|
|
|
// =============================================================================
|
|
// SIGNALS
|
|
// =============================================================================
|
|
|
|
buyFlip = trend == 1 and trend[1] == -1
|
|
sellFlip = trend == -1 and trend[1] == 1
|
|
|
|
buyReady = ta.barssince(buyFlip) == confirmBars
|
|
sellReady = ta.barssince(sellFlip) == confirmBars
|
|
|
|
// FINAL SIGNALS (all filters applied!)
|
|
finalLongSignal = buyReady and adxOk and longBufferOk and rsiLongOk and longPositionOk and volumeOk
|
|
finalShortSignal = sellReady and adxOk and shortBufferOk and rsiShortOk and shortPositionOk and volumeOk
|
|
|
|
// =============================================================================
|
|
// STRATEGY ENTRIES & EXITS
|
|
// =============================================================================
|
|
|
|
if finalLongSignal
|
|
strategy.entry("Long", strategy.long)
|
|
|
|
if finalShortSignal
|
|
strategy.entry("Short", strategy.short)
|
|
|
|
// Exits with TP/SL
|
|
if strategy.position_size > 0
|
|
strategy.exit("L Exit", "Long", stop=strategy.position_avg_price * (1 - slPct/100), limit=strategy.position_avg_price * (1 + tpPct/100))
|
|
if strategy.position_size < 0
|
|
strategy.exit("S Exit", "Short", stop=strategy.position_avg_price * (1 + slPct/100), limit=strategy.position_avg_price * (1 - tpPct/100))
|
|
|
|
// =============================================================================
|
|
// PLOTS
|
|
// =============================================================================
|
|
|
|
upTrend = trend == 1 ? supertrend : na
|
|
downTrend = trend == -1 ? supertrend : na
|
|
|
|
plot(upTrend, "Up Trend", color=color.new(color.green, 0), style=plot.style_linebr, linewidth=2)
|
|
plot(downTrend, "Down Trend", color=color.new(color.red, 0), style=plot.style_linebr, linewidth=2)
|
|
|
|
plotshape(finalLongSignal, title="Buy", location=location.belowbar, color=color.lime, style=shape.triangleup, size=size.small)
|
|
plotshape(finalShortSignal, title="Sell", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small)
|
|
|
|
// =============================================================================
|
|
// DEBUG TABLE
|
|
// =============================================================================
|
|
|
|
var table dbg = table.new(position.top_right, 2, 8, bgcolor=color.new(color.black, 80))
|
|
if barstate.islast
|
|
table.cell(dbg, 0, 0, "Trend", text_color=color.white)
|
|
table.cell(dbg, 1, 0, trend == 1 ? "LONG ✓" : "SHORT ✓", text_color=trend == 1 ? color.lime : color.red)
|
|
table.cell(dbg, 0, 1, "ADX", text_color=color.white)
|
|
table.cell(dbg, 1, 1, str.tostring(adxVal, "#.#") + (adxOk ? " ✓" : " ✗"), text_color=adxOk ? color.lime : color.red)
|
|
table.cell(dbg, 0, 2, "RSI", text_color=color.white)
|
|
table.cell(dbg, 1, 2, str.tostring(rsi14, "#.#") + (rsiLongOk or rsiShortOk ? " ✓" : " ✗"), text_color=rsiLongOk or rsiShortOk ? color.lime : color.orange)
|
|
table.cell(dbg, 0, 3, "Price Pos", text_color=color.white)
|
|
table.cell(dbg, 1, 3, str.tostring(pricePosition, "#.#") + "%" + (longPositionOk and shortPositionOk ? " ✓" : ""), text_color=color.white)
|
|
table.cell(dbg, 0, 4, "Volume", text_color=color.white)
|
|
table.cell(dbg, 1, 4, str.tostring(volumeRatio, "#.##") + "x" + (volumeOk ? " ✓" : " ✗"), text_color=volumeOk ? color.lime : color.orange)
|
|
table.cell(dbg, 0, 5, "Entry Buffer", text_color=color.white)
|
|
table.cell(dbg, 1, 5, longBufferOk or shortBufferOk ? "OK ✓" : "—", text_color=longBufferOk or shortBufferOk ? color.lime : color.gray)
|
|
table.cell(dbg, 0, 6, "Signal", text_color=color.white)
|
|
table.cell(dbg, 1, 6, finalLongSignal ? "BUY!" : finalShortSignal ? "SELL!" : "—", text_color=finalLongSignal ? color.lime : finalShortSignal ? color.red : color.gray)
|
|
table.cell(dbg, 0, 7, "TP/SL", text_color=color.white)
|
|
table.cell(dbg, 1, 7, "+" + str.tostring(tpPct, "#.#") + "% / -" + str.tostring(slPct, "#.#") + "%", text_color=color.yellow)
|