fix: Total P&L now uses actual account growth instead of API value

- Changed Total P&L display from tradingStats.totalPnL (closed trades only)
  to currentCapital - STARTING_CAPITAL (actual account growth)
- Now matches Net Profit calculation (22 vs previous 77)
- Both metrics now show consistent, accurate profit figures
This commit is contained in:
mindesbunister
2026-01-14 20:32:20 +01:00
parent ed18c9e70f
commit 46c4ee412c
2 changed files with 409 additions and 9 deletions

View File

@@ -0,0 +1,346 @@
//@version=6
indicator("Money Line v11.2 + Flip Preview", shorttitle="ML v11.2FP", overlay=true)
// V11.2 WITH FLIP PREVIEW (Jan 13, 2026):
// Shows when indicator is APPROACHING a bullish/bearish flip
// Yellow dots = Building momentum toward flip (X of Y bars confirmed)
// Blue background = All filters ready, waiting for flip only
// === 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")
multiplier = input.float(3.8, "Multiplier", minval=0.1, step=0.1, group="Core")
// === SIGNAL TIMING (OPTIMIZED) ===
confirmBars = input.int(1, "Bars to confirm after flip", minval=0, maxval=3, group="Timing")
flipThreshold = input.float(0.0, "Flip threshold %", minval=0.0, maxval=2.0, step=0.05, group="Timing")
// === ENTRY FILTERS (OPTIMIZED) ===
useEntryBuffer = input.bool(true, "Require entry buffer (ATR)", group="Filters")
entryBufferATR = input.float(-0.15, "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(17, "ADX Length", minval=1, group="Filters")
adxMin = input.int(15, "ADX minimum", minval=0, maxval=100, group="Filters")
// === RSI FILTER (OPTIMIZED) ===
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 (OPTIMIZED) ===
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 - 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")
// === FLIP PREVIEW SETTINGS ===
showFlipPreview = input.bool(true, "Show flip preview", group="Preview", tooltip="Yellow dots show momentum building toward flip")
showReadyZones = input.bool(true, "Show filters-ready zones", group="Preview", tooltip="Blue background when all filters pass, waiting for flip only")
// === ALERT SETTINGS ===
indicatorVersion = input.string("v11.2opt", "Indicator Version", group="Alert")
// =============================================================================
// 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)
atrPercent = (atr / close) * 100
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
// Calculate momentum state BEFORE updating trend
int prevBullMomentum = bullMomentumBars
int prevBearMomentum = bearMomentumBars
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
// =============================================================================
// FLIP PREVIEW DETECTION
// =============================================================================
// How close are we to flipping?
// Need (confirmBars + 1) bars to flip. Show progress.
barsNeeded = confirmBars + 1
// Pending bullish flip: Currently in downtrend, building bullish momentum
pendingBullishFlip = trend[1] == -1 and bullMomentumBars > 0 and bullMomentumBars < barsNeeded
bullishMomentumProgress = bullMomentumBars
// Pending bearish flip: Currently in uptrend, building bearish momentum
pendingBearishFlip = trend[1] == 1 and bearMomentumBars > 0 and bearMomentumBars < barsNeeded
bearishMomentumProgress = bearMomentumBars
// =============================================================================
// FILTER CALCULATIONS
// =============================================================================
// ADX
[diPlus, diMinus, adxVal] = ta.dmi(adxLen, adxLen)
// RSI
rsi14 = ta.rsi(close, 14)
// Volume ratio
volumeSMA = ta.sma(volume, 20)
volumeRatio = volumeSMA > 0 ? volume / volumeSMA : 1.0
// MA Gap
ma50 = ta.sma(close, 50)
ma200 = ta.sma(close, 200)
maGap = ma200 > 0 ? ((ma50 - ma200) / ma200) * 100 : 0
// Price position
highest100 = ta.highest(high, 100)
lowest100 = ta.lowest(low, 100)
priceRange = highest100 - lowest100
pricePosition = priceRange > 0 ? ((close - lowest100) / priceRange) * 100 : 50
// 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)
// All filters pass (for preview - checking if signal WOULD fire on flip)
allLongFiltersPass = adxOk and longBufferOk and rsiLongOk and longPositionOk and volumeOk
allShortFiltersPass = 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"
// =============================================================================
// 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 + 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
// =============================================================================
// FLIP PREVIEW CONDITIONS
// =============================================================================
// Ready for bullish flip: In downtrend, building momentum, all filters pass
readyForBullishFlip = pendingBullishFlip and allLongFiltersPass and allowLong
// Ready for bearish flip: In uptrend, building momentum, all filters pass
readyForBearishFlip = pendingBearishFlip and allShortFiltersPass and allowShort
// Filters ready but waiting for any flip (no momentum yet)
// In downtrend with filters ready for long
filtersReadyLong = trend == -1 and allLongFiltersPass and allowLong and not pendingBullishFlip
// In uptrend with filters ready for short
filtersReadyShort = trend == 1 and allShortFiltersPass and allowShort and not pendingBearishFlip
// =============================================================================
// ALERT MESSAGE CONSTRUCTION
// =============================================================================
sym = syminfo.ticker
baseCurrency = sym
baseCurrency := str.replace(baseCurrency, "USDT", "")
baseCurrency := str.replace(baseCurrency, "USD", "")
baseCurrency := str.replace(baseCurrency, "PERP", "")
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
// =============================================================================
if finalLongSignal
alert(longAlertMsg, alert.freq_once_per_bar_close)
if finalShortSignal
alert(shortAlertMsg, alert.freq_once_per_bar_close)
alertcondition(finalLongSignal, title="ML Long Signal", message="{{ticker}} buy {{interval}}")
alertcondition(finalShortSignal, title="ML Short Signal", message="{{ticker}} sell {{interval}}")
// =============================================================================
// 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)
// Main signals - triangles
plotshape(finalLongSignal, title="Buy Signal", location=location.belowbar, color=color.lime, style=shape.triangleup, size=size.small)
plotshape(finalShortSignal, title="Sell Signal", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small)
// FLIP MARKERS - Show exactly where trend flipped (helps find the moment)
plotshape(buyFlip, title="Bull Flip Marker", location=location.belowbar, color=color.white, style=shape.diamond, size=size.tiny, text="FLIP", textcolor=color.white)
plotshape(sellFlip, title="Bear Flip Marker", location=location.abovebar, color=color.white, style=shape.diamond, size=size.tiny, text="FLIP", textcolor=color.white)
// =============================================================================
// FLIP PREVIEW PLOTS
// =============================================================================
// Cyan/Magenta circles = Momentum building toward flip (more visible than yellow/orange)
plotshape(showFlipPreview and pendingBullishFlip, title="Pending Bull Flip", location=location.belowbar, color=color.aqua, style=shape.circle, size=size.small)
plotshape(showFlipPreview and pendingBearishFlip, title="Pending Bear Flip", location=location.abovebar, color=color.fuchsia, style=shape.circle, size=size.small)
// Blue background = All filters pass, in opposite trend, ready to flip
bgcolor(showReadyZones and readyForBullishFlip ? color.new(color.blue, 85) : na, title="Ready for Bull Flip")
bgcolor(showReadyZones and readyForBearishFlip ? color.new(color.purple, 85) : na, title="Ready for Bear Flip")
// Light green background = Filters ready but no momentum yet (watching zone)
bgcolor(showReadyZones and filtersReadyLong ? color.new(color.green, 92) : na, title="Filters Ready Long")
bgcolor(showReadyZones and filtersReadyShort ? color.new(color.red, 92) : na, title="Filters Ready Short")
// =============================================================================
// DEBUG TABLE (Enhanced with Flip Preview)
// =============================================================================
// Track when last flip happened and if signal fired
var int barsSinceLastBullFlip = na
var int barsSinceLastBearFlip = na
barsSinceLastBullFlip := buyFlip ? 0 : nz(barsSinceLastBullFlip[1], 999) + 1
barsSinceLastBearFlip := sellFlip ? 0 : nz(barsSinceLastBearFlip[1], 999) + 1
var table dbg = table.new(position.top_right, 2, 14, bgcolor=color.new(color.black, 80))
if barstate.islast
// Original rows
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, 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, "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)
// NEW: Flip Preview rows
table.cell(dbg, 0, 7, "━━━━━━━━", text_color=color.gray)
table.cell(dbg, 1, 7, "━━━━━━━━", text_color=color.gray)
// Bull momentum progress
bullProgressText = trend == -1 ? str.tostring(bullMomentumBars) + "/" + str.tostring(barsNeeded) + " bars" : "—"
table.cell(dbg, 0, 8, "Bull Flip", text_color=color.white)
table.cell(dbg, 1, 8, bullProgressText, text_color=bullMomentumBars > 0 ? color.yellow : color.gray)
// Bear momentum progress
bearProgressText = trend == 1 ? str.tostring(bearMomentumBars) + "/" + str.tostring(barsNeeded) + " bars" : "—"
table.cell(dbg, 0, 9, "Bear Flip", text_color=color.white)
table.cell(dbg, 1, 9, bearProgressText, text_color=bearMomentumBars > 0 ? color.orange : color.gray)
// Filters ready status - show relevant direction based on mode
filtersStatus = directionMode == "Long Only" ? (allLongFiltersPass ? "LONG ✓" : "NOT READY") : directionMode == "Short Only" ? (allShortFiltersPass ? "SHORT ✓" : "NOT READY") : trend == -1 ? (allLongFiltersPass ? "LONG ✓" : "NOT READY") : (allShortFiltersPass ? "SHORT ✓" : "NOT READY")
filtersColor = directionMode == "Long Only" ? (allLongFiltersPass ? color.lime : color.red) : directionMode == "Short Only" ? (allShortFiltersPass ? color.lime : color.red) : trend == -1 ? (allLongFiltersPass ? color.lime : color.red) : (allShortFiltersPass ? color.lime : color.red)
table.cell(dbg, 0, 10, "Filters", text_color=color.white)
table.cell(dbg, 1, 10, filtersStatus, text_color=filtersColor)
// Last flip info - helps debug "why didn't signal fire?"
lastFlipText = barsSinceLastBullFlip < barsSinceLastBearFlip ?
"BULL " + str.tostring(barsSinceLastBullFlip) + " bars ago" :
barsSinceLastBearFlip < 999 ? "BEAR " + str.tostring(barsSinceLastBearFlip) + " bars ago" : "None"
table.cell(dbg, 0, 11, "Last Flip", text_color=color.white)
table.cell(dbg, 1, 11, lastFlipText, text_color=barsSinceLastBullFlip < 5 ? color.lime : barsSinceLastBearFlip < 5 ? color.red : color.gray)
// buyReady status - did we pass the confirmation bar requirement?
buyReadyText = buyReady ? "READY!" : buyFlip ? "JUST FLIPPED" : barsSinceLastBullFlip < 10 ? "Confirm: " + str.tostring(barsSinceLastBullFlip) + "/" + str.tostring(confirmBars) : "—"
table.cell(dbg, 0, 12, "Buy Ready", text_color=color.white)
table.cell(dbg, 1, 12, buyReadyText, text_color=buyReady ? color.lime : buyFlip ? color.yellow : color.gray)
// Signal row
table.cell(dbg, 0, 13, "Signal", text_color=color.white)
table.cell(dbg, 1, 13, finalLongSignal ? "BUY!" : finalShortSignal ? "SELL!" : "—", text_color=finalLongSignal ? color.lime : finalShortSignal ? color.red : color.gray)