DCA交易管家工作流


创建日期: 2025-10-27 11:13:01 最后修改: 2025-10-30 18:35:45
复制: 0 点击次数: 159
avatar of ianzeng123 ianzeng123
2
关注
328
关注者
策略源码
{"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\":[-416,576],\"id\":\"052628cd-bcf8-41bc-bfde-89a55cac8231\",\"name\":\"Tele信息传送\",\"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\":[-816,720],\"id\":\"b5741091-2e3e-4003-9dfe-ba3b5d6d9e64\",\"name\":\"大模型\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=请使用FMZ的MCP进行下面的操作: \\n{{JSON.stringify($json.messageText)}}\\n请进行\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-816,576],\"id\":\"76dfbf30-e883-41a9-97c8-75d9b6238e0f\",\"name\":\"MCP操作执行\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const messageText = $input.first().json.message.text;\\n\\n// json 必须是对象,用 value 或其他键名包装\\nreturn [\\n  {\\n    json: {messageText}\\n  }\\n];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-1040,576],\"id\":\"6bf6d38f-ba2b-4818-af4e-eceead6e7492\",\"name\":\"Tele信息处理\"},{\"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\":[-624,720],\"id\":\"5d843cb7-13f5-45de-9a33-32c24bf29b79\",\"name\":\"MCP 客户端\",\"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\":[-1264,576],\"id\":\"7886d4f3-528f-40de-99a1-b2f2f76390bf\",\"name\":\"Telegram 触发器\",\"credentials\":{\"telegramApi\":{\"id\":\"64b40791-c845-4775-9d67-0efc4122d162\",\"name\":\"Telegram account\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== 初始化检查 ==========\\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(`📊 初始化账户: BTC=${_N(initBTCAmount, 8)}, USDT=${_N(initUSDTBalance, 2)}, 初始价格=$${_N(initPrice, 2)}, 初始总价值=$${_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// ========== 配置参数 ==========\\nconst TRADE_SYMBOL = $vars.contract || 'BTC';\\nconst TRADING_PAIR = `${TRADE_SYMBOL}_USDT`;\\n\\n// ========== 工具函数 ==========\\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(\\\"❌ 决策解析失败:\\\", e.message);\\n        return null;\\n    }\\n}\\n\\nfunction parseDecisionType(decision) {\\n    const text = decision.toLowerCase();\\n    \\n    const decisionMap = {\\n        '大幅加仓': { action: 'buy', description: '大幅加仓', emoji: '🚀' },\\n        '适度加仓': { action: 'buy', description: '适度加仓', emoji: '📈' },\\n        '小幅加仓': { action: 'buy', description: '小幅加仓', emoji: '➕' },\\n        '标准投资': { action: 'buy', description: '标准投资', emoji: '💰' },\\n        '保持观望': { action: 'hold', description: '保持观望', emoji: '⏸️' },\\n        '暂停观望': { action: 'hold', description: '暂停观望', emoji: '⏸️' },\\n        '暂不操作': { action: 'hold', description: '暂不操作', emoji: '⏸️' },\\n        '继续观望': { action: 'hold', description: '继续观望', 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: '保持观望', emoji: '⏸️' };\\n}\\n\\n// ========== 历史记录管理 ==========\\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.交易决策,\\n        planned_amount_usd: decision.交易金额,\\n        actual_amount_usd: 0,\\n        quantity: 0,\\n        price: price,\\n        executed: false,\\n        approved: false,\\n        reason: decision.交易依据\\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}天${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// ========== 表格生成(精简版)==========\\n\\nfunction generateAccountTable(initUSDT, currentUSDTBalance, initBTC, currentBTCAmount, initTotalValue, currentTotalValue, totalProfit, totalProfitPercent) {\\n    const profitEmoji = totalProfit >= 0 ? \\\"🟢\\\" : \\\"🔴\\\";\\n    \\n    return {\\n        type: \\\"table\\\", \\n        title: `💼 账户总览 (${TRADE_SYMBOL})`,\\n        cols: [\\\"类型\\\", \\\"初始\\\", \\\"当前\\\", \\\"盈利金额\\\", \\\"盈利率\\\"],\\n        rows: [\\n            [\\n                \\\"USDT余额\\\",\\n                `$${_N(initUSDT, 2)}`,\\n                `$${_N(currentUSDTBalance, 2)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                `${TRADE_SYMBOL}持仓`,\\n                `${_N(initBTC, 8)}`,\\n                `${_N(currentBTCAmount, 8)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                \\\"账户总值\\\",\\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信息 (${TRADE_SYMBOL})`,\\n        cols: [\\\"时间\\\", \\\"决策\\\", \\\"计划\\\", \\\"价格\\\", \\\"依据\\\", \\\"状态\\\"],\\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 ? \\\"✅同意\\\" : \\\"🚫否决\\\";\\n        } else {\\n            statusStr = record.executed ? \\\"✅执行\\\" : \\\"⏸️跳过\\\";\\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        `运行${stats.runtime}`,\\n        `共${stats.totalDecisions}次`,\\n        `投入$${_N(stats.totalInvested, 0)}`,\\n        `均价$${_N(stats.avgPrice, 0)}`,\\n        `${_N(stats.totalQuantity, 6)} ${TRADE_SYMBOL}`,\\n        `执行${stats.executedCount}次`\\n    ]);\\n    \\n    return table;\\n}\\n\\n// ========== 主逻辑 - 人工否决 ==========\\n\\nfunction main() {\\n    const decision = parseDecision($node[\\\"AI结果分析\\\"].json);\\n    \\n    if (!decision) {\\n        Log(\\\"❌ 未获取到有效决策\\\");\\n        return { json: { success: false, error: \\\"无效决策\\\" } };\\n    }\\n    \\n    Log(\\\"=\\\".repeat(60));\\n    Log(`🚫 ${TRADE_SYMBOL} AI定投决策分析 - 人工否决`);\\n    Log(\\\"=\\\".repeat(60));\\n    Log(`💡 决策: ${decision.交易决策}`);\\n    Log(`💰 金额: $${decision.交易金额}`);\\n    Log(`📝 依据: ${decision.交易依据}...`);\\n    Log(`🚫 人工决策: 否决 (不执行任何交易)`);\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    const actionInfo = parseDecisionType(decision.交易决策);\\n    Log(`🎯 AI建议: ${actionInfo.emoji} ${actionInfo.description}`);\\n    \\n    exchange.SetCurrency(TRADING_PAIR);\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker) {\\n        Log(\\\"❌ 获取价格失败\\\");\\n        return { json: { success: false, error: \\\"获取价格失败\\\" } };\\n    }\\n    \\n    const currentPrice = ticker.Last;\\n    Log(`💵 当前价格: $${currentPrice}`);\\n    \\n    Log(\\\"\\\\n🚫🚫🚫 人工否决,不执行任何交易 🚫🚫🚫\\\\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.交易决策,\\n        plannedAmount: decision.交易金额,\\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(\\\"🚫 人工否决,未执行任何交易\\\");\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    return rejectResult;\\n}\\n\\nconst rejectResult = main();\\n\\nreturn [{\\n  json: {\\n    invoketime: invoketime,\\n    duringtime: duringtime + '分钟',\\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: '✗ 人工否决',\\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\":[1120,-80],\"id\":\"54712e61-5ee8-487b-9df8-c8922398c82d\",\"name\":\"人工拒绝交易\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== 初始化检查 ==========\\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(`📊 初始化账户: BTC=${_N(initBTCAmount, 8)}, USDT=${_N(initUSDTBalance, 2)}, 初始价格=$${_N(initPrice, 2)}, 初始总价值=$${_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// ========== 配置参数 ==========\\nconst TRADE_SYMBOL = $vars.contract || 'BTC';\\nconst TRADING_PAIR = `${TRADE_SYMBOL}_USDT`;\\n\\n// ========== 工具函数 ==========\\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(\\\"❌ 决策解析失败:\\\", e.message);\\n        return null;\\n    }\\n}\\n\\nfunction parseDecisionType(decision) {\\n    const text = decision.toLowerCase();\\n    \\n    const decisionMap = {\\n        '大幅加仓': { action: 'buy', description: '大幅加仓', emoji: '🚀' },\\n        '适度加仓': { action: 'buy', description: '适度加仓', emoji: '📈' },\\n        '小幅加仓': { action: 'buy', description: '小幅加仓', emoji: '➕' },\\n        '标准投资': { action: 'buy', description: '标准投资', emoji: '💰' },\\n        '保持观望': { action: 'hold', description: '保持观望', emoji: '⏸️' },\\n        '暂停观望': { action: 'hold', description: '暂停观望', emoji: '⏸️' },\\n        '暂不操作': { action: 'hold', description: '暂不操作', emoji: '⏸️' },\\n        '继续观望': { action: 'hold', description: '继续观望', 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: '保持观望', emoji: '⏸️' };\\n}\\n\\n// ========== 历史记录管理 ==========\\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.交易决策,\\n        planned_amount_usd: decision.交易金额,\\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.交易依据\\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}天${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// ========== 现货买入函数 ==========\\n\\nfunction executeSpotBuy(symbol, usdAmount, currentPrice) {\\n    try {\\n        exchange.SetCurrency(symbol);\\n        \\n        Log(`💰 现货买入: $${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(`✅ 买入成功: ${_N(actualQty, 6)} ${TRADE_SYMBOL} @ $${_N(avgPrice, 2)} (花费$${_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(`✅ 买入成功 (预估${_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(`❌ 买入失败`);\\n            return { success: false, error: \\\"订单提交失败\\\" };\\n        }\\n    } catch (e) {\\n        Log(`❌ 买入异常: ${e.message}`);\\n        return { success: false, error: e.message };\\n    }\\n}\\n\\n// ========== 表格生成(精简版)==========\\n\\nfunction generateAccountTable(initUSDT, currentUSDTBalance, initBTC, currentBTCAmount, initTotalValue, currentTotalValue, totalProfit, totalProfitPercent) {\\n    const profitEmoji = totalProfit >= 0 ? \\\"🟢\\\" : \\\"🔴\\\";\\n    \\n    return {\\n        type: \\\"table\\\", \\n        title: `💼 账户总览 (${TRADE_SYMBOL})`,\\n        cols: [\\\"类型\\\", \\\"初始\\\", \\\"当前\\\", \\\"盈利金额\\\", \\\"盈利率\\\"],\\n        rows: [\\n            [\\n                \\\"USDT余额\\\",\\n                `$${_N(initUSDT, 2)}`,\\n                `$${_N(currentUSDTBalance, 2)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                `${TRADE_SYMBOL}持仓`,\\n                `${_N(initBTC, 8)}`,\\n                `${_N(currentBTCAmount, 8)}`,\\n                \\\"-\\\",\\n                \\\"-\\\"\\n            ],\\n            [\\n                \\\"账户总值\\\",\\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信息 (${TRADE_SYMBOL})`,\\n        cols: [\\\"时间\\\", \\\"决策\\\", \\\"计划\\\", \\\"价格\\\", \\\"依据\\\", \\\"状态\\\"],\\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 ? \\\"✅同意\\\" : \\\"🚫否决\\\";\\n        } else {\\n            statusStr = record.executed ? \\\"✅执行\\\" : \\\"⏸️跳过\\\";\\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        `运行${stats.runtime}`,\\n        `共${stats.totalDecisions}次`,\\n        `投入$${_N(stats.totalInvested, 0)}`,\\n        `均价$${_N(stats.avgPrice, 0)}`,\\n        `${_N(stats.totalQuantity, 6)} ${TRADE_SYMBOL}`,\\n        `执行${stats.executedCount}次`\\n    ]);\\n    \\n    return table;\\n}\\n\\n// ========== 主逻辑 ==========\\n\\nfunction main() {\\n    const decision = parseDecision($node[\\\"AI结果分析\\\"].json);\\n    \\n    if (!decision) {\\n        Log(\\\"❌ 未获取到有效决策\\\");\\n        return { json: { success: false, error: \\\"无效决策\\\" } };\\n    }\\n    \\n    Log(\\\"=\\\".repeat(60));\\n    Log(`📊 ${TRADE_SYMBOL} AI定投决策分析 - 人工同意`);\\n    Log(\\\"=\\\".repeat(60));\\n    Log(`💡 决策: ${decision.交易决策}`);\\n    Log(`💰 金额: $${decision.交易金额}`);\\n    Log(`📝 依据: ${decision.交易依据}...`);\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    const actionInfo = parseDecisionType(decision.交易决策);\\n    Log(`🎯 ${actionInfo.emoji} ${actionInfo.description}`);\\n    \\n    exchange.SetCurrency(TRADING_PAIR);\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker) {\\n        Log(\\\"❌ 获取价格失败\\\");\\n        return { json: { success: false, error: \\\"获取价格失败\\\" } };\\n    }\\n    \\n    const currentPrice = ticker.Last;\\n    Log(`💵 当前价格: $${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.交易金额 > 0) {\\n        Log(`\\\\n🚀 准备执行现货买入`);\\n        \\n        const result = executeSpotBuy(TRADING_PAIR, decision.交易金额, 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.交易金额;\\n            tradeInfo.avgPrice = result.avgPrice || currentPrice;\\n        }\\n        \\n        saveDecisionRecord(decision, tradeInfo, executeResult.executed);\\n    } else {\\n        Log(\\\"⏸️ 保持观望\\\");\\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 ? \\\"✅ 交易执行完成\\\" : \\\"⏸️ 无需交易操作\\\");\\n    Log(\\\"=\\\".repeat(60));\\n    \\n    return {\\n        success: executeResult.success,\\n        executed: executeResult.executed,\\n        action: actionInfo.action,\\n        decision: decision.交易决策,\\n        plannedAmount: decision.交易金额,\\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 + '分钟',\\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: '✓ 人工同意',\\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\":[1120,-272],\"id\":\"8c784ef0-e9dd-4ed3-95ad-63576a18408b\",\"name\":\"DCA交易执行\"},{\"parameters\":{\"mode\":\"rules\",\"rules\":{\"values\":[{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"strict\",\"version\":2},\"conditions\":[{\"leftValue\":\"={{ $json['交易决策确定'] }}\",\"rightValue\":\"是\",\"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['交易决策确定'] }}\",\"rightValue\":\"否\",\"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\":[896,-176],\"id\":\"b0358160-559e-4cac-a1d2-82ed4c899971\",\"name\":\"决策分支\"},{\"parameters\":{\"resume\":\"form\",\"formTitle\":\"交易决策确定\",\"formDescription\":\"\",\"formFields\":{\"values\":[{\"fieldLabel\":\"交易决策确定\",\"fieldType\":\"dropdown\",\"fieldOptions\":{\"values\":[{\"option\":\"是\"},{\"option\":\"否\"}]},\"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\":[672,-176],\"id\":\"bfcd5afe-c423-48fc-8b29-84e9c19e31f8\",\"name\":\"决策人工确定\"},{\"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\":[448,-176],\"id\":\"d78ade7d-fe99-42a2-857b-f33acafd2858\",\"name\":\"AI结果分析\"},{\"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\":[144,48],\"id\":\"014400dc-8617-4555-9321-c7a1424655ef\",\"name\":\"DCA决策模型\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=你是一位经验丰富的加密货币量化交易资金管理分析师。请基于以下技术指标和市场数据,进行多维度综合分析,并给出本周的定投交易决策。\\n\\n【市场数据概览】\\n- 基础定投金额: {{$vars.dcamoney }} USD\\n- 可调整范围: 0-{{ $vars.dcamoney * 2 }} USD(0表示暂停定投)\\n\\n【技术指标数据】\\n**RSI相对强弱指标:**\\n{{ $json.RSI }}\\n\\n**MACD趋势指标:**\\n{{ $json.MACD_HIST }}\\n\\n**ATR波动率:**\\n{{ $json[\\\"ATR\\\"] }}\\n\\n**OBV资金流向:**\\n{{ $json[\\\"OBV\\\"] }}\\n\\n**市场情绪分析:**\\n{{ JSON.stringify($json['情绪分析']) }}\\n\\n【分析要求】\\n请从以下维度进行综合评估:\\n\\n1. **趋势强度分析**\\n   - 评估MACD多空状态和趋势强度\\n   - 判断是否处于明确的上涨/下跌趋势\\n   - 识别趋势反转的早期信号\\n\\n2. **超买超卖判断**\\n   - 基于RSI判断当前市场是否过热或恐慌\\n   - 考虑逆向投资机会(恐慌时加仓)\\n   - 注意:加密货币市场可能出现持续超买或超卖,需结合其他指标综合判断\\n\\n3. **波动性风险评估**\\n   - 分析ATR波动率水平及其变化趋势\\n   - 高波动时考虑降低单次投入金额,分散风险\\n   - 低波动时可适度增加投入\\n   - 评估市场不确定性程度\\n\\n4. **资金面健康度**\\n   - OBV资金流向是否支持当前价格走势\\n   - 判断量价背离风险\\n   - 分析资金流入流出的持续性和强度\\n\\n5. **市场情绪权衡**\\n   - 短期与长期情绪的分歧或共振\\n   - 极端恐慌时往往是最佳加仓时机\\n   - 极端狂热时应保持冷静,考虑暂停\\n   - 情绪中性时维持标准投资\\n\\n6. **风险收益比评估**\\n   - 综合评估当前建仓的风险收益比\\n   - 考虑定投策略的长期性和安全性\\n   - 平衡积累筹码与风险控制\\n   - 识别不对称机会(下行风险有限,上行空间巨大)\\n\\n【DCA定投决策原则】\\n- ✅ **在恐慌和超卖时贪婪(大幅加仓)** - 市场恐慌是积累筹码的最佳时机\\n- ✅ **在回调和调整时积极(适度加仓)** - 把握阶段性低点\\n- ✅ **在震荡和不明朗时坚持(标准投资)** - 保持定投纪律\\n- ⚠️ **在狂热和超买时冷静(暂停观望)** - 极端高位暂停,等待回调\\n- ⚠️ **高波动时灵活调整** - 根据市场状态动态平衡\\n- ⚠️ **资金持续流出时保持谨慎** - 避免盲目抄底\\n\\n【重要提示】\\n- DCA策略的核心是长期持有和成本平滑,本策略不涉及卖出操作\\n- 止盈和退出应在独立的牛市顶部判断模块中执行\\n- 加密货币市场波动剧烈,需要结合多个指标综合判断,避免单一指标误导\\n- 重视市场结构性变化和宏观环境影响\\n\\n【输出格式要求】\\n请严格按照以下JSON格式输出,不要包含markdown代码块标记:\\n\\n{\\n  \\\"交易决策\\\": \\\"大幅加仓/适度加仓/标准投资/暂停观望\\\",\\n  \\\"交易金额\\\": 150,\\n  \\\"交易依据\\\": \\\"详细说明你的分析逻辑,包括各项指标的综合判断和决策理由\\\"\\n}\\n\\n【决策选项说明】\\n- **大幅加仓({{ $vars.dcamoney * 1.5 }}-{{ $vars.dcamoney * 2 }} USD)**:市场出现极度恐慌或深度回调,多项指标显示超卖且有反转迹象,风险收益比极佳\\n- **适度加仓({{ $vars.dcamoney * 1.2 }}-{{ $vars.dcamoney * 1.49 }} USD)**:市场处于回调调整期,指标显示有一定下行压力但长期趋势健康,适合增加投入\\n- **标准投资({{ $vars.dcamoney }} USD)**:市场处于震荡或趋势不明确状态,保持定投纪律,避免因短期波动影响决策\\n- **暂停观望(0 USD)**:市场出现极度狂热或严重超买,多项指标显示高位风险,或市场结构恶化需等待更好时机\\n\\n【输出要求】\\n- 交易决策:必须是上述四个选项之一\\n- 交易金额:必须是0或{{ Math.round($vars.dcamoney * 0.5) }}到{{ $vars.dcamoney * 2 }}之间的整数\\n- 交易依据:至少200字,清晰说明各指标权重和逻辑推理过程,包括:\\n  * 各技术指标的当前状态和变化趋势分析\\n  * 指标之间的相互验证、共振或背离现象\\n  * 市场所处周期阶段的判断(熊市/震荡/牛市初期/牛市中期/牛市末期等)\\n  * 风险与机会的权衡,包括潜在下行风险和上行空间\\n  * 最终决策的逻辑推演和信心程度\\n  * 特别关注的风险点或机会点\\n\\n现在开始分析并给出决策。\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[48,-176],\"id\":\"22118343-0a14-4f18-a1b6-538dcd1a587c\",\"name\":\"DCA决策判断\",\"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// 计数器,用于按顺序识别\\nlet resultIndex = 0;\\n\\n// Loop over items\\nfor (const item of items) {\\n    \\n    // 情绪指标(有output字段)\\n    if (item.json.output) {\\n        try {\\n            // 清理output中的markdown代码块标记\\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    // 处理有result字段的数据\\n    if (item.json.result && Array.isArray(item.json.result)) {\\n        const result = item.json.result;\\n        \\n        if (resultIndex === 0) {\\n            // 第1个:MACD(三维数组:[DIF, DEA, HIST])\\n            if (Array.isArray(result[0]) && result.length === 3) {\\n                // 三维数组,取HIST(索引2),并过滤null后取最近30个\\n                const histArray = result[2].filter(v => v !== null);\\n                macdHistData = histArray.slice(-30);\\n            } else {\\n                // 单维数组兜底\\n                const filtered = result.filter(v => v !== null);\\n                macdHistData = filtered.slice(-30);\\n            }\\n        } else if (resultIndex === 1) {\\n            // 第2个:RSI,过滤null后取最近30个\\n            const filtered = result.filter(v => v !== null);\\n            rsiData = filtered.slice(-30);\\n        } else if (resultIndex === 2) {\\n            // 第3个:ATR,过滤null后取最近30个\\n            const filtered = result.filter(v => v !== null);\\n            atrData = filtered.slice(-30);\\n        } else if (resultIndex === 3) {\\n            // 第4个:OBV,过滤null后取最近30个\\n            const filtered = result.filter(v => v !== null);\\n            obvData = filtered.slice(-30);\\n        }\\n        \\n        resultIndex++;\\n    }\\n}\\n\\n// 返回结果\\nreturn [{\\n    json: {\\n        \\\"MACD_HIST\\\": macdHistData,\\n        \\\"RSI\\\": rsiData,\\n        \\\"ATR\\\": atrData,\\n        \\\"OBV\\\": obvData,\\n        \\\"情绪分析\\\": sentimentData\\n    }\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-176,-176],\"id\":\"98511250-4a4d-4fe4-a8ac-dde2b60196fb\",\"name\":\"数据整合\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"anthropic/claude-sonnet-4\",\"mode\":\"list\",\"cachedResultName\":\"anthropic/claude-sonnet-4\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[-704,432],\"id\":\"994d2e8a-8316-4255-84f1-9e79d841eea1\",\"name\":\"情绪分析大模型\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=你是一位专门分析**加密货币市场舆情与市场预期**的高智能情感分析器。请针对币种 {{$vars.contract}},使用以下两部分方法对所提供文本进行加密货币情绪分析:\\n\\n**短期情感分析:**\\n\\n* 评估市场的即时反应,包括价格波动、社交媒体热度、新闻事件、链上数据和技术指标信号;\\n* 判断整体市场倾向:\\\"积极\\\"、\\\"中性\\\"或\\\"消极\\\";\\n* 计算一个介于 **-1(极度消极)** 到 **1(极度积极)** 的数值情感分数;\\n* 用简洁的理由说明短期情感,重点关注**最新市场动向、交易者行为、新闻事件或技术走势**。\\n\\n**长期情感分析:**\\n\\n* 评估该加密资产的长期前景,包括基本面、开发进展、市场采用度、宏观经济与监管环境;\\n* 判断整体情感类别:\\\"积极\\\"、\\\"中性\\\"或\\\"消极\\\";\\n* 计算一个介于 **-1(极度消极)** 到 **1(极度积极)** 的长期情感分数;\\n* 提供详细的理由说明长期情感,重点关注**项目潜力、市场结构、政策趋势及机构参与度**。\\n\\n输出结果必须为一个严格格式化的 JSON 对象,包含两个键:\\\"shortTermSentiment\\\" 和 \\\"longTermSentiment\\\"。\\n每个键的值为一个包含以下三个字段的对象:\\\"category\\\"、\\\"score\\\"、\\\"rationale\\\"。\\n不要输出任何额外文本。\\n\\n**输出示例:**\\n\\n```json\\n{\\n  \\\"shortTermSentiment\\\": {\\n    \\\"category\\\": \\\"积极\\\",\\n    \\\"score\\\": 0.7,\\n    \\\"rationale\\\": \\\"...\\\"\\n  },\\n  \\\"longTermSentiment\\\": {\\n    \\\"category\\\": \\\"中性\\\",\\n    \\\"score\\\": 0.0,\\n    \\\"rationale\\\": \\\"...\\\"\\n  }\\n}\\n```\\n\\n**现在,请分析以下加密货币市场相关文本并生成符合上述格式的 JSON 输出:**\\n{{ $json.text }}\\n\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-800,208],\"id\":\"a4c3d3c9-b0d7-4582-867b-34cd42232507\",\"name\":\"AI情绪分析\",\"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\":[-1024,320],\"id\":\"dc9b6442-41b8-4aee-8bfc-27bd1dcc1b6f\",\"name\":\"币种新闻获取\"},{\"parameters\":{\"operation\":\"OBV\",\"inReal\":\"={{ $json.result }}\",\"inPriceV\":\"[]\"},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[-720,-32],\"id\":\"c3b902b8-084c-44e9-b929-8f4d2c547253\",\"name\":\"OBV\"},{\"parameters\":{\"operation\":\"ATR\",\"inReal\":\"={{ $json.result }}\",\"optInTimePeriod\":14},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[-720,-176],\"id\":\"84412f69-8c20-496c-914e-a9b2ba75eb32\",\"name\":\"ATR\"},{\"parameters\":{\"operation\":\"RSI\",\"inReal\":\"={{ $json.result }}\",\"optInTimePeriod\":14},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[-720,-336],\"id\":\"113a4eb7-e595-4403-a0d1-1900e55d98bf\",\"name\":\"RSI\"},{\"parameters\":{\"operation\":\"MACD\",\"inReal\":\"={{ $json.result }}\",\"optInFastPeriod\":12,\"optInSlowPeriod\":26,\"optInSignalPeriod\":9},\"type\":\"n8n-nodes-base.ta\",\"typeVersion\":1,\"position\":[-720,-496],\"id\":\"261318fc-93ea-4fb9-b652-cd83ba7d7f2f\",\"name\":\"MACD\"},{\"parameters\":{\"mode\":\"append\",\"numberInputs\":5},\"type\":\"n8n-nodes-base.merge\",\"typeVersion\":3.2,\"position\":[-400,-224],\"id\":\"2d1dcf6a-2b58-4db7-8292-f33accea0442\",\"name\":\"合并\"},{\"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\":[-1024,-272],\"id\":\"9ae847f2-1422-48cf-bde7-479bd2c65394\",\"name\":\"市场信息\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":5}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-1248,-32],\"id\":\"372407c5-e82c-4f3e-bec8-15a784dda5fc\",\"name\":\"定时触发器\"}],\"pinData\":{},\"connections\":{\"大模型\":{\"ai_languageModel\":[[{\"node\":\"MCP操作执行\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"MCP操作执行\":{\"main\":[[{\"node\":\"Tele信息传送\",\"type\":\"main\",\"index\":0}]]},\"Tele信息处理\":{\"main\":[[{\"node\":\"MCP操作执行\",\"type\":\"main\",\"index\":0}]]},\"MCP 客户端\":{\"ai_tool\":[[{\"node\":\"MCP操作执行\",\"type\":\"ai_tool\",\"index\":0}]]},\"Telegram 触发器\":{\"main\":[[{\"node\":\"Tele信息处理\",\"type\":\"main\",\"index\":0}]]},\"决策分支\":{\"main\":[[{\"node\":\"DCA交易执行\",\"type\":\"main\",\"index\":0}],[{\"node\":\"人工拒绝交易\",\"type\":\"main\",\"index\":0}]]},\"决策人工确定\":{\"main\":[[{\"node\":\"决策分支\",\"type\":\"main\",\"index\":0}]]},\"AI结果分析\":{\"main\":[[{\"node\":\"决策人工确定\",\"type\":\"main\",\"index\":0}]]},\"DCA决策模型\":{\"ai_languageModel\":[[{\"node\":\"DCA决策判断\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"DCA决策判断\":{\"main\":[[{\"node\":\"AI结果分析\",\"type\":\"main\",\"index\":0}]]},\"数据整合\":{\"main\":[[{\"node\":\"DCA决策判断\",\"type\":\"main\",\"index\":0}]]},\"情绪分析大模型\":{\"ai_languageModel\":[[{\"node\":\"AI情绪分析\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"币种新闻获取\":{\"main\":[[{\"node\":\"AI情绪分析\",\"type\":\"main\",\"index\":0}]]},\"MACD\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":0}]]},\"AI情绪分析\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":4}]]},\"OBV\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":3}]]},\"ATR\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":2}]]},\"RSI\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":1}]]},\"合并\":{\"main\":[[{\"node\":\"数据整合\",\"type\":\"main\",\"index\":0}]]},\"市场信息\":{\"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}]]},\"定时触发器\":{\"main\":[[{\"node\":\"币种新闻获取\",\"type\":\"main\",\"index\":0},{\"node\":\"市场信息\",\"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 触发器\"}}"}