This strategy is a fully automated intelligent Dollar-Cost Averaging (DCA) system built on the FMZ Quant workflow engine. It integrates multi-dimensional technical indicator analysis, AI-driven market sentiment evaluation, and a human confirmation mechanism to enable dynamic DCA management of cryptocurrency assets.
Core Logic
The strategy runs automatically on an hourly cycle. Upon each trigger, the system concurrently collects two types of data: approximately 100 daily candlestick bars from the market data feed for computing four core technical indicators — MACD, RSI, ATR, and OBV — and the latest cryptocurrency news from an intelligence API, which is fed into an AI model for in-depth sentiment analysis, producing both short-term and long-term sentiment scores.
Once all data is integrated, a large language model synthesizes the technical indicators and sentiment signals, then selects one of four investment decisions based on a predefined DCA framework: - Major Increase: Extreme market panic or deep pullback with multi-indicator oversold resonance — exceptional risk/reward - Moderate Increase: Market in a corrective phase with a healthy long-term trend - Standard Investment: Market in consolidation or unclear trend — maintain DCA discipline - Pause and Observe: Extreme market euphoria or severe overbought conditions — await a better entry
Human Review and Execution
After the AI renders its decision, the system parses the structured output and generates a confirmation form, pushing it to the user interface for manual approval. Within a designated timeout window, the user selects “Yes” or “No”: - Approved: The system executes a market-order spot buy, logging fill quantity, average price, and actual cost - Rejected: The system records the rejection without executing any trade
In addition, the strategy integrates a Telegram bot command channel, allowing users to send natural language messages that directly invoke the FMZ MCP interface to query account status, holdings, and historical operation records — enabling real-time account monitoring and strategy interaction from anywhere.
Account Tracking and Visualization
The strategy continuously tracks initial and current account states, calculating USDT balance, coin holdings, total portfolio value, and cumulative profit/return since inception. A dashboard table displays the most recent decision history — including timestamp, decision type, planned amount, execution price, rationale, and approval status — giving users a comprehensive view of strategy performance at a glance.
Vedio Link:DCA Transaction Manager Workflow
{"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\"}}"}