const inp = $input.first().json; const a = inp.action; const t = inp.trade || {}; const p = inp.portfolio || {}; const btc = inp.btcSignal || {}; const ind = t.indicators || {}; const NL = '\n'; const now3 = new Date(Date.now() + 3 * 3600000); const timeStr = now3.toISOString().slice(11, 16) + ' UTC+3'; const btcLine = btc.trend ? 'BTC: ' + btc.trend.toUpperCase() + ' score:' + btc.score + ' RSI:' + btc.rsi + ' ROC5:' + btc.roc5 + '%' + NL : ''; let msg = ''; if (inp.orderStatus === 'ERROR') { msg = '\u26A0\uFE0F ORDER ERROR ' + timeStr + NL + (inp.message || 'Binance API error') + NL + 'Symbol: ' + (t.symbol||'?') + ' | Action: ' + (a||'?'); } else if (inp.orderStatus === 'SKIPPED') { if (inp.balanceFetchError) { msg = '\u26A0\uFE0F BALANCE FETCH ERROR ' + timeStr + NL + 'Cannot read USDT balance from Binance API' + NL + 'Check API key has Enable Reading permission on Binance' + NL + (inp.balanceFetchDiag || ''); } else { return []; } } else if (a === 'buy') { const fillPrice = t.realFillPrice || t.entryPrice || '?'; const spent = t.realQuoteSpent || t.tradeValue || '?'; msg = '\uD83D\uDE80 ENTRY: ' + (t.symbol||'?') + ' ' + timeStr + NL + 'Score: ' + (t.score||0) + ' | +' + (+(t.change24h||0)).toFixed(2) + '% today' + NL + 'Fill: $' + fillPrice + ' | Size: $' + spent + NL + 'TP: +' + (t.tpPct||'?') + '% | SL: -' + (t.slPct||'?') + '%' + NL + (ind.rsi ? 'RSI: ' + ind.rsi + ' | VolAccel: ' + ind.volAccel + 'x' + NL : '') + btcLine + 'Capital: $' + (t.capital||p.capital||'?') + ' | Trade #' + (t.todayTrades||1) + ' today'; } else if (a === 'close_switch') { const fillPrice = t.realFillPrice || t.exitPrice || '?'; const netPnl = t.realNetPnl !== undefined ? t.realNetPnl : (t.netPnl || 0); const pnlPct = t.realPnlPct !== undefined ? t.realPnlPct : (t.pnlPct || 0); const fees = t.realFees !== undefined ? t.realFees : (t.fees || 0); msg = '\uD83D\uDD04 SWITCH: Closed ' + (t.symbol||'?') + ' ' + timeStr + NL + 'Fill: $' + fillPrice + ' | ' + (pnlPct>=0?'+':'') + pnlPct + '% | Net: $' + netPnl + NL + 'Fees: $' + fees + ' | Held: ' + (t.holdMins||0) + ' min' + NL + btcLine + 'Opening best pair next cycle...'; } else if (a === 'close_tp') { const fillPrice = t.realFillPrice || t.exitPrice || '?'; const netPnl = t.realNetPnl !== undefined ? t.realNetPnl : (t.netPnl || 0); const pnlPct = t.realPnlPct !== undefined ? t.realPnlPct : (t.pnlPct || 0); const fees = t.realFees !== undefined ? t.realFees : (t.fees || 0); msg = '\u2705 TAKE PROFIT: ' + (t.symbol||'?') + ' ' + timeStr + NL + 'Entry: $' + (t.entryPrice||'?') + ' \u2192 Fill: $' + fillPrice + NL + 'Held: ' + (t.holdMins||0) + 'min | ' + (pnlPct>=0?'+':'') + pnlPct + '% | Net: $' + netPnl + '' + NL + 'Fees: $' + fees + ' | ' + (t.reason||'TP') + NL + 'W/L: ' + (t.wins||0) + 'W/' + (t.losses||0) + 'L (' + (t.winRate||0) + '%)' + NL + btcLine + 'Capital: $' + (t.capital||p.capital||'?') + ' | Total P&L: $' + (t.totalPnl||p.totalPnl||0); } else { const fillPrice = t.realFillPrice || t.exitPrice || '?'; const netPnl = t.realNetPnl !== undefined ? t.realNetPnl : (t.netPnl || 0); const pnlPct = t.realPnlPct !== undefined ? t.realPnlPct : (t.pnlPct || 0); const fees = t.realFees !== undefined ? t.realFees : (t.fees || 0); msg = '\uD83D\uDED1 STOP LOSS: ' + (t.symbol||'?') + ' ' + timeStr + NL + 'Entry: $' + (t.entryPrice||'?') + ' \u2192 Fill: $' + fillPrice + NL + 'Held: ' + (t.holdMins||0) + 'min | ' + pnlPct + '% | Net: $' + netPnl + NL + 'Fees: $' + fees + ' | ' + (t.reason||'SL') + NL + btcLine + 'Capital: $' + (t.capital||p.capital||'?') + ' | Total P&L: $' + (t.totalPnl||p.totalPnl||0); } if (!msg) return []; return [{ json: { message: msg, action: a, trade: t } }];