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 } }];