fix: Add TP1 TP2 SL prices to Telegram trade notifications

This commit is contained in:
mindesbunister
2026-01-07 08:39:24 +01:00
parent e2763d21f2
commit efbe4d0c04
6 changed files with 1675 additions and 6 deletions

View File

@@ -172,7 +172,7 @@
},
{
"parameters": {
"jsCode": "// Get data from previous nodes\nconst resp = $('Execute Trade1').item.json;\nconst parsed = $('Parse Signal Enhanced').item.json;\n\nlet message = '';\n\n// Smart Entry Queue (no entry yet, just queued)\nif (resp.smartEntry && !resp.entryPrice) {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n message = `\u23f0 SIGNAL QUEUED FOR SMART ENTRY\n\n${emoji} Direction: ${parsed.direction.toUpperCase()}\n\n Entry window: 5 minutes\n\n Quality: ${resp.qualityScore || 'N/A'}/100\n\n${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}\n}\n// Trade Opened (has entry price)\nelse if (resp.entryPrice) {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n const qualityLine = resp.qualityScore ? `\\n\\n\u2b50 Quality: ${resp.qualityScore}/100` : '';\n \n message = `\ud83d\udfe2 TRADE OPENED\n\n${emoji} Direction: ${parsed.direction.toUpperCase()}\n\n Leverage: ${resp.leverage}x${qualityLine}\n\n\n${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}\n Position monitored`;\n}\n// Fallback: Signal processed but no entry\nelse {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n message = `\ud83d\udcdd SIGNAL PROCESSED\n\n${emoji} Direction: ${parsed.direction.toUpperCase()}\n\n ${resp.message || 'Response received'}\n\n${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}`;\n}\n\nreturn { message };"
"jsCode": "// Get data from previous nodes\nconst resp = $('Execute Trade1').item.json;\nconst parsed = $('Parse Signal Enhanced').item.json;\n\nlet message = '';\n\n// Helper to format price\nconst formatPrice = (price) => price ? `$${Number(price).toFixed(2)}` : 'N/A';\n\n// Smart Entry Queue (no entry yet, just queued)\nif (resp.smartEntry && !resp.entryPrice) {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n message = `\u23f0 SIGNAL QUEUED FOR SMART ENTRY\n\n${emoji} Direction: ${parsed.direction.toUpperCase()}\n\n Entry window: 5 minutes\n\n Quality: ${resp.qualityScore || 'N/A'}/100\n\n${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}`;\n}\n// Trade Opened (has entry price)\nelse if (resp.entryPrice) {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n const qualityLine = resp.qualityScore ? `\\n\u2b50 Quality: ${resp.qualityScore}/100` : '';\n const tp1Line = `\\n\ud83c\udfaf TP1: ${formatPrice(resp.takeProfit1)}`;\n const tp2Line = `\\n\ud83c\udfaf TP2: ${formatPrice(resp.takeProfit2)}`;\n const slLine = `\\n\ud83d\uded1 SL: ${formatPrice(resp.stopLoss)}`;\n \n message = `\ud83d\udfe2 TRADE OPENED\n\n${emoji} ${parsed.direction.toUpperCase()} @ ${formatPrice(resp.entryPrice)}\n\ud83d\udcb0 Size: $${resp.positionSize?.toFixed(0) || 'N/A'} (${resp.leverage}x)${qualityLine}${tp1Line}${tp2Line}${slLine}\n\n\u23f0 ${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}\n\u2705 Position monitored`;\n}\n// Fallback: Signal processed but no entry\nelse {\n const emoji = parsed.direction === 'long' ? '\ud83d\udcc8' : '\ud83d\udcc9';\n message = `\ud83d\udcdd SIGNAL PROCESSED\n\n${emoji} Direction: ${parsed.direction.toUpperCase()}\n\n ${resp.message || 'Response received'}\n\n${$now.setZone('Europe/Berlin').toFormat('HH:mm:ss')}`;\n}\n\nreturn { message };"
},
"id": "79ab6122-cbd3-4aac-97d7-6b54f64e29b5",
"name": "Format Success",

View File

@@ -0,0 +1,194 @@
//@version=6
strategy("Mean Reversion SHORTS", shorttitle="MR Shorts", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=100,
initial_capital=1000, commission_type=strategy.commission.percent, commission_value=0.05)
// =============================================================================
// MEAN REVERSION SHORTS STRATEGY (Jan 6, 2026)
// =============================================================================
// PURPOSE: Catch overbought exhaustion for SHORT entries
//
// CONCEPT: Instead of trend-following (Money Line), this strategy:
// - Waits for RSI overbought (70+)
// - Confirms with price at top of range (80%+)
// - Looks for volume exhaustion or spike
// - Optional: Bollinger Band upper touch
// - Optional: Stochastic overbought cross down
//
// This is CONTRARIAN - shorting tops, not following trends down
// =============================================================================
// === RSI SETTINGS ===
rsiLen = input.int(14, "RSI Length", minval=5, maxval=30, group="RSI")
rsiOverbought = input.float(70, "RSI Overbought Level", minval=60, maxval=90, group="RSI")
rsiExitLevel = input.float(50, "RSI Exit Level", minval=30, maxval=60, group="RSI")
// === PRICE POSITION ===
pricePosPeriod = input.int(100, "Price Position Period", minval=20, maxval=200, group="Price Position")
pricePosMin = input.float(75, "Min Price Position %", minval=50, maxval=95, group="Price Position")
// === BOLLINGER BANDS (optional confirmation) ===
useBB = input.bool(true, "Use Bollinger Band Filter", group="Bollinger Bands")
bbLen = input.int(20, "BB Length", minval=10, maxval=50, group="Bollinger Bands")
bbMult = input.float(2.0, "BB Multiplier", minval=1.0, maxval=3.0, step=0.1, group="Bollinger Bands")
// === STOCHASTIC (optional confirmation) ===
useStoch = input.bool(false, "Use Stochastic Filter", group="Stochastic")
stochK = input.int(14, "Stoch K", minval=5, maxval=30, group="Stochastic")
stochD = input.int(3, "Stoch D", minval=1, maxval=10, group="Stochastic")
stochOB = input.float(80, "Stoch Overbought", minval=70, maxval=95, group="Stochastic")
// === VOLUME FILTER ===
useVolume = input.bool(true, "Use Volume Filter", group="Volume")
volPeriod = input.int(20, "Volume MA Period", minval=10, maxval=50, group="Volume")
volMultMin = input.float(1.2, "Min Volume Multiple", minval=0.5, maxval=3.0, step=0.1, group="Volume")
// === ADX FILTER (optional - avoid strong trends) ===
useADX = input.bool(true, "Use ADX Filter (avoid strong trends)", group="ADX")
adxLen = input.int(14, "ADX Length", minval=7, maxval=30, group="ADX")
adxMax = input.float(30, "ADX Maximum (weaker = better for MR)", minval=15, maxval=50, group="ADX")
// === CONSECUTIVE CANDLES ===
useConsec = input.bool(true, "Require Consecutive Green Candles", group="Candle Pattern")
consecMin = input.int(3, "Min Consecutive Green Candles", minval=2, maxval=7, group="Candle Pattern")
// === EXIT SETTINGS ===
tpPercent = input.float(1.5, "Take Profit %", minval=0.5, maxval=5.0, step=0.1, group="Exit")
slPercent = input.float(2.0, "Stop Loss %", minval=0.5, maxval=5.0, step=0.1, group="Exit")
useRsiExit = input.bool(true, "Exit on RSI Mean Reversion", group="Exit")
// =============================================================================
// INDICATORS
// =============================================================================
// RSI
rsi = ta.rsi(close, rsiLen)
// Price Position (where is price in recent range)
highest = ta.highest(high, pricePosPeriod)
lowest = ta.lowest(low, pricePosPeriod)
priceRange = highest - lowest
pricePosition = priceRange == 0 ? 50 : ((close - lowest) / priceRange) * 100
// Bollinger Bands
bbBasis = ta.sma(close, bbLen)
bbDev = bbMult * ta.stdev(close, bbLen)
bbUpper = bbBasis + bbDev
bbLower = bbBasis - bbDev
nearUpperBB = close >= bbUpper * 0.995 // Within 0.5% of upper band
// Stochastic
stochKVal = ta.stoch(close, high, low, stochK)
stochDVal = ta.sma(stochKVal, stochD)
stochOverbought = stochKVal > stochOB and stochDVal > stochOB
stochCrossDown = ta.crossunder(stochKVal, stochDVal) and stochKVal[1] > stochOB
// Volume
volMA = ta.sma(volume, volPeriod)
volRatio = volume / volMA
volumeSpike = volRatio >= volMultMin
// ADX (for filtering out strong trends)
[diPlus, diMinus, adxVal] = ta.dmi(adxLen, adxLen)
weakTrend = adxVal < adxMax
// Consecutive green candles (exhaustion setup)
isGreen = close > open
greenCount = ta.barssince(not isGreen)
hasConsecGreen = greenCount >= consecMin
// =============================================================================
// ENTRY CONDITIONS
// =============================================================================
// Core conditions (always required)
rsiOB = rsi >= rsiOverbought
priceAtTop = pricePosition >= pricePosMin
// Optional confirmations
bbOk = not useBB or nearUpperBB
stochOk = not useStoch or stochOverbought or stochCrossDown
volumeOk = not useVolume or volumeSpike
adxOk = not useADX or weakTrend
consecOk = not useConsec or hasConsecGreen
// FINAL SHORT SIGNAL
shortSignal = rsiOB and priceAtTop and bbOk and stochOk and volumeOk and adxOk and consecOk
// =============================================================================
// STRATEGY EXECUTION
// =============================================================================
// Entry
if shortSignal and strategy.position_size == 0
strategy.entry("Short", strategy.short)
// Exit with TP/SL
if strategy.position_size < 0
entryPrice = strategy.position_avg_price
tpPrice = entryPrice * (1 - tpPercent / 100)
slPrice = entryPrice * (1 + slPercent / 100)
strategy.exit("Exit", "Short", limit=tpPrice, stop=slPrice)
// RSI mean reversion exit (optional)
if useRsiExit and strategy.position_size < 0 and rsi <= rsiExitLevel
strategy.close("Short", comment="RSI Exit")
// =============================================================================
// PLOTS
// =============================================================================
// Bollinger Bands
plot(useBB ? bbUpper : na, "BB Upper", color=color.new(color.red, 50), linewidth=1)
plot(useBB ? bbBasis : na, "BB Mid", color=color.new(color.gray, 50), linewidth=1)
plot(useBB ? bbLower : na, "BB Lower", color=color.new(color.green, 50), linewidth=1)
// Entry signals
plotshape(shortSignal, title="Short Signal", location=location.abovebar,
color=color.red, style=shape.triangledown, size=size.small)
// Background when overbought
bgcolor(rsi >= rsiOverbought ? color.new(color.red, 90) : na)
// =============================================================================
// DEBUG TABLE
// =============================================================================
var table dbg = table.new(position.top_right, 2, 10, bgcolor=color.new(color.black, 80))
if barstate.islast
table.cell(dbg, 0, 0, "STRATEGY", text_color=color.white)
table.cell(dbg, 1, 0, "MEAN REVERSION", text_color=color.orange)
table.cell(dbg, 0, 1, "RSI", text_color=color.white)
table.cell(dbg, 1, 1, str.tostring(rsi, "#.#") + " >= " + str.tostring(rsiOverbought) + (rsiOB ? " ✓" : " ✗"),
text_color=rsiOB ? color.lime : color.red)
table.cell(dbg, 0, 2, "Price Pos", text_color=color.white)
table.cell(dbg, 1, 2, str.tostring(pricePosition, "#.#") + "% >= " + str.tostring(pricePosMin) + "%" + (priceAtTop ? " ✓" : " ✗"),
text_color=priceAtTop ? color.lime : color.red)
table.cell(dbg, 0, 3, "BB Upper", text_color=color.white)
table.cell(dbg, 1, 3, useBB ? (nearUpperBB ? "TOUCH ✓" : "—") : "OFF",
text_color=nearUpperBB ? color.lime : color.gray)
table.cell(dbg, 0, 4, "Volume", text_color=color.white)
table.cell(dbg, 1, 4, useVolume ? (str.tostring(volRatio, "#.#") + "x" + (volumeOk ? " ✓" : " ✗")) : "OFF",
text_color=volumeOk ? color.lime : color.gray)
table.cell(dbg, 0, 5, "ADX", text_color=color.white)
table.cell(dbg, 1, 5, useADX ? (str.tostring(adxVal, "#.#") + " < " + str.tostring(adxMax) + (weakTrend ? " ✓" : " ✗")) : "OFF",
text_color=weakTrend ? color.lime : color.red)
table.cell(dbg, 0, 6, "Consec Green", text_color=color.white)
table.cell(dbg, 1, 6, useConsec ? (str.tostring(greenCount) + " >= " + str.tostring(consecMin) + (hasConsecGreen ? " ✓" : " ✗")) : "OFF",
text_color=hasConsecGreen ? color.lime : color.gray)
table.cell(dbg, 0, 7, "Stochastic", text_color=color.white)
table.cell(dbg, 1, 7, useStoch ? (str.tostring(stochKVal, "#.#") + (stochOk ? " ✓" : " ✗")) : "OFF",
text_color=stochOk ? color.lime : color.gray)
table.cell(dbg, 0, 8, "TP/SL", text_color=color.white)
table.cell(dbg, 1, 8, str.tostring(tpPercent, "#.#") + "% / " + str.tostring(slPercent, "#.#") + "%", text_color=color.yellow)
table.cell(dbg, 0, 9, "SIGNAL", text_color=color.white)
table.cell(dbg, 1, 9, shortSignal ? "SHORT!" : "—", text_color=shortSignal ? color.red : color.gray)

View File

@@ -0,0 +1,203 @@
//@version=6
strategy("Money Line v11.2 SHORTS STRATEGY", shorttitle="ML Shorts", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=100,
initial_capital=1000, commission_type=strategy.commission.percent, commission_value=0.05)
// =============================================================================
// V11.2 SHORTS-ONLY STRATEGY (Jan 6, 2026)
// =============================================================================
// PURPOSE: Backtest and optimize SHORT parameters separately from LONGs
//
// FIXED PARAMETERS (from proven v11.2opt - DO NOT CHANGE):
// - ATR Period: 12, Multiplier: 3.8
// - ADX Length: 17
// - Flip Threshold: 0.0%, Confirm Bars: 1
//
// SWEEP PARAMETERS FOR SHORTS (what you're optimizing):
// - RSI Short Min/Max (default 30-70, try 30-50, 25-45, etc.)
// - Short Position Min (default 5%, try 15%, 25%, 35%)
// - ADX Minimum (default 15, try 18, 20, 22)
// - Entry Buffer ATR (default -0.15, try 0.0, 0.1, 0.15)
//
// TP/SL SETTINGS (match production):
// - TP1: 1.45% (100% close)
// - SL: 2.0%
// =============================================================================
// === FIXED CORE PARAMETERS (from v11.2opt - DO NOT MODIFY) ===
atrPeriod = 12
multiplier = 3.8
adxLen = 17
confirmBars = 1
flipThreshold = 0.0
// === SHORT SWEEP PARAMETERS (OPTIMIZE THESE) ===
adxMin = input.int(15, "ADX Minimum", minval=5, maxval=35, group="SHORT Filters")
rsiShortMin = input.float(30, "RSI Short Min", minval=0, maxval=100, group="SHORT Filters")
rsiShortMax = input.float(70, "RSI Short Max", minval=0, maxval=100, group="SHORT Filters")
shortPosMin = input.float(5, "Short Position Min %", minval=0, maxval=100, group="SHORT Filters")
entryBufferATR = input.float(-0.15, "Entry Buffer (ATR)", minval=-1.0, maxval=1.0, step=0.05, group="SHORT Filters")
// === VOLUME FILTER (optional) ===
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 SETTINGS (match production) ===
tpPercent = input.float(1.45, "Take Profit %", minval=0.1, maxval=10.0, step=0.05, group="Exit")
slPercent = input.float(2.0, "Stop Loss %", minval=0.1, maxval=10.0, step=0.1, group="Exit")
// =============================================================================
// MONEY LINE CALCULATION (identical to indicator)
// =============================================================================
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)
thresholdAmount = tsl * (flipThreshold / 100)
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
// =============================================================================
// SHORT SIGNAL FILTERS
// =============================================================================
adxOk = adxVal >= adxMin
shortBufferOk = calcC < supertrend - entryBufferATR * atr
rsiShortOk = rsi14 >= rsiShortMin and rsi14 <= rsiShortMax
shortPositionOk = pricePosition > shortPosMin
volumeOk = not useVolumeFilter or (volumeRatio >= volMin and volumeRatio <= volMax)
// =============================================================================
// SHORT SIGNAL
// =============================================================================
sellFlip = trend == -1 and trend[1] == 1
sellReady = ta.barssince(sellFlip) == confirmBars
// FINAL SHORT SIGNAL (all filters applied)
finalShortSignal = sellReady and adxOk and shortBufferOk and rsiShortOk and shortPositionOk and volumeOk
// =============================================================================
// STRATEGY EXECUTION
// =============================================================================
// Entry
if finalShortSignal and strategy.position_size == 0
strategy.entry("Short", strategy.short)
// Exit with TP/SL
if strategy.position_size < 0
entryPrice = strategy.position_avg_price
tpPrice = entryPrice * (1 - tpPercent / 100)
slPrice = entryPrice * (1 + slPercent / 100)
strategy.exit("Exit", "Short", limit=tpPrice, stop=slPrice)
// =============================================================================
// 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(finalShortSignal, title="Short Signal", 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, "MODE", text_color=color.white)
table.cell(dbg, 1, 0, "SHORT ONLY", text_color=color.red)
table.cell(dbg, 0, 1, "Trend", text_color=color.white)
table.cell(dbg, 1, 1, trend == 1 ? "UP" : "DOWN ✓", text_color=trend == 1 ? color.gray : color.red)
table.cell(dbg, 0, 2, "ADX", text_color=color.white)
table.cell(dbg, 1, 2, str.tostring(adxVal, "#.#") + " >= " + str.tostring(adxMin) + (adxOk ? " ✓" : " ✗"), text_color=adxOk ? color.lime : color.red)
table.cell(dbg, 0, 3, "RSI", text_color=color.white)
table.cell(dbg, 1, 3, str.tostring(rsi14, "#.#") + " [" + str.tostring(rsiShortMin, "#") + "-" + str.tostring(rsiShortMax, "#") + "]" + (rsiShortOk ? " ✓" : " ✗"), text_color=rsiShortOk ? color.lime : color.red)
table.cell(dbg, 0, 4, "Position", text_color=color.white)
table.cell(dbg, 1, 4, str.tostring(pricePosition, "#.#") + "% > " + str.tostring(shortPosMin, "#") + "%" + (shortPositionOk ? " ✓" : " ✗"), text_color=shortPositionOk ? color.lime : color.red)
table.cell(dbg, 0, 5, "Buffer", text_color=color.white)
table.cell(dbg, 1, 5, shortBufferOk ? "OK ✓" : "—", text_color=shortBufferOk ? color.lime : color.gray)
table.cell(dbg, 0, 6, "TP/SL", text_color=color.white)
table.cell(dbg, 1, 6, str.tostring(tpPercent, "#.##") + "% / " + str.tostring(slPercent, "#.#") + "%", text_color=color.yellow)
table.cell(dbg, 0, 7, "Signal", text_color=color.white)
table.cell(dbg, 1, 7, finalShortSignal ? "SELL!" : "—", text_color=finalShortSignal ? color.red : color.gray)