DCA Transaction Manager Workflow


创建日期: 2025-11-03 15:21:03 最后修改: 2025-11-03 15:42:56
复制: 0 点击次数: 97
avatar of ianzeng123 ianzeng123
2
关注
319
关注者
策略源码
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"operation\":\"sendMessage\",\"chatId\":{\"__rl\":true,\"value\":\"=8357264271\",\"mode\":\"id\"},\"text\":\"={{ $json.output }}\",\"parseMode\":\"HTML\"},\"type\":\"n8n-nodes-base.telegram\",\"typeVersion\":1.2,\"position\":[960,336],\"id\":\"2ab70ac8-51b0-44c5-b5ed-88ab8c48f028\",\"name\":\"Telegram Message Sender\",\"credentials\":{\"telegramApi\":{\"id\":\"64b40791-c845-4775-9d67-0efc4122d162\",\"name\":\"Telegram account\"}}},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"deepseek/deepseek-chat-v3.1\",\"mode\":\"list\",\"cachedResultName\":\"deepseek/deepseek-chat-v3.1\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[592,560],\"id\":\"666b23db-40c2-445a-b00f-1f18c1bc7ac7\",\"name\":\"AI Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=Please use FMZ's MCP to perform the following operation: \\n{{JSON.stringify($json.messageText)}}\\nPlease proceed and return the answer in english\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[560,336],\"id\":\"594d81c5-44c6-4af7-b0c8-5b303a6ff2fb\",\"name\":\"MCP Operation Executor\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const messageText = $input.first().json.message.text;\\n\\n// json must be an object, wrapped with value or other key names\\nreturn [\\n  {\\n    json: {messageText}\\n  }\\n];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[336,336],\"id\":\"a2ae7878-b666-477b-91d4-9cd3b3950a30\",\"name\":\"Telegram Message Processor\"},{\"parameters\":{\"endpointUrl\":\"https://www.fmz.com/api/mcp/338244b4b2c77edf39e705199a95a41c?token=759f04ab87553277c8fbcd97239453ae\",\"authentication\":\"bearerAuth\",\"credentials\":\"\",\"include\":\"all\"},\"type\":\"@n8n/n8n-nodes-langchain.mcpClientTool\",\"typeVersion\":1.1,\"position\":[720,560],\"id\":\"502a94fc-5b2f-45a6-b01d-08c8058c6edd\",\"name\":\"MCP Client\",\"credentials\":{\"httpBearerAuth\":{\"id\":\"636bfcb6-f73d-4487-80a6-574919e7dc15\",\"name\":\"fmzflowtest\"}}},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"seconds\",\"secondsInterval\":30}]},\"updates\":[\"*\"],\"additionalFields\":{}},\"type\":\"n8n-nodes-base.telegramTrigger\",\"typeVersion\":1,\"position\":[112,336],\"id\":\"c026936f-1993-4798-b7e4-a027bc2f7493\",\"name\":\"Telegram Trigger\",\"credentials\":{\"telegramApi\":{\"id\":\"64b40791-c845-4775-9d67-0efc4122d162\",\"name\":\"Telegram account\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== Initialization Check ==========\\nif (_G('invoketime') === null) {\\n  _G('invoketime', 0);\\n  _G('STARTTIME', Date.now());\\n  \\n  const initAccount = exchange.GetAccount();\\n  const ticker = exchange.GetTicker();\\n  const initBTCAmount = initAccount.Stocks || 0;\\n  const initUSDTBalance = initAccount.Balance || 0;\\n  const initPrice = ticker.Last;\\n  const initBTCValue = initBTCAmount * initPrice;\\n  const initTotalValue = initUSDTBalance + initBTCValue;\\n  \\n  _G('initBTC', initBTCAmount);\\n  _G('initUSDT', initUSDTBalance);\\n  _G('initPrice', initPrice);\\n  _G('initBTCValue', initBTCValue);\\n  _G('initTotalValue', initTotalValue);\\n  _G('approveCount', 0);\\n  _G('rejectCount', 0);\\n  _G('totalDecisions', 0);\\n  \\n  Log(`📊 Account Initialization: BTC=${_N(initBTCAmount, 8)}, USDT=${_N(initUSDTBalance, 2)}, Initial Price=$${_N(initPrice, 2)}, Initial Total Value=$${_N(initTotalValue, 2)}`);\\n}\\n\\n_G('totalDecisions', _G('totalDecisions') + 1);\\n_G('rejectCount', _G('rejectCount') + 1);\\n\\nconst invoketime = _G('invoketime') + 1;\\n_G('invoketime', invoketime);\\n\\nconst duringtime = Math.floor((Date.now() - _G('STARTTIME')) / 60000);\\n\\nconst currentAccount = exchange.GetAccount();\\nconst currentTicker = exchange.GetTicker();\\nconst currentBTCAmount = currentAccount.Stocks || 0;\\nconst currentUSDTBalance = currentAccount.Balance || 0;\\nconst currentPrice = currentTicker.Last;\\nconst currentBTCValue = currentBTCAmount * currentPrice;\\nconst currentTotalValue = currentUSDTBalance + currentBTCValue;\\n\\nconst initBTC = _G('initBTC');\\nconst initUSDT = _G('initUSDT');\\nconst initPrice = _G('initPrice');\\nconst initBTCValue = _G('initBTCValue');\\nconst initTotalValue = _G('initTotalValue');\\n\\nconst totalProfit = currentTotalValue - initTotalValue;\\nconst totalProfitPercent = initTotalValue > 0 ? ((totalProfit / initTotalValue) * 100).toFixed(2) : '0.00';\\n\\nLogProfit(totalProfit, \\\"&\\\");\\n\\nconst approveCount = _G('approveCount');\\nconst rejectCount = _G('rejectCount');\\nconst totalDecisions = _G('totalDecisions');\\n\\n// ========== Configuration Parameters ==========\\nconst TRADE_SYMBOL = $vars.contract || 'BTC';\\nconst TRADING_PAIR = `${TRADE_SYMBOL}_USDT`;\\n\\n// ========== Utility Functions ==========\\n\\nfunction parseDecision(decision) {\\n    try {\\n        if (Array.isArray(decision) && decision.length > 0) {\\n            return decision[0];\\n        }\\n        return decision;\\n    } catch (e) {\\n        Log(\\\"❌ Decision parsing failed:\\\", e.message);\\n        return null;\\n    }\\n}\\n\\nfunction parseDecisionType(decision) {\\n    const text = decision.toLowerCase();\\n    \\n    const decisionMap = {\\n        'major increase': { action: 'buy', description: 'Major Position Increase', emoji: '🚀' },\\n        'moderate increase': { action: 'buy', description: 'Moderate Position Increase', emoji: '📈' },\\n        'minor increase': { action: 'buy', description: 'Minor Position Increase', emoji: '➕' },\\n        'standard investment': { action: 'buy', description: 'Standard Investment', emoji: '💰' },\\n        'hold position': { action: 'hold', description: 'Hold Position', emoji: '⏸️' },\\n        'pause and observe': { action: 'hold', description: 'Pause and Observe', emoji: '⏸️' },\\n        'no action': { action: 'hold', description: 'No Action', emoji: '⏸️' },\\n        'continue observing': { action: 'hold', description: 'Continue Observing', emoji: '⏸️' }\\n    };\\n    \\n    for (const [key, value] of Object.entries(decisionMap)) {\\n        if (text.includes(key)) {\\n            return value;\\n        }\\n    }\\n    \\n    return { action: 'hold', description: 'Hold Position', emoji: '⏸️' };\\n}\\n\\n// ========== History Management ==========\\n\\nfunction saveRejectionRecord(decision, price) {\\n    const history = _G('dca_history') || [];\\n    \\n    const now = new Date();\\n    const year = now.getFullYear();\\n    const month = String(now.getMonth() + 1).padStart(2, '0');\\n    const day = String(now.getDate()).padStart(2, '0');\\n    const hour = String(now.getHours()).padStart(2, '0');\\n    const minute = String(now.getMinutes()).padStart(2, '0');\\n    const second = String(now.getSeconds()).padStart(2, '0');\\n    const formattedTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;\\n    \\n    const record = {\\n        timestamp: now.toISOString(),\\n        datetime: formattedTime,\\n        symbol: TRADE_SYMBOL,\\n        decision: decision.tradingDecision,\\n        planned_amount_usd: decision.tradingAmount,\\n        actual_amount_usd: 0,\\n        quantity: 0,\\n        price: price,\\n        executed: false,\\n        approved: false,\\n        reason: decision.tradingRationale\\n    };\\n    \\n    history.push(record);\\n    _G('dca_history', history);\\n    \\n    return history;\\n}\\n\\nfunction getStrategyStats(history, currentPrice) {\\n    if (!history || history.length === 0) {\\n        return {\\n            runtime: '-',\\n            totalDecisions: 0,\\n            executedCount: 0,\\n            totalInvested: 0,\\n            totalQuantity: 0,\\n            avgPrice: 0,\\n            currentValue: 0,\\n            currentProfit: 0,\\n            profitPercent: 0\\n        };\\n    }\\n    \\n    const symbolHistory = history.filter(r => r.symbol === TRADE_SYMBOL);\\n    \\n    if (symbolHistory.length === 0) {\\n        return {\\n            runtime: '-',\\n            totalDecisions: 0,\\n            executedCount: 0,\\n            totalInvested: 0,\\n            totalQuantity: 0,\\n            avgPrice: 0,\\n            currentValue: 0,\\n            currentProfit: 0,\\n            profitPercent: 0\\n        };\\n    }\\n    \\n    const startTime = new Date(symbolHistory[0].timestamp);\\n    const now = new Date();\\n    const runtimeHours = Math.floor((now - startTime) / (1000 * 60 * 60));\\n    const runtimeDays = Math.floor(runtimeHours / 24);\\n    \\n    const executedRecords = symbolHistory.filter(r => r.executed);\\n    const totalInvested = executedRecords.reduce((sum, r) => sum + (r.actual_amount_usd || 0), 0);\\n    const totalQuantity = executedRecords.reduce((sum, r) => sum + (r.quantity || 0), 0);\\n    const avgPrice = totalQuantity > 0 ? totalInvested / totalQuantity : 0;\\n    const currentValue = totalQuantity * currentPrice;\\n    const currentProfit = currentValue - totalInvested;\\n    const profitPercent = totalInvested > 0 ? (currentProfit / totalInvested * 100) : 0;\\n    \\n    return {\\n        runtime: runtimeDays > 0 ? `${runtimeDays}d${runtimeHours % 24}h` : `${runtimeHours}h`,\\n        totalDecisions: symbolHistory.length,\\n        executedCount: executedRecords.length,\\n        totalInvested: totalInvested,\\n        totalQuantity: totalQuantity,\\n        avgPrice: avgPrice,\\n        currentValue: currentValue,\\n        currentProfit: currentProfit,\\n        profitPercent: profitPercent\\n    };\\n}\\n\\n// ========== Table Generation (Simplified) ==========\\n\\nfunction generateAccountTable(initUSDT, currentUSDTBalance, initBTC, currentBTCAmount, initTotalValue, currentTotalValue, totalProfit, totalProfitPercent) {\\n    const profitEmoji = totalProfit >= 0 ? \\\"🟢\\\" : \\\"🔴\\\";\\n    \\n    return {\\n        type: \\\"table\\\", \\n        title: `💼 Account Overview (${TRADE_SYMBOL})`,\\n        cols: [\\\"Type\\\", \\\"Initial\\\", \\\"Current\\\", \\\"Profit Amount\\\", \\\"Profit Rate\\\"],\\n        rows: [\\n            [\\n                \\\"USDT Balance\\\",\\n                `$${_N(initUSDT, 2)}`,\\n                `$${_N(currentUSDTBalance, 2)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                `${TRADE_SYMBOL} Holdings`,\\n                `${_N(initBTC, 8)}`,\\n                `${_N(currentBTCAmount, 8)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                \\\"Total Account Value\\\",\\n                `$${_N(initTotalValue, 2)}`,\\n                `$${_N(currentTotalValue, 2)}`,\\n                `${profitEmoji} $${_N(totalProfit, 2)}`,\\n                `${profitEmoji} ${totalProfitPercent}%`\\n            ]\\n        ]\\n    };\\n}\\n\\nfunction generateDCATable(history, stats) {\\n    const table = {\\n        type: \\\"table\\\",\\n        title: `📊 DCA Information (${TRADE_SYMBOL})`,\\n        cols: [\\\"Time\\\", \\\"Decision\\\", \\\"Plan\\\", \\\"Price\\\", \\\"Rationale\\\", \\\"Status\\\"],\\n        rows: []\\n    };\\n    \\n    const symbolHistory = history.filter(r => r.symbol === TRADE_SYMBOL);\\n    const recentHistory = symbolHistory.slice(-10);\\n    \\n    recentHistory.forEach(record => {\\n        const timeStr = record.datetime;\\n        let statusStr;\\n        if (record.approved !== undefined) {\\n            statusStr = record.approved ? \\\"✅ Approved\\\" : \\\"🚫 Rejected\\\";\\n        } else {\\n            statusStr = record.executed ? \\\"✅ Executed\\\" : \\\"⏸️ Skipped\\\";\\n        }\\n        \\n        const planStr = record.executed ? `$${_N(record.actual_amount_usd, 0)}` : `$${_N(record.planned_amount_usd, 0)}`;\\n        const priceStr = `$${_N(record.price, 0)}`;\\n        const reasonStr = (record.reason || '').substring(0, 50);\\n        \\n        table.rows.push([\\n            timeStr,\\n            record.decision,\\n            planStr,\\n            priceStr,\\n            reasonStr,\\n            statusStr\\n        ]);\\n    });\\n    \\n    table.rows.push([\\n        `Runtime ${stats.runtime}`,\\n        `Total ${stats.totalDecisions}`,\\n        `Invested $${_N(stats.totalInvested, 0)}`,\\n        `Avg Price $${_N(stats.avgPrice, 0)}`,\\n        `${_N(stats.totalQuantity, 6)} ${TRADE_SYMBOL}`,\\n        `Executed ${stats.executedCount}`\\n    ]);\\n    \\n    return table;\\n}\\n\\n// ========== Main Logic - Manual Rejection ==========\\n\\nfunction main() {\\n    const decision = parseDecision($node[\\\"AI Result Analysis\\\"].json);\\n    \\n    if (!decision) {\\n        Log(\\\"❌ No valid decision obtained\\\");\\n        return { json: { success: false, error: \\\"Invalid decision\\\" } };\\n    }\\n    \\n    Log(\\\"=\\\".repeat(60));\\n    Log(`🚫 ${TRADE_SYMBOL} AI DCA Decision Analysis - Manual Rejection`);\\n    Log(\\\"=\\\".repeat(60));\\n    Log(`💡 Decision: ${decision.tradingDecision}`);\\n    Log(`💰 Amount: $${decision.tradingAmount}`);\\n    Log(`📝 Rationale: ${decision.tradingRationale}...`);\\n    Log(`🚫 Manual Decision: Rejected (No trade execution)`);\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    const actionInfo = parseDecisionType(decision.tradingDecision);\\n    Log(`🎯 AI Recommendation: ${actionInfo.emoji} ${actionInfo.description}`);\\n    \\n    exchange.SetCurrency(TRADING_PAIR);\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker) {\\n        Log(\\\"❌ Failed to get price\\\");\\n        return { json: { success: false, error: \\\"Failed to get price\\\" } };\\n    }\\n    \\n    const currentPrice = ticker.Last;\\n    Log(`💵 Current Price: $${currentPrice}`);\\n    \\n    Log(\\\"\\\\n🚫🚫🚫 Manually Rejected, No Trade Execution 🚫🚫🚫\\\\n\\\");\\n    \\n    saveRejectionRecord(decision, currentPrice);\\n    \\n    const rejectResult = {\\n        success: true,\\n        executed: false,\\n        rejected: true,\\n        action: 'rejected',\\n        decision: decision.tradingDecision,\\n        plannedAmount: decision.tradingAmount,\\n        actualAmount: 0,\\n        quantity: 0,\\n        price: currentPrice\\n    };\\n    \\n    const history = _G('dca_history') || [];\\n    const stats = getStrategyStats(history, currentPrice);\\n    \\n    const accountTable = generateAccountTable(\\n        initUSDT, currentUSDTBalance, initBTC, currentBTCAmount,\\n        initTotalValue, currentTotalValue, totalProfit, totalProfitPercent\\n    );\\n    \\n    const dcaTable = generateDCATable(history, stats);\\n    \\n    LogStatus(\\n        '`' + JSON.stringify(accountTable) + '`\\\\n\\\\n' +\\n        '`' + JSON.stringify(dcaTable) + '`'\\n    );\\n    \\n    Log(\\\"\\\\n\\\" + \\\"=\\\".repeat(60));\\n    Log(\\\"🚫 Manually rejected, no trade executed\\\");\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    return rejectResult;\\n}\\n\\nconst rejectResult = main();\\n\\nreturn [{\\n  json: {\\n    invoketime: invoketime,\\n    duringtime: duringtime + ' minutes',\\n    tradeSymbol: TRADE_SYMBOL,\\n    \\n    initUSDT: _N(initUSDT, 2),\\n    currentUSDT: _N(currentUSDTBalance, 2),\\n    initBTC: _N(initBTC, 8),\\n    currentBTC: _N(currentBTCAmount, 8),\\n    initPrice: _N(initPrice, 2),\\n    currentPrice: _N(currentPrice, 2),\\n    initTotalValue: _N(initTotalValue, 2),\\n    currentTotalValue: _N(currentTotalValue, 2),\\n    totalProfit: _N(totalProfit, 2),\\n    totalProfitPercent: totalProfitPercent + '%',\\n    \\n    totalDecisions: totalDecisions,\\n    approveCount: approveCount,\\n    rejectCount: rejectCount,\\n    currentDecision: '✗ Manual Rejection',\\n    \\n    rejected: true,\\n    tradeExecuted: false,\\n    tradeSuccess: true,\\n    aiDecision: rejectResult.decision,\\n    aiPlannedAmount: rejectResult.plannedAmount,\\n    actualAmount: 0,\\n    tradeQuantity: 0,\\n    tradePrice: rejectResult.price\\n  }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[2480,-496],\"id\":\"8564d51b-7200-4a2c-b78d-172686391bf6\",\"name\":\"Manual Trade Rejection\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== Initialization Check ==========\\nif (_G('invoketime') === null) {\\n  _G('invoketime', 0);\\n  _G('STARTTIME', Date.now());\\n  \\n  const initAccount = exchange.GetAccount();\\n  const ticker = exchange.GetTicker();\\n  const initBTCAmount = initAccount.Stocks || 0;\\n  const initUSDTBalance = initAccount.Balance || 0;\\n  const initPrice = ticker.Last;\\n  const initBTCValue = initBTCAmount * initPrice;\\n  const initTotalValue = initUSDTBalance + initBTCValue;\\n  \\n  _G('initBTC', initBTCAmount);\\n  _G('initUSDT', initUSDTBalance);\\n  _G('initPrice', initPrice);\\n  _G('initBTCValue', initBTCValue);\\n  _G('initTotalValue', initTotalValue);\\n  _G('approveCount', 0);\\n  _G('rejectCount', 0);\\n  _G('totalDecisions', 0);\\n  \\n  Log(`📊 Account Initialization: BTC=${_N(initBTCAmount, 8)}, USDT=${_N(initUSDTBalance, 2)}, Initial Price=$${_N(initPrice, 2)}, Initial Total Value=$${_N(initTotalValue, 2)}`);\\n}\\n\\n_G('totalDecisions', _G('totalDecisions') + 1);\\n_G('approveCount', _G('approveCount') + 1);\\n\\nconst invoketime = _G('invoketime') + 1;\\n_G('invoketime', invoketime);\\n\\nconst duringtime = Math.floor((Date.now() - _G('STARTTIME')) / 60000);\\n\\nconst currentAccount = exchange.GetAccount();\\nconst currentTicker = exchange.GetTicker();\\nconst currentBTCAmount = currentAccount.Stocks || 0;\\nconst currentUSDTBalance = currentAccount.Balance || 0;\\nconst currentPrice = currentTicker.Last;\\nconst currentBTCValue = currentBTCAmount * currentPrice;\\nconst currentTotalValue = currentUSDTBalance + currentBTCValue;\\n\\nconst initBTC = _G('initBTC');\\nconst initUSDT = _G('initUSDT');\\nconst initPrice = _G('initPrice');\\nconst initBTCValue = _G('initBTCValue');\\nconst initTotalValue = _G('initTotalValue');\\n\\nconst totalProfit = currentTotalValue - initTotalValue;\\nconst totalProfitPercent = initTotalValue > 0 ? ((totalProfit / initTotalValue) * 100).toFixed(2) : '0.00';\\n\\nLogProfit(totalProfit, \\\"&\\\");\\n\\nconst approveCount = _G('approveCount');\\nconst rejectCount = _G('rejectCount');\\nconst totalDecisions = _G('totalDecisions');\\n\\n// ========== Configuration Parameters ==========\\nconst TRADE_SYMBOL = $vars.contract || 'BTC';\\nconst TRADING_PAIR = `${TRADE_SYMBOL}_USDT`;\\n\\n// ========== Utility Functions ==========\\n\\nfunction parseDecision(decision) {\\n    try {\\n        if (Array.isArray(decision) && decision.length > 0) {\\n            return decision[0];\\n        }\\n        return decision;\\n    } catch (e) {\\n        Log(\\\"❌ Decision parsing failed:\\\", e.message);\\n        return null;\\n    }\\n}\\n\\nfunction parseDecisionType(decision) {\\n    const text = decision.toLowerCase();\\n    \\n    const decisionMap = {\\n        'major increase': { action: 'buy', description: 'Major Position Increase', emoji: '🚀' },\\n        'moderate increase': { action: 'buy', description: 'Moderate Position Increase', emoji: '📈' },\\n        'minor increase': { action: 'buy', description: 'Minor Position Increase', emoji: '➕' },\\n        'standard investment': { action: 'buy', description: 'Standard Investment', emoji: '💰' },\\n        'hold position': { action: 'hold', description: 'Hold Position', emoji: '⏸️' },\\n        'pause and observe': { action: 'hold', description: 'Pause and Observe', emoji: '⏸️' },\\n        'no action': { action: 'hold', description: 'No Action', emoji: '⏸️' },\\n        'continue observing': { action: 'hold', description: 'Continue Observing', emoji: '⏸️' }\\n    };\\n    \\n    for (const [key, value] of Object.entries(decisionMap)) {\\n        if (text.includes(key)) {\\n            return value;\\n        }\\n    }\\n    \\n    return { action: 'hold', description: 'Hold Position', emoji: '⏸️' };\\n}\\n\\n// ========== History Management ==========\\n\\nfunction saveDecisionRecord(decision, tradeInfo, executed) {\\n    const history = _G('dca_history') || [];\\n    \\n    const now = new Date();\\n    const year = now.getFullYear();\\n    const month = String(now.getMonth() + 1).padStart(2, '0');\\n    const day = String(now.getDate()).padStart(2, '0');\\n    const hour = String(now.getHours()).padStart(2, '0');\\n    const minute = String(now.getMinutes()).padStart(2, '0');\\n    const second = String(now.getSeconds()).padStart(2, '0');\\n    const formattedTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;\\n    \\n    const record = {\\n        timestamp: now.toISOString(),\\n        datetime: formattedTime,\\n        symbol: TRADE_SYMBOL,\\n        decision: decision.tradingDecision,\\n        planned_amount_usd: decision.tradingAmount,\\n        actual_amount_usd: tradeInfo.actualAmount || 0,\\n        quantity: tradeInfo.quantity || 0,\\n        price: tradeInfo.price,\\n        executed: executed,\\n        approved: true,\\n        reason: decision.tradingRationale\\n    };\\n    \\n    history.push(record);\\n    _G('dca_history', history);\\n    \\n    return history;\\n}\\n\\nfunction getStrategyStats(history, currentPrice) {\\n    if (!history || history.length === 0) {\\n        return {\\n            runtime: '-',\\n            totalDecisions: 0,\\n            executedCount: 0,\\n            totalInvested: 0,\\n            totalQuantity: 0,\\n            avgPrice: 0,\\n            currentValue: 0,\\n            currentProfit: 0,\\n            profitPercent: 0\\n        };\\n    }\\n    \\n    const symbolHistory = history.filter(r => r.symbol === TRADE_SYMBOL);\\n    \\n    if (symbolHistory.length === 0) {\\n        return {\\n            runtime: '-',\\n            totalDecisions: 0,\\n            executedCount: 0,\\n            totalInvested: 0,\\n            totalQuantity: 0,\\n            avgPrice: 0,\\n            currentValue: 0,\\n            currentProfit: 0,\\n            profitPercent: 0\\n        };\\n    }\\n    \\n    const startTime = new Date(symbolHistory[0].timestamp);\\n    const now = new Date();\\n    const runtimeHours = Math.floor((now - startTime) / (1000 * 60 * 60));\\n    const runtimeDays = Math.floor(runtimeHours / 24);\\n    \\n    const executedRecords = symbolHistory.filter(r => r.executed);\\n    const totalInvested = executedRecords.reduce((sum, r) => sum + (r.actual_amount_usd || 0), 0);\\n    const totalQuantity = executedRecords.reduce((sum, r) => sum + (r.quantity || 0), 0);\\n    const avgPrice = totalQuantity > 0 ? totalInvested / totalQuantity : 0;\\n    const currentValue = totalQuantity * currentPrice;\\n    const currentProfit = currentValue - totalInvested;\\n    const profitPercent = totalInvested > 0 ? (currentProfit / totalInvested * 100) : 0;\\n    \\n    return {\\n        runtime: runtimeDays > 0 ? `${runtimeDays}d${runtimeHours % 24}h` : `${runtimeHours}h`,\\n        totalDecisions: symbolHistory.length,\\n        executedCount: executedRecords.length,\\n        totalInvested: totalInvested,\\n        totalQuantity: totalQuantity,\\n        avgPrice: avgPrice,\\n        currentValue: currentValue,\\n        currentProfit: currentProfit,\\n        profitPercent: profitPercent\\n    };\\n}\\n\\n// ========== Spot Buy Function ==========\\n\\nfunction executeSpotBuy(symbol, usdAmount, currentPrice) {\\n    try {\\n        exchange.SetCurrency(symbol);\\n        \\n        Log(`💰 Spot Buy: $${usdAmount}`);\\n        \\n        const orderId = exchange.Buy(-1, usdAmount);\\n        \\n        if (orderId) {\\n            Sleep(2000);\\n            \\n            const order = exchange.GetOrder(orderId);\\n            if (order) {\\n                const actualQty = order.DealAmount || 0;\\n                const avgPrice = order.AvgPrice || currentPrice;\\n                const actualCost = order.DealAmount * order.AvgPrice || usdAmount;\\n                \\n                Log(`✅ Buy Successful: ${_N(actualQty, 6)} ${TRADE_SYMBOL} @ $${_N(avgPrice, 2)} (Cost $${_N(actualCost, 2)})`);\\n                \\n                return { \\n                    success: true, \\n                    orderId: orderId,\\n                    quantity: actualQty,\\n                    avgPrice: avgPrice,\\n                    actualCost: actualCost\\n                };\\n            } else {\\n                const estimatedQty = usdAmount / currentPrice;\\n                Log(`✅ Buy Successful (Estimated ${_N(estimatedQty, 6)} ${TRADE_SYMBOL})`);\\n                \\n                return { \\n                    success: true, \\n                    orderId: orderId,\\n                    quantity: estimatedQty,\\n                    avgPrice: currentPrice,\\n                    actualCost: usdAmount\\n                };\\n            }\\n        } else {\\n            Log(`❌ Buy Failed`);\\n            return { success: false, error: \\\"Order submission failed\\\" };\\n        }\\n    } catch (e) {\\n        Log(`❌ Buy Exception: ${e.message}`);\\n        return { success: false, error: e.message };\\n    }\\n}\\n\\n// ========== Table Generation (Simplified) ==========\\n\\nfunction generateAccountTable(initUSDT, currentUSDTBalance, initBTC, currentBTCAmount, initTotalValue, currentTotalValue, totalProfit, totalProfitPercent) {\\n    const profitEmoji = totalProfit >= 0 ? \\\"🟢\\\" : \\\"🔴\\\";\\n    \\n    return {\\n        type: \\\"table\\\", \\n        title: `💼 Account Overview (${TRADE_SYMBOL})`,\\n        cols: [\\\"Type\\\", \\\"Initial\\\", \\\"Current\\\", \\\"Profit Amount\\\", \\\"Profit Rate\\\"],\\n        rows: [\\n            [\\n                \\\"USDT Balance\\\",\\n                `$${_N(initUSDT, 2)}`,\\n                `$${_N(currentUSDTBalance, 2)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                `${TRADE_SYMBOL} Holdings`,\\n                `${_N(initBTC, 8)}`,\\n                `${_N(currentBTCAmount, 8)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                \\\"Total Account Value\\\",\\n                `$${_N(initTotalValue, 2)}`,\\n                `$${_N(currentTotalValue, 2)}`,\\n                `${profitEmoji} $${_N(totalProfit, 2)}`,\\n                `${profitEmoji} ${totalProfitPercent}%`\\n            ]\\n        ]\\n    };\\n}\\n\\nfunction generateDCATable(history, stats) {\\n    const table = {\\n        type: \\\"table\\\",\\n        title: `📊 DCA Information (${TRADE_SYMBOL})`,\\n        cols: [\\\"Time\\\", \\\"Decision\\\", \\\"Plan\\\", \\\"Price\\\", \\\"Rationale\\\", \\\"Status\\\"],\\n        rows: []\\n    };\\n    \\n    const symbolHistory = history.filter(r => r.symbol === TRADE_SYMBOL);\\n    const recentHistory = symbolHistory.slice(-10);\\n    \\n    recentHistory.forEach(record => {\\n        const timeStr = record.datetime;\\n        let statusStr;\\n        if (record.approved !== undefined) {\\n            statusStr = record.approved ? \\\"✅ Approved\\\" : \\\"🚫 Rejected\\\";\\n        } else {\\n            statusStr = record.executed ? \\\"✅ Executed\\\" : \\\"⏸️ Skipped\\\";\\n        }\\n        \\n        const planStr = record.executed ? `$${_N(record.actual_amount_usd, 0)}` : `$${_N(record.planned_amount_usd, 0)}`;\\n        const priceStr = `$${_N(record.price, 0)}`;\\n        const reasonStr = (record.reason || '').substring(0, 50);\\n        \\n        \\n        table.rows.push([\\n            timeStr,\\n            record.decision,\\n            planStr,\\n            priceStr,\\n            reasonStr,\\n            statusStr\\n        ]);\\n    });\\n    \\n    table.rows.push([\\n        `Runtime ${stats.runtime}`,\\n        `Total ${stats.totalDecisions}`,\\n        `Invested $${_N(stats.totalInvested, 0)}`,\\n        `Avg Price $${_N(stats.avgPrice, 0)}`,\\n        `${_N(stats.totalQuantity, 6)} ${TRADE_SYMBOL}`,\\n        `Executed ${stats.executedCount}`\\n    ]);\\n    \\n    return table;\\n}\\n\\n// ========== Main Logic ==========\\n\\nfunction main() {\\n    const decision = parseDecision($node[\\\"AI Result Analysis\\\"].json);\\n    \\n    if (!decision) {\\n        Log(\\\"❌ No valid decision obtained\\\");\\n        return { json: { success: false, error: \\\"Invalid decision\\\" } };\\n    }\\n    \\n    Log(\\\"=\\\".repeat(60));\\n    Log(`📊 ${TRADE_SYMBOL} AI DCA Decision Analysis - Manual Approval`);\\n    Log(\\\"=\\\".repeat(60));\\n    Log(`💡 Decision: ${decision.tradingDecision}`);\\n    Log(`💰 Amount: $${decision.tradingAmount}`);\\n    Log(`📝 Rationale: ${decision.tradingRationale}...`);\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    const actionInfo = parseDecisionType(decision.tradingDecision);\\n    Log(`🎯 ${actionInfo.emoji} ${actionInfo.description}`);\\n    \\n    exchange.SetCurrency(TRADING_PAIR);\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker) {\\n        Log(\\\"❌ Failed to get price\\\");\\n        return { json: { success: false, error: \\\"Failed to get price\\\" } };\\n    }\\n    \\n    const currentPrice = ticker.Last;\\n    Log(`💵 Current Price: $${currentPrice}`);\\n    \\n    let executeResult = {\\n        success: false,\\n        executed: false,\\n        orderId: null,\\n        error: null\\n    };\\n    \\n    let tradeInfo = {\\n        price: currentPrice,\\n        quantity: 0,\\n        actualAmount: 0,\\n        avgPrice: currentPrice\\n    };\\n    \\n    if (actionInfo.action === 'buy' && decision.tradingAmount > 0) {\\n        Log(`\\\\n🚀 Preparing to execute spot buy`);\\n        \\n        const result = executeSpotBuy(TRADING_PAIR, decision.tradingAmount, currentPrice);\\n        \\n        executeResult = {\\n            ...result,\\n            executed: result.success\\n        };\\n        \\n        if (result.success) {\\n            tradeInfo.quantity = result.quantity;\\n            tradeInfo.actualAmount = result.actualCost || decision.tradingAmount;\\n            tradeInfo.avgPrice = result.avgPrice || currentPrice;\\n        }\\n        \\n        saveDecisionRecord(decision, tradeInfo, executeResult.executed);\\n    } else {\\n        Log(\\\"⏸️ Hold Position\\\");\\n        executeResult.executed = false;\\n        executeResult.success = true;\\n        \\n        saveDecisionRecord(decision, tradeInfo, false);\\n    }\\n    \\n    const history = _G('dca_history') || [];\\n    const stats = getStrategyStats(history, currentPrice);\\n    \\n    const accountTable = generateAccountTable(\\n        initUSDT, currentUSDTBalance, initBTC, currentBTCAmount,\\n        initTotalValue, currentTotalValue, totalProfit, totalProfitPercent\\n    );\\n    \\n    const dcaTable = generateDCATable(history, stats);\\n    \\n    LogStatus(\\n        '`' + JSON.stringify(accountTable) + '`\\\\n\\\\n' +\\n        '`' + JSON.stringify(dcaTable) + '`'\\n    );\\n    \\n    Log(\\\"\\\\n\\\" + \\\"=\\\".repeat(60));\\n    Log(executeResult.executed ? \\\"✅ Trade execution completed\\\" : \\\"⏸️ No trade operation needed\\\");\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    return {\\n        success: executeResult.success,\\n        executed: executeResult.executed,\\n        action: actionInfo.action,\\n        decision: decision.tradingDecision,\\n        plannedAmount: decision.tradingAmount,\\n        actualAmount: tradeInfo.actualAmount,\\n        quantity: tradeInfo.quantity,\\n        price: currentPrice,\\n        orderId: executeResult.orderId\\n    };\\n}\\n\\nconst tradeResult = main();\\n\\nreturn [{\\n  json: {\\n    invoketime: invoketime,\\n    duringtime: duringtime + ' minutes',\\n    tradeSymbol: TRADE_SYMBOL,\\n    \\n    initUSDT: _N(initUSDT, 2),\\n    currentUSDT: _N(currentUSDTBalance, 2),\\n    initBTC: _N(initBTC, 8),\\n    currentBTC: _N(currentBTCAmount, 8),\\n    initPrice: _N(initPrice, 2),\\n    currentPrice: _N(currentPrice, 2),\\n    initTotalValue: _N(initTotalValue, 2),\\n    currentTotalValue: _N(currentTotalValue, 2),\\n    totalProfit: _N(totalProfit, 2),\\n    totalProfitPercent: totalProfitPercent + '%',\\n    \\n    totalDecisions: totalDecisions,\\n    approveCount: approveCount,\\n    rejectCount: rejectCount,\\n    currentDecision: '✓ Manual Approval',\\n    \\n    tradeExecuted: tradeResult.executed,\\n    tradeSuccess: tradeResult.success,\\n    tradeAction: tradeResult.action,\\n    aiDecision: tradeResult.decision,\\n    aiPlannedAmount: tradeResult.plannedAmount,\\n    actualAmount: tradeResult.actualAmount,\\n    tradeQuantity: tradeResult.quantity,\\n    tradePrice: tradeResult.price,\\n    orderId: tradeResult.orderId || '-'\\n  }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[2480,-688],\"id\":\"36846b5d-8c6b-4923-9327-511f266473ad\",\"name\":\"DCA Trade Execution\"},{\"parameters\":{\"mode\":\"rules\",\"rules\":{\"values\":[{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"strict\",\"version\":2},\"conditions\":[{\"leftValue\":\"={{ $json['tradingDecisionConfirm'] }}\",\"rightValue\":\"Yes\",\"operator\":{\"type\":\"string\",\"operation\":\"equals\"},\"id\":\"9c0a7b2e-58c3-4a34-af76-93c8ba3a7100\"}],\"combinator\":\"and\"},\"renameOutput\":false},{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"strict\",\"version\":2},\"conditions\":[{\"id\":\"f2e69ae0-0dea-4654-84e8-881119a47f8e\",\"leftValue\":\"={{ $json['tradingDecisionConfirm'] }}\",\"rightValue\":\"No\",\"operator\":{\"type\":\"string\",\"operation\":\"equals\",\"name\":\"filter.operator.equals\"}}],\"combinator\":\"and\"},\"renameOutput\":false}]},\"looseTypeValidation\":false,\"options\":{}},\"type\":\"n8n-nodes-base.switch\",\"typeVersion\":3.2,\"position\":[2256,-592],\"id\":\"9e9e344b-49b1-4753-aece-409f4f6669b0\",\"name\":\"Decision Branch\"},{\"parameters\":{\"resume\":\"form\",\"formTitle\":\"Trading Decision Confirmation\",\"formDescription\":\"\",\"formFields\":{\"values\":[{\"fieldLabel\":\"tradingDecisionConfirm\",\"fieldType\":\"dropdown\",\"fieldOptions\":{\"values\":[{\"option\":\"Yes\"},{\"option\":\"No\"}]},\"multiselect\":false,\"requiredField\":false}]},\"pushNotifications\":true,\"autoPopup\":true,\"limitWaitTime\":true,\"limitType\":\"afterTimeInterval\",\"resumeAmount\":1,\"resumeUnit\":\"minutes\"},\"type\":\"n8n-nodes-base.wait\",\"typeVersion\":1.1,\"position\":[2032,-592],\"id\":\"72613e10-950a-4a05-839e-0038cc7f2823\",\"name\":\"Manual Decision Confirmation\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"function parseAIOutput(output) {\\n    try {\\n        const cleaned = output.replace(/```[a-z]*\\\\n?/gi, '').trim();\\n        const start = cleaned.indexOf('{');\\n        const end = cleaned.lastIndexOf('}');\\n        return JSON.parse(cleaned.substring(start, end + 1));\\n    } catch (e) {\\n        return {};\\n    }\\n}\\n\\n\\nconst signals = parseAIOutput($input.first().json.output);\\n\\nreturn signals\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[1808,-592],\"id\":\"144bae9a-b44c-4211-bedd-287859a2caf3\",\"name\":\"AI Result Analysis\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"deepseek/deepseek-chat-v3.1\",\"mode\":\"list\",\"cachedResultName\":\"deepseek/deepseek-chat-v3.1\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[1504,-368],\"id\":\"d4a7deb4-d22e-4911-b5e4-56a5b6f37d06\",\"name\":\"DCA Decision Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=You are an experienced cryptocurrency quantitative trading fund management analyst. Please conduct a multi-dimensional comprehensive analysis based on the following technical indicators and market data, and provide this week's DCA trading decision.\\n\\n【Market Data Overview】\\n- Base DCA Amount: {{$vars.dcamoney }} USD\\n- Adjustable Range: 0-{{ $vars.dcamoney * 2 }} USD (0 indicates pause DCA)\\n\\n【Technical Indicator Data】\\n**RSI Relative Strength Index:**\\n{{ $json.RSI }}\\n\\n**MACD Trend Indicator:**\\n{{ $json.MACD_HIST }}\\n\\n**ATR Volatility:**\\n{{ $json[\\\"ATR\\\"] }}\\n\\n**OBV Money Flow:**\\n{{ $json[\\\"OBV\\\"] }}\\n\\n**Market Sentiment Analysis:**\\n{{ JSON.stringify($json['sentimentAnalysis']) }}\\n\\n【Analysis Requirements】\\nPlease evaluate comprehensively from the following dimensions:\\n\\n1. **Trend Strength Analysis**\\n   - Assess MACD long/short status and trend strength\\n   - Determine if in a clear uptrend/downtrend\\n   - Identify early signals of trend reversal\\n\\n2. **Overbought/Oversold Assessment**\\n   - Judge if market is overheated or panicked based on RSI\\n   - Consider contrarian investment opportunities (increase position during panic)\\n   - Note: Crypto markets may experience sustained overbought or oversold conditions, need to combine with other indicators\\n\\n3. **Volatility Risk Assessment**\\n   - Analyze ATR volatility level and trend changes\\n   - Consider reducing single investment amount during high volatility to spread risk\\n   - May moderately increase investment during low volatility\\n   - Assess market uncertainty level\\n\\n4. **Capital Flow Health**\\n   - Whether OBV capital flow supports current price movement\\n   - Judge volume-price divergence risk\\n   - Analyze sustainability and strength of capital inflow/outflow\\n\\n5. **Market Sentiment Balance**\\n   - Short-term vs long-term sentiment divergence or resonance\\n   - Extreme panic often presents best accumulation opportunities\\n   - Extreme euphoria requires calm consideration of pause\\n   - Neutral sentiment maintains standard investment\\n\\n6. **Risk-Reward Ratio Assessment**\\n   - Comprehensively assess current position building risk-reward ratio\\n   - Consider long-term nature and safety of DCA strategy\\n   - Balance chip accumulation with risk control\\n   - Identify asymmetric opportunities (limited downside risk, huge upside potential)\\n\\n【DCA Investment Decision Principles】\\n- ✅ **Be greedy during panic and oversold conditions (major increase)** - Market panic is the best time to accumulate chips\\n- ✅ **Be active during pullbacks and corrections (moderate increase)** - Seize periodic lows\\n- ✅ **Persist during consolidation and uncertainty (standard investment)** - Maintain DCA discipline\\n- ⚠️ **Stay calm during euphoria and overbought conditions (pause and observe)** - Pause at extreme highs, wait for pullback\\n- ⚠️ **Flexible adjustment during high volatility** - Dynamically balance according to market conditions\\n- ⚠️ **Stay cautious during sustained capital outflow** - Avoid blind bottom fishing\\n\\n【Important Notes】\\n- Core of DCA strategy is long-term holding and cost smoothing, this strategy does not involve selling operations\\n- Profit-taking and exit should be executed in independent bull market top judgment modules\\n- Crypto markets are highly volatile, need to combine multiple indicators for comprehensive judgment, avoid single indicator misleading\\n- Pay attention to structural market changes and macroeconomic environment impact\\n\\n【Output Format Requirements】\\nPlease output strictly according to the following JSON format, do not include markdown code block markers:\\n\\n{\\n  \\\"tradingDecision\\\": \\\"Major Increase/Moderate Increase/Standard Investment/Pause and Observe\\\",\\n  \\\"tradingAmount\\\": 150,\\n  \\\"tradingRationale\\\": \\\"Detailed explanation of your analysis logic, including comprehensive judgment of various indicators and decision rationale\\\"\\n}\\n\\n【Decision Option Explanations】\\n- **Major Increase ({{ $vars.dcamoney * 1.5 }}-{{ $vars.dcamoney * 2 }} USD)**: Market shows extreme panic or deep pullback, multiple indicators show oversold with reversal signs, excellent risk-reward ratio\\n- **Moderate Increase ({{ $vars.dcamoney * 1.2 }}-{{ $vars.dcamoney * 1.49 }} USD)**: Market in pullback adjustment period, indicators show some downward pressure but long-term trend healthy, suitable for increased investment\\n- **Standard Investment ({{ $vars.dcamoney }} USD)**: Market in consolidation or unclear trend state, maintain DCA discipline, avoid being affected by short-term volatility\\n- **Pause and Observe (0 USD)**: Market shows extreme euphoria or serious overbought, multiple indicators show high-level risk, or market structure deterioration requires waiting for better timing\\n\\n【Output Requirements】\\n- Trading Decision: Must be one of the four options above\\n- Trading Amount: Must be 0 or integer between {{ Math.round($vars.dcamoney * 0.5) }} to {{ $vars.dcamoney * 2 }}\\n- Trading Rationale: At least 200 words, clearly explain indicator weights and logical reasoning process, including:\\n  * Current status and trend analysis of various technical indicators\\n  * Mutual verification, resonance, or divergence phenomena between indicators\\n  * Judgment of market cycle stage (bear market/consolidation/early bull/mid bull/late bull, etc.)\\n  * Balance of risks and opportunities, including potential downside risk and upside space\\n  * Final decision logical reasoning and confidence level\\n  * Particular attention to risk points or opportunity points\\n\\nNow begin analysis and provide decision.\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[1408,-592],\"id\":\"594fc237-f546-4e28-a83d-6ec69a0ffc55\",\"name\":\"DCA Decision Analysis\",\"retryOnFail\":true,\"waitBetweenTries\":5000},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Initialize containers\\nlet macdHistData = null;\\nlet rsiData = null;\\nlet atrData = null;\\nlet obvData = null;\\nlet sentimentData = null;\\n\\n// Counter for sequential identification\\nlet resultIndex = 0;\\n\\n// Loop over items\\nfor (const item of items) {\\n    \\n    // Sentiment indicators (with output field)\\n    if (item.json.output) {\\n        try {\\n            // Clean markdown code block markers from output\\n            const cleanOutput = item.json.output.replace(/```json\\\\n?|\\\\n?```/g, '').trim();\\n            sentimentData = JSON.parse(cleanOutput);\\n        } catch (e) {\\n            sentimentData = item.json.output;\\n        }\\n        continue;\\n    }\\n    \\n    // Process data with result field\\n    if (item.json.result && Array.isArray(item.json.result)) {\\n        const result = item.json.result;\\n        \\n        if (resultIndex === 0) {\\n            // 1st: MACD (3D array: [DIF, DEA, HIST])\\n            if (Array.isArray(result[0]) && result.length === 3) {\\n                // 3D array, take HIST (index 2), filter null and take recent 30\\n                const histArray = result[2].filter(v => v !== null);\\n                macdHistData = histArray.slice(-30);\\n            } else {\\n                // Single dimension array fallback\\n                const filtered = result.filter(v => v !== null);\\n                macdHistData = filtered.slice(-30);\\n            }\\n        } else if (resultIndex === 1) {\\n            // 2nd: RSI, filter null and take recent 30\\n            const filtered = result.filter(v => v !== null);\\n            rsiData = filtered.slice(-30);\\n        } else if (resultIndex === 2) {\\n            // 3rd: ATR, filter null and take recent 30\\n            const filtered = result.filter(v => v !== null);\\n            atrData = filtered.slice(-30);\\n        } else if (resultIndex === 3) {\\n            // 4th: OBV, filter null and take recent 30\\n            const filtered = result.filter(v => v !== null);\\n            obvData = filtered.slice(-30);\\n        }\\n        \\n        resultIndex++;\\n    }\\n}\\n\\n// Return result\\nreturn [{\\n    json: {\\n        \\\"MACD_HIST\\\": macdHistData,\\n        \\\"RSI\\\": rsiData,\\n        \\\"ATR\\\": atrData,\\n        \\\"OBV\\\": obvData,\\n        \\\"sentimentAnalysis\\\": sentimentData\\n    }\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[1184,-592],\"id\":\"b7e56d88-2ac4-4509-bbda-1cc7d9c4b39a\",\"name\":\"Data Integration\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"anthropic/claude-sonnet-4\",\"mode\":\"list\",\"cachedResultName\":\"anthropic/claude-sonnet-4\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[656,16],\"id\":\"c2284296-dd86-4851-b9b7-2d45628bc30e\",\"name\":\"Sentiment Analysis Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=You are a highly intelligent sentiment analyzer specialized in analyzing **cryptocurrency market sentiment and market expectations**. Please conduct cryptocurrency sentiment analysis for coin {{$vars.contract}} using the following two-part method on the provided text:\\n\\n**Short-term Sentiment Analysis:**\\n\\n* Assess immediate market reactions, including price volatility, social media heat, news events, on-chain data, and technical indicator signals;\\n* Determine overall market tendency: \\\"Positive\\\", \\\"Neutral\\\", or \\\"Negative\\\";\\n* Calculate a numerical sentiment score between **-1 (extremely negative)** and **1 (extremely positive)**;\\n* Provide concise reasoning for short-term sentiment, focusing on **latest market movements, trader behavior, news events, or technical trends**.\\n\\n**Long-term Sentiment Analysis:**\\n\\n* Assess long-term prospects of the crypto asset, including fundamentals, development progress, market adoption, macroeconomic and regulatory environment;\\n* Determine overall sentiment category: \\\"Positive\\\", \\\"Neutral\\\", or \\\"Negative\\\";\\n* Calculate a long-term sentiment score between **-1 (extremely negative)** and **1 (extremely positive)**;\\n* Provide detailed reasoning for long-term sentiment, focusing on **project potential, market structure, policy trends, and institutional participation**.\\n\\nOutput must be a strictly formatted JSON object containing two keys: \\\"shortTermSentiment\\\" and \\\"longTermSentiment\\\".\\nEach key's value is an object containing the following three fields: \\\"category\\\", \\\"score\\\", \\\"rationale\\\".\\nDo not output any additional text.\\n\\n**Output Example:**\\n\\n```json\\n{\\n  \\\"shortTermSentiment\\\": {\\n    \\\"category\\\": \\\"Positive\\\",\\n    \\\"score\\\": 0.7,\\n    \\\"rationale\\\": \\\"...\\\"\\n  },\\n  \\\"longTermSentiment\\\": {\\n    \\\"category\\\": \\\"Neutral\\\",\\n    \\\"score\\\": 0.0,\\n    \\\"rationale\\\": \\\"...\\\"\\n  }\\n}\\n```\\n\\n**Now, please analyze the following cryptocurrency market-related text and generate JSON output conforming to the above format:**\\n{{ $json.text }}\\n\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[560,-208],\"id\":\"7f936565-162c-4131-8008-e49af06f2919\",\"name\":\"AI Sentiment Analysis\",\"retryOnFail\":true,\"waitBetweenTries\":5000},{\"parameters\":{\"endpointUrl\":\"https://mcp.alphavantage.co/mcp?apikey='HCTH32FPOI6UPF66'\",\"authentication\":\"none\",\"tool\":\"NEWS_SENTIMENT\",\"tickers\":\"=CRYPTO:{{$vars.contract}}\"},\"type\":\"@n8n/n8n-nodes-langchain.mcpClient\",\"typeVersion\":1.1,\"position\":[336,-112],\"id\":\"0c79252c-bc23-4603-9e8e-ddf5b65f4f67\",\"name\":\"Crypto News Retrieval\"},{\"parameters\":{\"operation\":\"OBV\",\"inReal\":\"={{ $json.result }}\",\"inPriceV\":\"[]\"},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[656,-400],\"id\":\"7a9cbd3e-a7f3-44db-90d7-75faf7d03645\",\"name\":\"OBV\"},{\"parameters\":{\"operation\":\"ATR\",\"inReal\":\"={{ $json.result }}\",\"optInTimePeriod\":14},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[656,-592],\"id\":\"d89b6aa5-095d-49c7-833b-1566c5a85f0e\",\"name\":\"ATR\"},{\"parameters\":{\"operation\":\"RSI\",\"inReal\":\"={{ $json.result }}\",\"optInTimePeriod\":14},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[656,-784],\"id\":\"9173f360-862c-4603-ad6b-678f088b218a\",\"name\":\"RSI\"},{\"parameters\":{\"operation\":\"MACD\",\"inReal\":\"={{ $json.result }}\",\"optInFastPeriod\":12,\"optInSlowPeriod\":26,\"optInSignalPeriod\":9},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[656,-976],\"id\":\"3e64f173-9ffd-489e-84bb-8d97e6b12c78\",\"name\":\"MACD\"},{\"parameters\":{\"mode\":\"append\",\"numberInputs\":5},\"type\":\"n8n-nodes-base.merge\",\"typeVersion\":3.2,\"position\":[960,-640],\"id\":\"2027a15d-bf95-4b96-b16d-1ef2e8cbb0ee\",\"name\":\"Merge\"},{\"parameters\":{\"operation\":\"getRecords\",\"exchange\":0,\"symbol\":{\"__rl\":true,\"value\":\"={{$vars.contract}}_USDT\",\"mode\":\"id\"},\"period\":86400,\"limit\":100},\"type\":\"n8n-nodes-base.marketInfo\",\"typeVersion\":1,\"position\":[336,-688],\"id\":\"2170f346-eb3a-4848-a725-f7521a559056\",\"name\":\"Market Information\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":5}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[112,-448],\"id\":\"e0957a91-4232-46ea-8a2d-e1f39e3fb27d\",\"name\":\"Schedule Trigger\"}],\"pinData\":{},\"connections\":{\"AI Model\":{\"ai_languageModel\":[[{\"node\":\"MCP Operation Executor\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"MCP Operation Executor\":{\"main\":[[{\"node\":\"Telegram Message Sender\",\"type\":\"main\",\"index\":0}]]},\"Telegram Message Processor\":{\"main\":[[{\"node\":\"MCP Operation Executor\",\"type\":\"main\",\"index\":0}]]},\"MCP Client\":{\"ai_tool\":[[{\"node\":\"MCP Operation Executor\",\"type\":\"ai_tool\",\"index\":0}]]},\"Telegram Trigger\":{\"main\":[[{\"node\":\"Telegram Message Processor\",\"type\":\"main\",\"index\":0}]]},\"Decision Branch\":{\"main\":[[{\"node\":\"DCA Trade Execution\",\"type\":\"main\",\"index\":0}],[{\"node\":\"Manual Trade Rejection\",\"type\":\"main\",\"index\":0}]]},\"Manual Decision Confirmation\":{\"main\":[[{\"node\":\"Decision Branch\",\"type\":\"main\",\"index\":0}]]},\"AI Result Analysis\":{\"main\":[[{\"node\":\"Manual Decision Confirmation\",\"type\":\"main\",\"index\":0}]]},\"DCA Decision Model\":{\"ai_languageModel\":[[{\"node\":\"DCA Decision Analysis\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"DCA Decision Analysis\":{\"main\":[[{\"node\":\"AI Result Analysis\",\"type\":\"main\",\"index\":0}]]},\"Data Integration\":{\"main\":[[{\"node\":\"DCA Decision Analysis\",\"type\":\"main\",\"index\":0}]]},\"Sentiment Analysis Model\":{\"ai_languageModel\":[[{\"node\":\"AI Sentiment Analysis\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"AI Sentiment Analysis\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":4}]]},\"Crypto News Retrieval\":{\"main\":[[{\"node\":\"AI Sentiment Analysis\",\"type\":\"main\",\"index\":0}]]},\"OBV\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":3}]]},\"ATR\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":2}]]},\"RSI\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":1}]]},\"MACD\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":0}]]},\"Merge\":{\"main\":[[{\"node\":\"Data Integration\",\"type\":\"main\",\"index\":0}]]},\"Market Information\":{\"main\":[[{\"node\":\"OBV\",\"type\":\"main\",\"index\":0},{\"node\":\"ATR\",\"type\":\"main\",\"index\":0},{\"node\":\"RSI\",\"type\":\"main\",\"index\":0},{\"node\":\"MACD\",\"type\":\"main\",\"index\":0}]]},\"Schedule Trigger\":{\"main\":[[{\"node\":\"Crypto News Retrieval\",\"type\":\"main\",\"index\":0},{\"node\":\"Market Information\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"meta\":{\"templateCredsSetupCompleted\":true},\"credentials\":{},\"id\":\"a10fa198-f87f-4509-9a4a-9827a76a2371\",\"plugins\":{},\"mcpClients\":{\"NEWS_SENTIMENT\":{\"name\":\"NEWS_SENTIMENT\",\"description\":\"Returns live and historical market news & sentiment data from premier news outlets worldwide.\",\"properties\":[{\"displayName\":\"tickers\",\"name\":\"tickers\",\"type\":\"string\",\"description\":\"Stock/crypto/forex symbols to filter articles. Example: \\\"IBM\\\" or \\\"COIN,CRYPTO:BTC,FOREX:USD\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter tickers\"},{\"displayName\":\"topics\",\"name\":\"topics\",\"type\":\"string\",\"description\":\"News topics to filter by. Example: \\\"technology\\\" or \\\"technology,ipo\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter topics\"},{\"displayName\":\"time_from\",\"name\":\"time_from\",\"type\":\"string\",\"description\":\"Start time range in YYYYMMDDTHHMM format. Example: \\\"20220410T0130\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter time_from\"},{\"displayName\":\"time_to\",\"name\":\"time_to\",\"type\":\"string\",\"description\":\"End time range in YYYYMMDDTHHMM format. Defaults to current time if time_from specified.\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter time_to\"},{\"displayName\":\"sort\",\"name\":\"sort\",\"type\":\"string\",\"description\":\"Sort order - \\\"LATEST\\\" (default), \\\"EARLIEST\\\", or \\\"RELEVANCE\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter sort\"},{\"displayName\":\"limit\",\"name\":\"limit\",\"type\":\"number\",\"description\":\"Number of results to return. Default 50, max 1000.\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}}}]}}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"Telegram Trigger\"}}"}