策略源码
{"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\"}}"}