fix: Restore Direction Mode to optimized indicator (Jan 2, 2026)
- Optimized indicator was missing Direction Mode feature (Long Only) - Copied working moneyline_v11_2_indicator.pinescript over broken optimized version - Direction Mode defaults to 'Long Only' (disables SHORT signals) - Key feature: allowLong/allowShort gates final signals based on direction setting - Green supertrend line now properly sticks to candles with plot.style_linebr - Added Monte Carlo simulation notebook for v11.2 projections Files changed: - workflows/trading/moneyline_v11_2_optimized_indicator.pinescript (restored) - docs/analysis/v11_compounding_simulation.ipynb (new)
This commit is contained in:
1390
docs/analysis/v11_compounding_simulation.ipynb
Normal file
1390
docs/analysis/v11_compounding_simulation.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -1,11 +1,11 @@
|
||||
//@version=6
|
||||
indicator("Money Line v11.2 OPTIMIZED", shorttitle="ML v11.2 OPT", overlay=true)
|
||||
// V11.2 OPTIMIZED INDICATOR (Dec 25, 2025):
|
||||
// - Parameters tuned from exhaustive backtesting (+49.43% return, PF 2.507)
|
||||
// - Sends quality score 100 to bypass bot quality filter
|
||||
// - TP reduced from 1.6% to 1.3% to account for execution delay
|
||||
// - SL at 2.8% (validated)
|
||||
// - Volume filter DISABLED (backtesting showed better results without)
|
||||
indicator("Money Line v11.2 INDICATOR", shorttitle="ML v11.2", overlay=true)
|
||||
// V11.2 INDICATOR VERSION (Jan 2, 2026):
|
||||
// Production indicator with ALERTS for n8n webhook
|
||||
// Added Direction Mode: Trade LONG only until SHORT settings optimized
|
||||
|
||||
// === DIRECTION MODE ===
|
||||
directionMode = input.string("Long Only", "Trade Direction", options=["Both", "Long Only", "Short Only"], group="Direction", tooltip="Set to 'Long Only' to disable SHORT signals")
|
||||
|
||||
// === CORE PARAMETERS (OPTIMIZED) ===
|
||||
atrPeriod = input.int(12, "ATR Period", minval=1, group="Core")
|
||||
@@ -34,17 +34,13 @@ 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 (DISABLED - better results in backtest) ===
|
||||
// === VOLUME FILTER (DISABLED - OPTIMIZED) ===
|
||||
useVolumeFilter = input.bool(false, "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")
|
||||
|
||||
// === TP/SL DISPLAY (for reference only - bot handles actual exits) ===
|
||||
tpPct = input.float(1.3, "TP % (adjusted for execution delay)", minval=0.1, maxval=10, step=0.1, group="Exits")
|
||||
slPct = input.float(2.8, "SL %", minval=0.1, maxval=10, step=0.1, group="Exits")
|
||||
|
||||
// === INDICATOR VERSION ===
|
||||
indicatorVer = "v11.2opt"
|
||||
// === ALERT SETTINGS ===
|
||||
indicatorVersion = input.string("v11.2opt", "Indicator Version", group="Alert")
|
||||
|
||||
// =============================================================================
|
||||
// MONEY LINE CALCULATION
|
||||
@@ -103,7 +99,7 @@ else
|
||||
supertrend = tsl
|
||||
|
||||
// =============================================================================
|
||||
// INDICATORS (calculated for display, but alert sends bypass values)
|
||||
// INDICATORS
|
||||
// =============================================================================
|
||||
|
||||
// ADX
|
||||
@@ -123,7 +119,7 @@ adxVal = ta.rma(dx, adxLen)
|
||||
// RSI
|
||||
rsi14 = ta.rsi(calcC, 14)
|
||||
|
||||
// Volume ratio (for display even though filter disabled)
|
||||
// Volume ratio
|
||||
volMA20 = ta.sma(volume, 20)
|
||||
volumeRatio = volume / volMA20
|
||||
|
||||
@@ -133,8 +129,13 @@ lowest100 = ta.lowest(calcL, 100)
|
||||
priceRange = highest100 - lowest100
|
||||
pricePosition = priceRange == 0 ? 50.0 : ((calcC - lowest100) / priceRange) * 100
|
||||
|
||||
// ATR as percentage of price (for alert)
|
||||
atrPct = (atr / calcC) * 100
|
||||
// ATR as percentage for alert
|
||||
atrPercent = (atr / calcC) * 100
|
||||
|
||||
// MA Gap (for alert message compatibility)
|
||||
ma50 = ta.sma(calcC, 50)
|
||||
ma200 = ta.sma(calcC, 200)
|
||||
maGap = ma200 != 0 ? ((ma50 - ma200) / ma200) * 100 : 0
|
||||
|
||||
// =============================================================================
|
||||
// FILTER CHECKS
|
||||
@@ -159,34 +160,70 @@ 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
|
||||
// Direction filters
|
||||
allowLong = directionMode == "Both" or directionMode == "Long Only"
|
||||
allowShort = directionMode == "Both" or directionMode == "Short Only"
|
||||
|
||||
// FINAL SIGNALS (all filters applied + direction mode!)
|
||||
finalLongSignal = buyReady and adxOk and longBufferOk and rsiLongOk and longPositionOk and volumeOk and allowLong
|
||||
finalShortSignal = sellReady and adxOk and shortBufferOk and rsiShortOk and shortPositionOk and volumeOk and allowShort
|
||||
|
||||
// =============================================================================
|
||||
// ALERTS - Send "perfect" metrics to bypass bot quality scoring
|
||||
// ALERT MESSAGE CONSTRUCTION
|
||||
// =============================================================================
|
||||
|
||||
// Extract base currency from symbol (e.g., "SOLUSDT" -> "SOL")
|
||||
baseCurrency = str.replace(syminfo.ticker, "USDT", "")
|
||||
// Extract base currency from symbol (e.g., "SOLUSDT" -> "SOL", "FARTCOINUSDT" -> "FARTCOIN")
|
||||
sym = syminfo.ticker
|
||||
baseCurrency = sym
|
||||
// Strip common suffixes in sequence to avoid nested conditionals
|
||||
baseCurrency := str.replace(baseCurrency, "USDT", "")
|
||||
baseCurrency := str.replace(baseCurrency, "USD", "")
|
||||
baseCurrency := str.replace(baseCurrency, "PERP", "")
|
||||
|
||||
// Alert message format for n8n with BYPASS VALUES for quality score 100
|
||||
// Real indicator filters already validated signal quality - these values just bypass redundant bot check
|
||||
longAlertMsg = baseCurrency + " buy " + timeframe.period + " | ATR:0.50 | ADX:35 | RSI:60 | VOL:1.2 | POS:50 | MAGAP:0.5 | IND:" + indicatorVer
|
||||
shortAlertMsg = baseCurrency + " sell " + timeframe.period + " | ATR:0.50 | ADX:35 | RSI:40 | VOL:1.2 | POS:50 | MAGAP:0.5 | IND:" + indicatorVer
|
||||
// Format: "SOL buy 5 | ATR:0.50 | ADX:35 | RSI:60 | VOL:1.2 | POS:50 | MAGAP:0.5 | IND:v11.2opt | SCORE:100"
|
||||
// NOTE: SCORE:100 bypasses bot quality scoring - indicator already filtered to 2.5+ PF
|
||||
// All signals from v11.2opt are pre-validated profitable, no additional filtering needed
|
||||
|
||||
// Fire alerts on bar close when signal confirmed
|
||||
if finalLongSignal and barstate.isconfirmed
|
||||
longAlertMsg = str.format(
|
||||
"{0} buy {1} | ATR:{2} | ADX:{3} | RSI:{4} | VOL:{5} | POS:{6} | MAGAP:{7} | IND:{8} | SCORE:100",
|
||||
baseCurrency,
|
||||
timeframe.period,
|
||||
str.tostring(atrPercent, "#.##"),
|
||||
str.tostring(adxVal, "#.#"),
|
||||
str.tostring(rsi14, "#"),
|
||||
str.tostring(volumeRatio, "#.##"),
|
||||
str.tostring(pricePosition, "#.#"),
|
||||
str.tostring(maGap, "#.##"),
|
||||
indicatorVersion
|
||||
)
|
||||
|
||||
shortAlertMsg = str.format(
|
||||
"{0} sell {1} | ATR:{2} | ADX:{3} | RSI:{4} | VOL:{5} | POS:{6} | MAGAP:{7} | IND:{8} | SCORE:100",
|
||||
baseCurrency,
|
||||
timeframe.period,
|
||||
str.tostring(atrPercent, "#.##"),
|
||||
str.tostring(adxVal, "#.#"),
|
||||
str.tostring(rsi14, "#"),
|
||||
str.tostring(volumeRatio, "#.##"),
|
||||
str.tostring(pricePosition, "#.#"),
|
||||
str.tostring(maGap, "#.##"),
|
||||
indicatorVersion
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// SEND ALERTS
|
||||
// =============================================================================
|
||||
|
||||
// alert() fires on bar close when signal is true
|
||||
if finalLongSignal
|
||||
alert(longAlertMsg, alert.freq_once_per_bar_close)
|
||||
|
||||
if finalShortSignal and barstate.isconfirmed
|
||||
if finalShortSignal
|
||||
alert(shortAlertMsg, alert.freq_once_per_bar_close)
|
||||
|
||||
// Alert conditions for TradingView alert setup
|
||||
alertcondition(finalLongSignal, title="ML v11.2 OPT BUY", message="{{ticker}} buy {{interval}} | ATR:0.50 | ADX:35 | RSI:60 | VOL:1.2 | POS:50 | MAGAP:0.5 | IND:v11.2opt")
|
||||
alertcondition(finalShortSignal, title="ML v11.2 OPT SELL", message="{{ticker}} sell {{interval}} | ATR:0.50 | ADX:35 | RSI:40 | VOL:1.2 | POS:50 | MAGAP:0.5 | IND:v11.2opt")
|
||||
// alertcondition() for TradingView alert dialog (legacy method)
|
||||
alertcondition(finalLongSignal, title="ML Long Signal", message="{{ticker}} buy {{interval}}")
|
||||
alertcondition(finalShortSignal, title="ML Short Signal", message="{{ticker}} sell {{interval}}")
|
||||
|
||||
// =============================================================================
|
||||
// PLOTS
|
||||
@@ -201,10 +238,6 @@ plot(downTrend, "Down Trend", color=color.new(color.red, 0), style=plot.style_li
|
||||
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)
|
||||
|
||||
// TP/SL visualization
|
||||
tpLong = strategy.position_avg_price > 0 ? strategy.position_avg_price * (1 + tpPct/100) : na
|
||||
slLong = strategy.position_avg_price > 0 ? strategy.position_avg_price * (1 - slPct/100) : na
|
||||
|
||||
// =============================================================================
|
||||
// DEBUG TABLE
|
||||
// =============================================================================
|
||||
@@ -220,12 +253,12 @@ if barstate.islast
|
||||
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" + (useVolumeFilter ? (volumeOk ? " ✓" : " ✗") : " OFF"), text_color=useVolumeFilter ? (volumeOk ? color.lime : color.orange) : color.gray)
|
||||
table.cell(dbg, 1, 4, useVolumeFilter ? (str.tostring(volumeRatio, "#.##") + "x" + (volumeOk ? " ✓" : " ✗")) : "OFF", text_color=useVolumeFilter ? (volumeOk ? color.lime : color.orange) : color.gray)
|
||||
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)
|
||||
table.cell(dbg, 0, 8, "Quality", text_color=color.white)
|
||||
table.cell(dbg, 1, 8, "BYPASS (100)", text_color=color.lime)
|
||||
table.cell(dbg, 0, 6, "Direction", text_color=color.white)
|
||||
table.cell(dbg, 1, 6, directionMode, text_color=directionMode == "Long Only" ? color.lime : directionMode == "Short Only" ? color.red : color.yellow)
|
||||
table.cell(dbg, 0, 7, "Signal", text_color=color.white)
|
||||
table.cell(dbg, 1, 7, finalLongSignal ? "BUY!" : finalShortSignal ? "SELL!" : "—", text_color=finalLongSignal ? color.lime : finalShortSignal ? color.red : color.gray)
|
||||
table.cell(dbg, 0, 8, "Version", text_color=color.white)
|
||||
table.cell(dbg, 1, 8, indicatorVersion, text_color=color.yellow)
|
||||
|
||||
Reference in New Issue
Block a user