加密货币轮动AI增强版策略


创建日期: 2026-01-13 11:50:00 最后修改: 2026-01-21 12:00:04
复制: 9 点击次数: 85
avatar of ianzeng123 ianzeng123
2
关注
364
关注者
策略源码
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"hours\",\"hoursInterval\":4,\"triggerAtMinute\":0}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-512,16],\"id\":\"cfd83d9d-f6a9-40ba-a5a0-2de140ee832f\",\"name\":\"定时触发器\"},{\"parameters\":{\"text\":\"=你是一位专业的交易监督顾问,负责对量化策略的交易决策进行把关。你的主要职责是:\\n- 在关键时刻提供第二层判断\\n- 识别技术信号与市场环境的矛盾\\n- 捕捉重大风险信号\\n- 避免策略在极端情况下的误判\\n\\n**你不是**: 主策略执行者,不需要过度干预每个细节决策\\n**你是**: 最后一道防线,在重要关口提供专业意见\\n\\n---\\n\\n## 重要声明:基于现有信息决策\\n\\n⚠️ **数据完整性说明**:\\n\\n由于市场环境和系统限制,输入数据可能存在以下情况:\\n- ✅ 技术指标可能计算不全 (某些币种数据不足)\\n- ✅ 新闻获取可能缺失 (recentNews为空数组或数量少于5条,或新闻和币种无关)\\n- ✅ 部分字段可能为null或0\\n\\n**你的决策原则**:\\n1. **基于现有信息判断** - 不要因为信息不全就拒绝决策\\n2. **优先参考持仓信息** - positionStatus和positionInfo最重要\\n3. **技术指标优先** - 即使无新闻,技术信号仍有效\\n4. **新闻为辅助** - 新闻缺失时,纯技术面判断\\n5. **保守但不僵化** - 信息不足时偏保守,但不要全部观望\\n\\n**特殊情况处理**:\\n\\n| 情况 | 决策方式 |\\n|------|---------|\\n| 新闻为空 | 仅依据技术指标(score)+持仓状态判断 |\\n| score接近0 | 依据新闻面+持仓状态判断 |\\n| 反向持仓+信息不全 | 偏向保守,建议平仓 |\\n| 同向持仓+信息不全 | 可持有观察 |\\n| 无持仓+信息不全 | 建议观望 |\\n\\n**必须完成的任务**:\\n- 对输入的每个币种都必须给出决策\\n- 不能因为信息不全就跳过币种\\n- 合理利用已有信息做出最佳判断\\n\\n---\\n\\n## 策略背景\\n\\n本策略采用多周期均线综合评分系统,通过三个维度评估币种强弱:\\n\\n1. **均线排列形态** (arrangementScore: -4到+4)\\n   - 正值=多头排列,负值=空头排列\\n   - 绝对值越大排列越完整\\n\\n2. **均线扩散间距** (gapScore)\\n   - 正值=向上扩散,负值=向下扩散\\n   - 绝对值越大趋势越强劲\\n\\n3. **均线时序变化** (timeSeriesScore: -4到+4)\\n   - 正值=多数均线上升,负值=下降\\n   - 绝对值越大方向越一致\\n\\n**综合得分计算**:\\n```\\nscore = 扩散间距 × 排列形态得分 × 时序变化得分\\n```\\n\\n**分组说明**:\\n- **正向组**: score>0,多头趋势,得分越大越强\\n- **负向组**: score<0,空头趋势,绝对值越大越强\\n\\n---\\n\\n## 输入数据结构\\n\\n### 1. 正向组(多头信号)\\n\\n**字段说明**:\\n```json\\n{\\n  \\\"symbol\\\": \\\"BTCUSDT\\\",              // 交易对 (必有)\\n  \\\"processedSymbol\\\": \\\"BTC_USDT.swap\\\", // FMZ格式 (必有)\\n  \\\"score\\\": 0.0234,                  // 综合得分 (必有,可能为0)\\n  \\\"arrangementScore\\\": 4,            // 排列形态 (必有)\\n  \\\"gapScore\\\": 0.0156,               // 扩散间距 (必有)\\n  \\\"timeSeriesScore\\\": 3,             // 时序变化 (必有)\\n  \\\"bullCount\\\": 3,                   // 多头条件数 (必有)\\n  \\\"positionStatus\\\": \\\"无持仓\\\",        // 持仓状态 (必有,见下表)\\n  \\\"positionInfo\\\": {                 // 持仓详情 (可能为null)\\n    \\\"type\\\": 0,                      // 0=多仓, 1=空仓\\n    \\\"amount\\\": 0.5,\\n    \\\"price\\\": 45000.0,\\n    \\\"profit\\\": 1250.5                // 浮盈(正)/浮亏(负)\\n  },\\n  \\\"recentNews\\\": [                   // 最近新闻 (可能为空数组[])\\n    {\\n      \\\"title\\\": \\\"标题\\\",\\n      \\\"url\\\": \\\"链接\\\",\\n      \\\"description\\\": \\\"描述\\\",\\n      \\\"time\\\": \\\"时间\\\"\\n    }\\n  ]\\n}\\n```\\n\\n---\\n\\n### 2. 负向组(空头信号)\\n\\n**字段说明**:\\n```json\\n{\\n  \\\"symbol\\\": \\\"ETHUSDT\\\",\\n  \\\"processedSymbol\\\": \\\"ETH_USDT.swap\\\",\\n  \\\"score\\\": -0.0289,                 // 综合得分 (必有,可能为0)\\n  \\\"arrangementScore\\\": -4,           // 排列形态 (必有)\\n  \\\"gapScore\\\": -0.0198,              // 扩散间距 (必有)\\n  \\\"timeSeriesScore\\\": -3,            // 时序变化 (必有)\\n  \\\"bearCount\\\": 3,                   // 空头条件数 (必有)\\n  \\\"positionStatus\\\": \\\"无持仓\\\",        // 持仓状态 (必有,见下表)\\n  \\\"positionInfo\\\": { ... },          // 同上 (可能为null)\\n  \\\"recentNews\\\": [ ... ],            // 同上 (可能为空数组[])\\n  \\\"newsCount\\\": 5                    // 新闻数量 (可能为0)\\n}\\n```\\n\\n---\\n\\n## 持仓状态(positionStatus)完整说明\\n\\n### 正向组(多头信号)可能的positionStatus:\\n\\n| positionStatus | 含义 | positionInfo.type | 判断要点 |\\n|---------------|------|------------------|---------|\\n| `\\\"无持仓\\\"` | 无仓位,技术多头信号 | null | 评估是否开多 |\\n| `\\\"持有多仓\\\"` | 同向持仓,信号强(在前5) | 0 | 持有或加仓 |\\n| `\\\"持有多仓但指标强度不在前列\\\"` | 同向持仓,信号弱化(不在前5) | 0 | 观察或减仓 |\\n| `\\\"持有空仓\\\"` | ⚠️ **反向持仓**(技术转多但持有空仓) | 1 | **重点关注**:平仓或反手 |\\n\\n### 负向组(空头信号)可能的positionStatus:\\n\\n| positionStatus | 含义 | positionInfo.type | 判断要点 |\\n|---------------|------|------------------|---------|\\n| `\\\"无持仓\\\"` | 无仓位,技术空头信号 | null | 评估是否开空 |\\n| `\\\"持有空仓\\\"` | 同向持仓,信号强(在后5) | 1 | 持有或加仓 |\\n| `\\\"持有空仓但指标强度不在前列\\\"` | 同向持仓,信号弱化(不在后5) | 1 | 观察或减仓 |\\n| `\\\"持有多仓\\\"` | ⚠️ **反向持仓**(技术转空但持有多仓) | 0 | **重点关注**:平仓或反手 |\\n\\n**关键说明**:\\n- 同向持仓 = positionInfo.type与技术信号方向一致\\n- 反向持仓 = positionInfo.type与技术信号方向相反 (⚠️ 这是AI监督的核心关注点!)\\n- \\\"指标强度不在前列\\\" = 该持仓币种的技术信号已弱化,不在前5/后5强信号中\\n\\n---\\n\\n## 核心判断逻辑\\n\\n### 情景1: 无持仓 → 判断是否开仓\\n\\n**关注重点**:\\n- 技术信号强度 (score绝对值大小)\\n- 新闻面是否支持技术信号 (若有新闻)\\n- **关键风险**: 重大利空(监管/黑客/项目方问题)\\n\\n**决策表**:\\n\\n| 技术信号 | 新闻面 | 风险 | 建议决策 |\\n|---------|-------|------|---------|\\n| 强(score绝对值>0.05) | 支持 | 无 | `开多`/`开空` |\\n| 强 | 中性 | 无 | `开多`/`开空` |\\n| 强 | 无新闻 | 无 | `开多`/`开空` (纯技术) |\\n| 强 | 矛盾 | 无 | `观望` |\\n| 强 | 任何 | 重大利空 | `观望` |\\n| 中等(0.02-0.05) | 支持 | 无 | `开多`/`开空` |\\n| 中等 | 中性/无新闻 | 无 | `观望` |\\n| 中等 | 矛盾 | 无 | `观望` |\\n| 弱(<0.02) | 任何 | 无 | `观望` |\\n\\n---\\n\\n### 情景2: 同向持仓 → 判断是否继续持有\\n\\n**同向持仓定义**:\\n- 正向组中 positionStatus = `\\\"持有多仓\\\"` 或 `\\\"持有多仓但指标强度不在前列\\\"`\\n- 负向组中 positionStatus = `\\\"持有空仓\\\"` 或 `\\\"持有空仓但指标强度不在前列\\\"`\\n\\n**关注重点**:\\n- positionStatus中是否包含\\\"指标强度不在前列\\\"(信号弱化标志)\\n- 新闻面是否转向 (若有新闻)\\n- positionInfo.profit盈亏状态\\n\\n**决策表**:\\n\\n| 信号状态 | 新闻面 | profit | 建议决策 |\\n|---------|-------|--------|---------|\\n| 持有仓位(在前5/后5) | 稳定/支持/无新闻 | 任何 | `持有` |\\n| 持有仓位(在前5/后5) | 转向/利空 | 盈利 | `平仓` |\\n| 持有仓位(在前5/后5) | 转向/利空 | 亏损 | `平仓`(止损) |\\n| \\\"但指标强度不在前列\\\" | 稳定/无新闻 | 盈利 | `持有`(观察) |\\n| \\\"但指标强度不在前列\\\" | 稳定/无新闻 | 亏损 | `平仓`(止损) |\\n| \\\"但指标强度不在前列\\\" | 转向/利空 | 任何 | `平仓` |\\n| \\\"但指标强度不在前列\\\" | 新闻缺失 | 盈利 | `持有`(观察) |\\n| \\\"但指标强度不在前列\\\" | 新闻缺失 | 亏损 | `平仓`(止损) |\\n\\n**特别说明**:\\n- \\\"但指标强度不在前列\\\" 表示技术信号已弱化,需谨慎\\n- 新闻缺失时,主要依据技术信号+盈亏状态\\n- 盈利时可观察,亏损时建议止损\\n- 新闻面重大转向时,无论盈亏都建议平仓\\n\\n---\\n\\n### 情景3: 反向持仓 → 判断是否平仓/反手\\n\\n**反向持仓定义**:\\n- 正向组中 positionStatus = `\\\"持有空仓\\\"` (技术多头但持有空仓)\\n- 负向组中 positionStatus = `\\\"持有多仓\\\"` (技术空头但持有多仓)\\n\\n**这是AI监督者的核心价值场景!**\\n\\n**关注重点**:\\n- 反向信号强度 (score绝对值)\\n- 新闻面是否确认趋势反转 (若有新闻)\\n- 信号冲突的严重程度\\n\\n**决策表**:\\n\\n| score绝对值 | 新闻面 | 建议决策 |\\n|-----------|-------|---------|\\n| 强(>0.05) | 确认反转 | `平多开空`/`平空开多` (反手) |\\n| 强(>0.05) | 中性/无新闻 | `平多开空`/`平空开多` (反手) |\\n| 强(>0.05) | 矛盾 | `平仓` (存疑时保守) |\\n| 中等(0.02-0.05) | 确认反转 | `平多开空`/`平空开多` (反手) |\\n| 中等(0.02-0.05) | 中性/无新闻 | `平仓` (信号不够强) |\\n| 中等(0.02-0.05) | 矛盾 | `平仓` |\\n| 弱(<0.02) | 任何 | `持有`(观察,信号太弱) |\\n\\n**反手操作条件** (同时满足):\\n1. score绝对值 > 0.05 (强信号)\\n2. 新闻面明确支持反向,或中性/无新闻 (但如果新闻矛盾则只平仓)\\n3. 无重大风险 (如监管打击、黑客攻击)\\n\\n**注意**: \\n- 强信号+确认反转 = 果断反手\\n- 强信号+新闻矛盾 = 保守平仓 (技术和新闻打架时不冒险)\\n- 中等信号 = 只在新闻确认时才反手,否则平仓\\n- 弱信号 = 继续观察,不急于操作\\n\\n---\\n\\n## 必须警示的情况\\n\\n### ❌ 立即反对开仓/建议平仓:\\n\\n1. **监管打击**: 政府禁令、交易所下架公告\\n2. **安全事件**: 黑客攻击、合约漏洞、项目方跑路\\n3. **重大矛盾**: 技术强多头 + 新闻重大利空 (或相反)\\n4. **系统风险**: BTC暴跌>10%、市场整体恐慌\\n\\n### ✅ 可以放行:\\n\\n1. 技术信号强 + 新闻中性或支持或缺失\\n2. 同向持仓 + 信号延续 + 新闻面稳定或缺失\\n3. 同向持仓 + 信号轻微弱化 + 盈利状态 (可观察)\\n\\n---\\n\\n## 信息不全时的决策优先级\\n\\n**决策依据优先级** (从高到低):\\n\\n1. **positionStatus + profit** (最重要)\\n   - 反向持仓+亏损 → 必须平仓\\n   - 同向持仓+盈利 → 可持有\\n   - 同向持仓+亏损+信号弱化 → 平仓\\n\\n2. **score技术信号** (次重要)\\n   - 强信号(>0.05) → 可开仓/持有\\n   - 弱信号(<0.02) → 观望/平仓\\n\\n3. **新闻面** (辅助参考)\\n   - 有新闻 → 结合判断\\n   - 无新闻 → 纯技术面决策\\n\\n**示例场景**:\\n```javascript\\n// 场景A: 新闻缺失,但技术+持仓信息完整\\n{\\n  \\\"symbol\\\": \\\"BTCUSDT\\\",\\n  \\\"score\\\": 0.0856,              // 强多头信号\\n  \\\"positionStatus\\\": \\\"无持仓\\\",\\n  \\\"recentNews\\\": []              // 无新闻\\n}\\n// 决策: 开多 (技术信号强,无风险,纯技术判断)\\n\\n// 场景B: 反向持仓+新闻缺失\\n{\\n  \\\"symbol\\\": \\\"ETHUSDT\\\",\\n  \\\"score\\\": 0.0623,              // 多头信号\\n  \\\"positionStatus\\\": \\\"持有空仓\\\", // 反向持仓\\n  \\\"positionInfo\\\": { \\\"profit\\\": -200 }, // 亏损\\n  \\\"recentNews\\\": []              // 无新闻\\n}\\n// 决策: 平仓 (反向持仓+亏损,即使无新闻也建议止损)\\n\\n// 场景C: 同向持仓+信号弱化+新闻缺失\\n{\\n  \\\"symbol\\\": \\\"SOLUSDT\\\",\\n  \\\"score\\\": 0.0134,              // 弱多头信号\\n  \\\"positionStatus\\\": \\\"持有多仓但指标强度不在前列\\\",\\n  \\\"positionInfo\\\": { \\\"profit\\\": 500 }, // 盈利\\n  \\\"recentNews\\\": []              // 无新闻\\n}\\n// 决策: 持有 (同向持仓+盈利,信号虽弱但可观察)\\n```\\n\\n---\\n\\n## 输出格式\\n\\n**只输出JSON数组**, 每个币种包含:\\n```json\\n[\\n  {\\n    \\\"symbol\\\": \\\"BTCUSDT\\\",\\n    \\\"currentPosition\\\": \\\"无持仓\\\",\\n    \\\"score\\\": 0.0234,\\n    \\\"newsAnalysis\\\": \\\"新闻缺失,纯技术面判断\\\",\\n    \\\"overallJudgment\\\": \\\"技术多头强劲,无持仓,建议开多\\\",\\n    \\\"decision\\\": \\\"开多\\\"\\n  },\\n  {\\n    \\\"symbol\\\": \\\"ETHUSDT\\\",\\n    \\\"currentPosition\\\": \\\"持有空仓\\\",\\n    \\\"score\\\": 0.0623,\\n    \\\"newsAnalysis\\\": \\\"机构质押增加,资金流入(或:新闻缺失)\\\",\\n    \\\"overallJudgment\\\": \\\"反向持仓亏损,技术已转多,止损\\\",\\n    \\\"decision\\\": \\\"平仓\\\"\\n  }\\n]\\n```\\n\\n### 字段说明:\\n\\n- **symbol**: 币种代码 (如 \\\"BTCUSDT\\\")\\n- **currentPosition**: 直接使用positionStatus的值\\n- **score**: 综合得分 (保留4位小数)\\n- **newsAnalysis**: 新闻面分析 (20字内,若无新闻写\\\"新闻缺失,纯技术判断\\\")\\n- **overallJudgment**: 综合判断理由 (30字内,说明决策依据)\\n- **decision**: 交易决策 (见下表)\\n\\n### decision取值规范:\\n\\n| 当前状态 | 可用决策 | 使用场景 |\\n|---------|---------|---------|\\n| 无持仓 | `观望` | 信号弱或风险大或信息不全,暂不开仓 |\\n| 无持仓 | `开多` | 技术多头+新闻支持(或无新闻但信号强),开多仓 |\\n| 无持仓 | `开空` | 技术空头+新闻支持(或无新闻但信号强),开空仓 |\\n| 有持仓 | `持有` | 信号延续,继续持有现有仓位 |\\n| 有持仓 | `平仓` | 平掉当前仓位(同向弱化或反向强) |\\n| 持有多仓 | `平多开空` | 反手:平多仓后开空仓(需满足反手条件) |\\n| 持有空仓 | `平空开多` | 反手:平空仓后开多仓(需满足反手条件) |\\n\\n**注意**:\\n1. `观望` 只用于无持仓时\\n2. `持有` 只用于有持仓时\\n3. 反手操作需满足强信号+盈利条件\\n4. 必须包含所有输入的币种(正向组+负向组)\\n5. 新闻缺失时在newsAnalysis中明确说明\\n\\n---\\n\\n## 核心原则\\n\\n1. **基于现有信息决策** - 不因信息不全拒绝判断\\n2. **持仓信息优先** - positionStatus和profit最重要\\n3. **相信技术信号** - 即使无新闻,技术信号仍有效\\n4. **关注重大风险** - 识别技术面无法捕捉的风险\\n5. **识别反向持仓** - 这是监督者的核心价值\\n6. **保守但不僵化** - 信息不足时偏保守但不全部观望\\n7. **不过度干预** - 只在关键时刻提供判断\\n\\n---\\n\\n## 输入数据\\n\\n**正向组(多头信号)**:\\n{{JSON.stringify($node['实时新闻获取'].json.positive)}}\\n\\n**负向组(空头信号)**:\\n{{JSON.stringify($node['实时新闻获取'].json.negative)}}\\n\\n---\\n\\n**请直接输出JSON数组,不要有任何其他内容(无需```json标记)**\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[1056,160],\"id\":\"4e751178-96c9-48d3-b448-66e311d2a04c\",\"name\":\"AI 智能体\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"deepseek/deepseek-v3.2\",\"mode\":\"list\",\"cachedResultName\":\"deepseek/deepseek-v3.2\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[1056,336],\"id\":\"a66f062f-5a07-4214-8217-932c81e70257\",\"name\":\"OpenAI 模型\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== 移动止盈止损监控系统 + 可视化仪表板 ==========\\n\\nif (_G('initmoney') === null) {\\n  return {}\\n}\\n\\n// ========== 配置参数 ==========\\nconst CONFIG = {\\n    TRAILING_STOP_PERCENT: $vars.lossPercent,  // 移动止损回撤百分比\\n    CHECK_INTERVAL: 500  // 检查间隔(毫秒)\\n};\\n\\n// ========== 工具函数 ==========\\n\\n/**\\n * 执行平仓操作\\n */\\nfunction closePosition(coin, pos, isLong, reason, currentPnl, maxPnl) {\\n    try {\\n        const closeAmount = pos.Amount;\\n        \\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n        \\n        const orderId = isLong ? exchange.Sell(-1, closeAmount) : exchange.Buy(-1, closeAmount);\\n        \\n        if (orderId) {\\n            Log(`✅ ${coin} ${reason} - 当前盈利: ${(currentPnl*100).toFixed(2)}% | 最高盈利: ${(maxPnl*100).toFixed(2)}% | 数量=${closeAmount}`);\\n            \\n            // 清除最高盈利记录\\n            _G(`${coin}_USDT.swap_maxprofit`, null);\\n            \\n            return true;\\n        } else {\\n            Log(`❌ ${coin} 平仓失败`);\\n            return false;\\n        }\\n    } catch (e) {\\n        Log(`❌ ${coin} 平仓异常: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n/**\\n * 移动止盈止损监控\\n */\\nfunction monitorPositionWithTrailingStop(coin) {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        \\n        const positions = exchange.GetPositions();\\n        const pos = positions && positions.find(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n        \\n        if (!pos) {\\n            return { status: \\\"no_position\\\" };\\n        }\\n        \\n        const ticker = exchange.GetTicker();\\n        if (!ticker) {\\n            return { status: \\\"no_ticker\\\" };\\n        }\\n        \\n        const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n        const currentPrice = ticker.Last;\\n        const entryPrice = pos.Price;\\n        \\n        // 计算当前盈亏百分比\\n        const currentPnl = (currentPrice - entryPrice) * (isLong ? 1 : -1) / entryPrice;\\n        \\n        // 获取历史最高盈利\\n        const symbolKey = `${coin}_USDT.swap_maxprofit`;\\n        let maxProfit = _G(symbolKey);\\n        \\n        // 如果是首次或者当前盈利更高,更新最高盈利\\n        if (maxProfit === null || currentPnl - maxProfit > 0.001) {\\n            maxProfit = currentPnl;\\n            _G(symbolKey, maxProfit);\\n            Log(`📈 ${coin} 更新最高盈利: ${(maxProfit * 100).toFixed(2)}%`);\\n        }\\n        \\n        // 计算回撤\\n        const drawdown = maxProfit - currentPnl;\\n        \\n        // 判断是否触发移动止损\\n        let shouldClose = false;\\n        \\n        if (drawdown >= CONFIG.TRAILING_STOP_PERCENT) {\\n            shouldClose = true;\\n            Log(`🔔 ${coin} 触发移动止损! 最高盈利: ${(maxProfit*100).toFixed(2)}% | 当前盈利: ${(currentPnl*100).toFixed(2)}% | 回撤: ${(drawdown*100).toFixed(2)}%`);\\n        }\\n        \\n        if (shouldClose) {\\n            const closeSuccess = closePosition(coin, pos, isLong, \\\"移动止损\\\", currentPnl, maxProfit);\\n            return closeSuccess ? \\n                { status: \\\"closed\\\", reason: \\\"trailing_stop\\\", currentPnl: currentPnl, maxPnl: maxProfit } : \\n                { status: \\\"close_failed\\\" };\\n        }\\n        \\n        return {\\n            status: \\\"monitoring\\\",\\n            currentPnl: currentPnl,\\n            maxPnl: maxProfit,\\n            drawdown: drawdown,\\n            position: pos,\\n            currentPrice: currentPrice,\\n            isLong: isLong\\n        };\\n        \\n    } catch (e) {\\n        Log(`❌ ${coin} 监控异常: ${e.message}`);\\n        return { status: \\\"error\\\", error: e.message };\\n    }\\n}\\n\\n// ========== 可视化图表函数 ==========\\n\\n/**\\n * 创建账户概览表\\n */\\nfunction createAccountOverviewTable() {\\n    const accountTable = {\\n        type: \\\"table\\\",\\n        title: \\\"💰 账户概览\\\",\\n        cols: [\\\"💵 初始权益\\\", \\\"💰 实时权益\\\", \\\"💸 收益(USD)\\\", \\\"📈 盈利率\\\", \\\"🛡️ 止损模式\\\"],\\n        rows: []\\n    };\\n    \\n    try {\\n        const currentAccount = exchange.GetAccount();\\n        const currentEquity = currentAccount.Equity;\\n        const initMoney = _G('initmoney') || currentEquity;\\n        const totalProfit = currentEquity - initMoney;\\n        const profitPercent = ((currentEquity - initMoney) / initMoney * 100);\\n        \\n        // LogProfit\\n        LogProfit(totalProfit, \\\"&\\\");\\n        \\n        // 状态行\\n        const statusRow = [];\\n        \\n        // 初始权益 - 状态\\n        statusRow.push(\\\"📊\\\");\\n        \\n        // 实时权益 - 状态\\n        statusRow.push(currentEquity >= initMoney ? \\\"✅\\\" : \\\"⚠️\\\");\\n        \\n        // 收益状态\\n        if (totalProfit > 0) {\\n            statusRow.push(\\\"✅ 盈利\\\");\\n        } else if (totalProfit < 0) {\\n            statusRow.push(\\\"⚠️ 亏损\\\");\\n        } else {\\n            statusRow.push(\\\"⚪ 持平\\\");\\n        }\\n        \\n        // 盈利率状态\\n        if (profitPercent > 5) {\\n            statusRow.push(\\\"🔥 优秀\\\");\\n        } else if (profitPercent > 0) {\\n            statusRow.push(\\\"✅ 良好\\\");\\n        } else {\\n            statusRow.push(\\\"⚠️ 需改进\\\");\\n        }\\n        \\n        // 止损模式状态\\n        statusRow.push(\\\"✅ 运行中\\\");\\n        \\n        // 数值行\\n        const valueRow = [\\n            `$${_N(initMoney, 2)}`,\\n            `$${_N(currentEquity, 2)}`,\\n            totalProfit > 0 ? `🟢 +$${_N(totalProfit, 2)}` : \\n            totalProfit < 0 ? `🔴 $${_N(totalProfit, 2)}` : `⚪ $0`,\\n            profitPercent > 0 ? `🟢 +${_N(profitPercent, 2)}%` : \\n            profitPercent < 0 ? `🔴 ${_N(profitPercent, 2)}%` : `⚪ 0%`,\\n            `移动止损 ${(CONFIG.TRAILING_STOP_PERCENT * 100).toFixed(1)}%`\\n        ];\\n      \\n        accountTable.rows.push(valueRow);\\n        accountTable.rows.push(statusRow);\\n        \\n        \\n    } catch (e) {\\n        Log(`❌ 创建账户概览表失败: ${e.message}`);\\n        accountTable.rows.push([\\n            \\\"❌ 错误\\\",\\n            \\\"获取账户失败\\\",\\n            \\\"🚨\\\",\\n            \\\"🚨\\\",\\n            \\\"🚨\\\"\\n        ]);\\n    }\\n    \\n    return accountTable;\\n}\\n\\n/**\\n * 创建AI决策表 - 按正负向分组\\n */\\nfunction createAIDecisionTable() {\\n    const aiTable = {\\n        type: \\\"table\\\",\\n        title: \\\"🤖 AI决策信号\\\",\\n        cols: [\\\"分组\\\", \\\"币种\\\", \\\"决策\\\", \\\"执行状态\\\", \\\"评分\\\", \\\"新闻分析\\\", \\\"综合判断\\\"],\\n        rows: []\\n    };\\n    \\n    try {\\n        // 从_G获取最新执行结果\\n        const executionData = _G('latestExecutionResults') || { results: {} };\\n        const executionResults = executionData.results || {};\\n        \\n        if (Object.keys(executionResults).length === 0) {\\n            aiTable.rows.push([\\n                \\\"⏳\\\",\\n                \\\"📭 暂无信号\\\",\\n                \\\"⏳ 等待AI\\\",\\n                \\\"⏭️ 待分析\\\",\\n                \\\"-\\\",\\n                \\\"等待AI生成交易决策\\\",\\n                \\\"-\\\"\\n            ]);\\n        } else {\\n            // 分组:正向组(score>0)和负向组(score<0)\\n            const positiveResults = [];\\n            const negativeResults = [];\\n            \\n            Object.entries(executionResults).forEach(([coin, result]) => {\\n                if (result.score > 0) {\\n                    positiveResults.push([coin, result]);\\n                } else if (result.score < 0) {\\n                    negativeResults.push([coin, result]);\\n                } else {\\n                    // score=0的归入其他\\n                    positiveResults.push([coin, result]);\\n                }\\n            });\\n            \\n            // 格式化行的函数\\n            const formatRow = (coin, result, groupIcon) => {\\n                // 决策显示\\n                let decisionDisplay = result.decision || result.signal || \\\"未知\\\";\\n                let decisionEmoji = \\\"⏸️\\\";\\n                \\n                switch (result.decision || result.originalDecision) {\\n                    case '开多':\\n                        decisionDisplay = \\\"🟢 开多\\\";\\n                        decisionEmoji = \\\"📈\\\";\\n                        break;\\n                    case '开空':\\n                        decisionDisplay = \\\"🔴 开空\\\";\\n                        decisionEmoji = \\\"📉\\\";\\n                        break;\\n                    case '平仓':\\n                        decisionDisplay = \\\"⏹️ 平仓\\\";\\n                        decisionEmoji = \\\"🔚\\\";\\n                        break;\\n                    case '持有':\\n                    case '观望':\\n                        decisionDisplay = \\\"⏸️ 持有\\\";\\n                        decisionEmoji = \\\"⏳\\\";\\n                        break;\\n                    case '平多开空':\\n                        decisionDisplay = \\\"🔄 平多开空\\\";\\n                        decisionEmoji = \\\"🔁\\\";\\n                        break;\\n                    case '平空开多':\\n                        decisionDisplay = \\\"🔄 平空开多\\\";\\n                        decisionEmoji = \\\"🔁\\\";\\n                        break;\\n                }\\n                \\n                // 执行状态\\n                let statusDisplay = \\\"\\\";\\n                if (result.executed) {\\n                    statusDisplay = `✅ 已执行 (${result.operations || 1}次)`;\\n                } else if (result.skipReason) {\\n                    statusDisplay = `⏭️ ${result.skipReason}`;\\n                } else {\\n                    statusDisplay = \\\"❌ 失败\\\";\\n                }\\n                \\n                // 评分显示\\n                const score = result.score || 0;\\n                let scoreDisplay = \\\"-\\\";\\n                if (score !== 0) {\\n                    const scoreAbs = Math.abs(score);\\n                    if (scoreAbs > 0.1) {\\n                        scoreDisplay = `🔥 ${score.toFixed(4)}`;\\n                    } else if (scoreAbs > 0.05) {\\n                        scoreDisplay = `⚡ ${score.toFixed(4)}`;\\n                    } else {\\n                        scoreDisplay = `${score.toFixed(4)}`;\\n                    }\\n                }\\n                \\n                // 新闻分析(截取前25字符)\\n                const newsAnalysis = result.newsAnalysis || \\\"-\\\";\\n                \\n                // 综合判断(截取前25字符)\\n                const overallJudgment = result.overallJudgment || \\\"-\\\";\\n                \\n                return [\\n                    groupIcon,\\n                    `💎 ${coin}`,\\n                    `${decisionEmoji} ${decisionDisplay}`,\\n                    statusDisplay,\\n                    scoreDisplay,\\n                    newsAnalysis,\\n                    overallJudgment\\n                ];\\n            };\\n            \\n            // 添加正向组\\n            if (positiveResults.length > 0) {\\n                positiveResults\\n                    .sort((a, b) => (b[1].score || 0) - (a[1].score || 0))\\n                    .forEach(([coin, result]) => {\\n                        aiTable.rows.push(formatRow(coin, result, \\\"📈\\\"));\\n                    });\\n            }\\n            \\n            // 添加负向组\\n            if (negativeResults.length > 0) {\\n                negativeResults\\n                    .sort((a, b) => (a[1].score || 0) - (b[1].score || 0))\\n                    .forEach(([coin, result]) => {\\n                        aiTable.rows.push(formatRow(coin, result, \\\"📉\\\"));\\n                    });\\n            }\\n            \\n            // 添加汇总行\\n            if (executionData.timestamp) {\\n                const executionTime = new Date(executionData.timestamp).toLocaleTimeString();\\n                const executedCount = Object.values(executionResults).filter(r => r.executed).length;\\n                const totalSignals = Object.keys(executionResults).length;\\n                \\n                aiTable.rows.push([\\n                    \\\"📊\\\",\\n                    `汇总: ${totalSignals}信号`,\\n                    `📈${positiveResults.length} 📉${negativeResults.length}`,\\n                    `✅ ${executedCount}已执行`,\\n                    \\\"-\\\",\\n                    \\\"-\\\",\\n                    `更新: ${executionTime}`\\n                ]);\\n            }\\n        }\\n        \\n    } catch (e) {\\n        Log(`❌ 创建AI决策表失败: ${e.message}`);\\n        aiTable.rows.push([\\n            \\\"❌\\\",\\n            \\\"错误\\\",\\n            \\\"获取AI信号失败\\\",\\n            \\\"🚨\\\",\\n            \\\"-\\\",\\n            e.message.substring(0, 20),\\n            \\\"-\\\"\\n        ]);\\n    }\\n    \\n    return aiTable;\\n}\\n\\n/**\\n * 创建实时持仓表 - 按正负向分组\\n */\\nfunction createRealtimePositionTable() {\\n    const positionTable = {\\n        type: \\\"table\\\",\\n        title: \\\"💼 实时持仓监控\\\",\\n        cols: [\\\"分组\\\", \\\"币种\\\", \\\"方向\\\", \\\"数量\\\", \\\"入场价\\\", \\\"当前价\\\", \\\"当前盈亏%\\\", \\\"最高盈亏%\\\", \\\"回撤%\\\", \\\"状态\\\"],\\n        rows: []\\n    };\\n    \\n    try {\\n        const positions = exchange.GetPositions();\\n        let totalCurrentPnl = 0;\\n        let positionCount = 0;\\n        \\n        // 分组存储\\n        const longPositions = [];\\n        const shortPositions = [];\\n        \\n        if (positions && positions.length > 0) {\\n            positions.forEach(pos => {\\n                if (Math.abs(pos.Amount) > 0) {\\n                    const coinMatch = pos.Symbol.match(/^(.+)_USDT/);\\n                    if (!coinMatch) return;\\n                    \\n                    const coin = coinMatch[1];\\n                    const symbolKey = `${coin}_USDT.swap_maxprofit`;\\n                    \\n                    // 获取当前价格\\n                    exchange.SetCurrency(coin + '_USDT');\\n                    exchange.SetContractType(\\\"swap\\\");\\n                    const ticker = exchange.GetTicker();\\n                    \\n                    if (ticker) {\\n                        const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n                        const currentPrice = ticker.Last;\\n                        const entryPrice = pos.Price;\\n                        \\n                        // 当前盈亏\\n                        const currentPnl = ((currentPrice - entryPrice) * (isLong ? 1 : -1) / entryPrice * 100);\\n                        \\n                        // 最高盈亏\\n                        const maxProfit = _G(symbolKey);\\n                        const maxPnlPercent = maxProfit !== null ? (maxProfit * 100) : currentPnl;\\n                        \\n                        // 回撤\\n                        const drawdown = maxProfit !== null ? ((maxProfit * 100) - currentPnl) : 0;\\n                        \\n                        // 当前盈亏显示\\n                        let currentPnlDisplay = \\\"\\\";\\n                        if (currentPnl > 0) {\\n                            currentPnlDisplay = `🟢 +${currentPnl.toFixed(2)}%`;\\n                        } else if (currentPnl < 0) {\\n                            currentPnlDisplay = `🔴 ${currentPnl.toFixed(2)}%`;\\n                        } else {\\n                            currentPnlDisplay = `⚪ ${currentPnl.toFixed(2)}%`;\\n                        }\\n                        \\n                        // 最高盈亏显示\\n                        let maxPnlDisplay = \\\"\\\";\\n                        if (maxPnlPercent > 0) {\\n                            maxPnlDisplay = `🔥 +${maxPnlPercent.toFixed(2)}%`;\\n                        } else if (maxPnlPercent < 0) {\\n                            maxPnlDisplay = `${maxPnlPercent.toFixed(2)}%`;\\n                        } else {\\n                            maxPnlDisplay = `${maxPnlPercent.toFixed(2)}%`;\\n                        }\\n                        \\n                        // 回撤显示\\n                        let drawdownDisplay = \\\"\\\";\\n                        if (drawdown > 0) {\\n                            const warningLevel = (CONFIG.TRAILING_STOP_PERCENT * 100);\\n                            if (drawdown >= warningLevel * 0.8) {\\n                                drawdownDisplay = `🔴 -${drawdown.toFixed(2)}%`;\\n                            } else if (drawdown >= warningLevel * 0.5) {\\n                                drawdownDisplay = `🟡 -${drawdown.toFixed(2)}%`;\\n                            } else {\\n                                drawdownDisplay = `🟢 -${drawdown.toFixed(2)}%`;\\n                            }\\n                        } else {\\n                            drawdownDisplay = `⚪ 0%`;\\n                        }\\n                        \\n                        // 状态\\n                        let statusDisplay = \\\"✅ 监控中\\\";\\n                        if (maxProfit !== null) {\\n                            const stopLevel = (CONFIG.TRAILING_STOP_PERCENT * 100);\\n                            if (drawdown >= stopLevel * 0.9) {\\n                                statusDisplay = \\\"⚠️ 即将止损\\\";\\n                            } else if (drawdown >= stopLevel * 0.7) {\\n                                statusDisplay = \\\"🟡 警戒中\\\";\\n                            } else {\\n                                statusDisplay = \\\"✅ 持续监控\\\";\\n                            }\\n                        }\\n                        \\n                        const positionRow = [\\n                            isLong ? \\\"📈\\\" : \\\"📉\\\",\\n                            `💎 ${coin}`,\\n                            isLong ? \\\"📈 多\\\" : \\\"📉 空\\\",\\n                            _N(Math.abs(pos.Amount), 4),\\n                            `$${_N(entryPrice, 4)}`,\\n                            `$${_N(currentPrice, 4)}`,\\n                            currentPnlDisplay,\\n                            maxPnlDisplay,\\n                            drawdownDisplay,\\n                            statusDisplay\\n                        ];\\n                        \\n                        if (isLong) {\\n                            longPositions.push(positionRow);\\n                        } else {\\n                            shortPositions.push(positionRow);\\n                        }\\n                        \\n                        totalCurrentPnl += currentPnl;\\n                        positionCount++;\\n                    }\\n                    \\n                    Sleep(200);\\n                }\\n            });\\n        }\\n        \\n        // 先添加多仓\\n        longPositions.forEach(row => positionTable.rows.push(row));\\n        \\n        // 再添加空仓\\n        shortPositions.forEach(row => positionTable.rows.push(row));\\n        \\n        // 添加汇总行\\n        if (positionCount > 0) {\\n            const avgPnl = totalCurrentPnl / positionCount;\\n            positionTable.rows.push([\\n                `📊`,\\n                `汇总: ${positionCount}仓`,\\n                `📈${longPositions.length} 📉${shortPositions.length}`,\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                avgPnl > 0 ? `🟢 +${_N(avgPnl, 2)}%` : `🔴 ${_N(avgPnl, 2)}%`,\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                `移动止损: ${(CONFIG.TRAILING_STOP_PERCENT * 100).toFixed(1)}%`\\n            ]);\\n        } else {\\n            positionTable.rows.push([\\n                \\\"📭\\\",\\n                \\\"空仓\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"-\\\",\\n                \\\"等待信号\\\"\\n            ]);\\n        }\\n        \\n    } catch (e) {\\n        Log(`❌ 创建持仓表失败: ${e.message}`);\\n        positionTable.rows.push([\\n            \\\"❌\\\",\\n            \\\"错误\\\",\\n            \\\"获取持仓失败\\\",\\n            \\\"-\\\",\\n            \\\"-\\\",\\n            \\\"-\\\",\\n            \\\"-\\\",\\n            \\\"-\\\",\\n            \\\"-\\\",\\n            \\\"🚨\\\"\\n        ]);\\n    }\\n    \\n    return positionTable;\\n}\\n\\n/**\\n * 显示完整监控仪表板\\n */\\nfunction displayMonitoringDashboard() {\\n    try {\\n        // 1. 账户概览\\n        const accountTable = createAccountOverviewTable();\\n        \\n        // 2. AI决策\\n        const aiTable = createAIDecisionTable();\\n        \\n        // 3. 实时持仓\\n        const positionTable = createRealtimePositionTable();\\n        \\n        // 组合显示\\n        const dashboardDisplay = \\n            '`' + JSON.stringify(accountTable) + '`\\\\n\\\\n' +\\n            '`' + JSON.stringify(aiTable) + '`\\\\n\\\\n' +\\n            '`' + JSON.stringify(positionTable) + '`';\\n        \\n        LogStatus(dashboardDisplay);\\n        \\n    } catch (e) {\\n        Log(`❌ 显示监控仪表板失败: ${e.message}`);\\n        LogStatus('❌ 仪表板显示失败: ' + e.message);\\n    }\\n}\\n\\n// ========== 主执行逻辑 ==========\\n\\nfunction main() {\\n    try {\\n\\n        \\n        const monitoringResults = {};\\n        let totalActions = 0;\\n        \\n        // 获取所有持仓进行监控\\n        const positions = exchange.GetPositions();\\n        \\n        if (positions && positions.length > 0) {\\n            \\n            positions.forEach(pos => {\\n                if (Math.abs(pos.Amount) > 0) {\\n                    const coinMatch = pos.Symbol.match(/^(.+)_USDT/);\\n                    if (!coinMatch) return;\\n                    \\n                    const coin = coinMatch[1];\\n                    \\n                    const result = monitorPositionWithTrailingStop(coin);\\n                    monitoringResults[coin] = result;\\n                    \\n                    if (result.status === \\\"closed\\\") {\\n                        Log(`🎯 ${coin} 已平仓 - 移动止损触发`);\\n                        totalActions++;\\n                    }\\n                    \\n                    Sleep(CONFIG.CHECK_INTERVAL);\\n                }\\n            });\\n        } \\n        \\n        // 显示监控仪表板\\n        displayMonitoringDashboard();\\n        \\n        \\n        return {\\n            monitoringResults: monitoringResults,\\n            totalActions: totalActions,\\n            timestamp: Date.now()\\n        };\\n        \\n    } catch (e) {\\n        Log(`❌ 主执行流程失败: ${e.message}`);\\n        Log(`❌ 错误堆栈: ${e.stack}`);\\n        LogStatus('❌ 系统错误: ' + e.message);\\n        return { error: e.message };\\n    }\\n}\\n\\n// 执行主函数并返回结果\\nreturn main();\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-304,304],\"id\":\"9b7ab016-fd6c-4905-af6e-301f11bdbaee\",\"name\":\"止盈止损监控\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"if (_G('initmoney') === null) {\\n  const initAccount = _C(exchange.GetAccount);\\n  _G('initmoney', initAccount.Balance);\\n\\n  const market = _C(exchange.GetMarkets)\\n  _G('markets', market)\\n}\\n\\nreturn {}\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-288,16],\"id\":\"88a9366c-3224-47e9-85b3-1b41c3f92196\",\"name\":\"初始设置\"},{\"parameters\":{\"method\":\"GET\",\"url\":\"https://api.binance.com/api/v3/ticker/24hr\",\"authentication\":\"none\",\"sendQuery\":false,\"sendHeaders\":false,\"sendBody\":false,\"options\":{},\"infoMessage\":\"\"},\"type\":\"n8n-nodes-base.httpRequest\",\"typeVersion\":4.2,\"position\":[-64,16],\"id\":\"ac617285-231c-4538-bff5-92a365226ddf\",\"name\":\"币种查询\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const symbols = $json.data\\n    .filter(item => {\\n        if (!item.symbol.endsWith('USDT')) return false;\\n        if (item.symbol.startsWith('USDC')) return false;\\n        \\n        const symbol = item.symbol.replace('USDT', '_USDT.swap');\\n        const markets = _G('markets');\\n        \\n        if (markets && !markets[symbol]) return false;\\n        \\n        return true;\\n    })\\n    .sort((a, b) => parseFloat(b.quoteVolume) - parseFloat(a.quoteVolume))\\n    .slice(0, $vars['coinNumber'])\\n    .map(item => item.symbol);\\n\\nLog(`✅ 筛选出 ${symbols.length} 个币种: ${symbols.join(', ')}`);\\n\\nreturn { symbol: symbols };\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[160,16],\"id\":\"d0212102-292f-4999-93e0-192376fe856b\",\"name\":\"高流动币种查询\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ==================== 多币种均线综合评分与排名 ====================\\n\\n// 1. 获取参数\\nvar wheelPeriod = $vars['wheelPeriod'];\\nvar meanPeriod = $vars['meanPeriod'];\\n\\n// 2. 获取输入的币种数组\\nvar inputSymbols = $input.first().json.symbol;  // [\\\"BTCUSDT\\\", \\\"ETHUSDT\\\", ...]\\n\\nLog(`📥 收到 ${inputSymbols.length} 个高流动币种`);\\n\\n// ==================== 先获取持仓信息 ====================\\nLog(\\\"📦 开始获取持仓信息...\\\");\\n\\nconst positions = exchange.GetPositions();\\nconst positionMap = {};\\nconst positionCoins = new Set();\\nconst positionSymbols = [];\\n\\nif (positions && positions.length > 0) {\\n    positions.forEach(pos => {\\n        if (Math.abs(pos.Amount) > 0) {\\n            const coin = pos.Symbol.replace('_USDT.swap', '');\\n\\n            if (coin && coin !== pos.Symbol) {\\n                positionCoins.add(coin);\\n                const symbol = coin + 'USDT';\\n                positionSymbols.push(symbol);\\n                \\n                positionMap[coin] = {\\n                    symbol: symbol,\\n                    type: pos.Type,  // 0=多仓, 1=空仓\\n                    amount: pos.Amount,\\n                    price: pos.Price,\\n                    profit: pos.Profit,\\n                    positionStatus: pos.Type === 0 || pos.Type === PD_LONG ? '持有多仓' : '持有空仓'\\n                };\\n                Log(`📍 持仓: ${coin} ${positionMap[coin].positionStatus} 盈亏=${pos.Profit}`);\\n            }\\n        }\\n    });\\n}\\n\\nLog(`✅ 共发现 ${positionCoins.size} 个持仓币种`);\\n\\n// ==================== 检查持仓币种是否在输入列表中,不在则添加 ====================\\nconst inputSymbolSet = new Set(inputSymbols);\\nconst missingPositionSymbols = positionSymbols.filter(symbol => !inputSymbolSet.has(symbol));\\n\\nif (missingPositionSymbols.length > 0) {\\n    Log(`➕ 补充 ${missingPositionSymbols.length} 个持仓币种到计算列表: ${missingPositionSymbols.join(', ')}`);\\n    inputSymbols = inputSymbols.concat(missingPositionSymbols);\\n}\\n\\n// 存储所有币种的评分结果\\nvar allResults = [];\\n\\n// 3. 遍历每个币种进行计算\\nfor (var i = 0; i < inputSymbols.length; i++) {\\n    \\n    var originalSymbol = inputSymbols[i];\\n    Log('处理币种:', originalSymbol);\\n    \\n    var processedSymbol = originalSymbol.replace('USDT', '_USDT.swap');\\n    \\n    try {\\n        // 获取不同周期的K线数据\\n        var s = exchange.GetRecords(processedSymbol, wheelPeriod / 4);\\n        var ms = exchange.GetRecords(processedSymbol, wheelPeriod / 2);\\n        var ml = exchange.GetRecords(processedSymbol, wheelPeriod * 2);\\n        var ls = exchange.GetRecords(processedSymbol, wheelPeriod * 4);\\n        \\n        if (!s || !ms || !ml || !ls || ls.length < meanPeriod + 2) {\\n            Log(`⚠️ ${originalSymbol} 数据不足,跳过`);\\n            continue;\\n        }\\n        \\n        // 计算各周期均线\\n        var sMA = TA.EMA(s, meanPeriod);\\n        var msMA = TA.EMA(ms, meanPeriod);\\n        var mlMA = TA.EMA(ml, meanPeriod);\\n        var lMA = TA.EMA(ls, meanPeriod);\\n        \\n        if (!sMA || !msMA || !mlMA || !lMA || sMA.length < 2) {\\n            Log(`⚠️ ${originalSymbol} 均线计算失败,跳过`);\\n            continue;\\n        }\\n        \\n        var len_s = sMA.length;\\n        var len_ms = msMA.length;\\n        var len_ml = mlMA.length;\\n        var len_l = lMA.length;\\n        \\n        var PS = sMA[len_s - 1];\\n        var PMS = msMA[len_ms - 1];\\n        var PML = mlMA[len_ml - 1];\\n        var PL = lMA[len_l - 1];\\n        \\n        var PS_prev = sMA[len_s - 2];\\n        var PMS_prev = msMA[len_ms - 2];\\n        var PML_prev = mlMA[len_ml - 2];\\n        var PL_prev = lMA[len_l - 2];\\n        \\n        // ==================== 指标1:均线排列形态 ====================\\n        var bullCount = 0;\\n        var bearCount = 0;\\n        \\n        var compare1 = PS > PMS ? 1 : (PS < PMS ? -1 : 0);\\n        var compare2 = PMS > PML ? 1 : (PMS < PML ? -1 : 0);\\n        var compare3 = PML > PL ? 1 : (PML < PL ? -1 : 0);\\n        \\n        if (compare1 > 0) bullCount++;\\n        if (compare2 > 0) bullCount++;\\n        if (compare3 > 0) bullCount++;\\n        if (compare1 < 0) bearCount++;\\n        if (compare2 < 0) bearCount++;\\n        if (compare3 < 0) bearCount++;\\n        \\n        var arrangementScore = 0;\\n        if (bullCount == 3) {\\n            arrangementScore = 4;\\n        } else if (bearCount == 3) {\\n            arrangementScore = -4;\\n        } else if (bullCount == 2) {\\n            if ((compare1 > 0 && compare2 > 0) || (compare2 > 0 && compare3 > 0)) {\\n                arrangementScore = 3;\\n            } else {\\n                arrangementScore = 2;\\n            }\\n        } else if (bearCount == 2) {\\n            if ((compare1 < 0 && compare2 < 0) || (compare2 < 0 && compare3 < 0)) {\\n                arrangementScore = -3;\\n            } else {\\n                arrangementScore = -2;\\n            }\\n        } else if (bullCount == 1) {\\n            arrangementScore = 1;\\n        } else if (bearCount == 1) {\\n            arrangementScore = -1;\\n        }\\n        \\n        // ==================== 指标2:均线扩散间距 ====================\\n        var gap1 = PS / PMS - 1;\\n        var gap2 = PMS / PML - 1;\\n        var gap3 = PML / PL - 1;\\n        var gapScore = (gap1 + gap2 + gap3) / 3;\\n        \\n        // ==================== 指标3:均线时序变化 ====================\\n        var riseCount = 0;\\n        var fallCount = 0;\\n        \\n        if (PS > PS_prev) riseCount++;\\n        if (PMS > PMS_prev) riseCount++;\\n        if (PML > PML_prev) riseCount++;\\n        if (PL > PL_prev) riseCount++;\\n        \\n        if (PS < PS_prev) fallCount++;\\n        if (PMS < PMS_prev) fallCount++;\\n        if (PML < PML_prev) fallCount++;\\n        if (PL < PL_prev) fallCount++;\\n        \\n        var timeSeriesScore = 0;\\n        if (riseCount > fallCount) {\\n            timeSeriesScore = riseCount;\\n        } else if (fallCount > riseCount) {\\n            timeSeriesScore = -fallCount;\\n        }\\n        \\n        // ==================== 合成均线综合得分 ====================\\n        var comprehensiveScore = 0;\\n        if (gapScore > 0) {\\n            comprehensiveScore = gapScore * arrangementScore * timeSeriesScore;\\n        } else if (gapScore < 0) {\\n            comprehensiveScore = gapScore * Math.abs(arrangementScore) * Math.abs(timeSeriesScore);\\n        }\\n        \\n        const coin = originalSymbol.replace('USDT', '');\\n        const isPosition = positionCoins.has(coin);\\n        const positionTag = isPosition ? ' [持仓]' : '';\\n        \\n        Log(\\\"🍃\\\", originalSymbol + positionTag, \\\"综合得分:\\\", comprehensiveScore.toFixed(6), \\\"| 排列:\\\", arrangementScore, \\\"扩散:\\\", gapScore.toFixed(6), \\\"时序:\\\", timeSeriesScore, \\\"| 多头:\\\", bullCount, \\\"空头:\\\", bearCount);\\n        \\n        allResults.push({\\n            originalSymbol: originalSymbol,\\n            processedSymbol: processedSymbol,\\n            score: comprehensiveScore,\\n            arrangementScore: arrangementScore,\\n            gapScore: gapScore,\\n            timeSeriesScore: timeSeriesScore,\\n            bullCount: bullCount,\\n            bearCount: bearCount,\\n            isPosition: isPosition,\\n            coin: coin\\n        });\\n        \\n    } catch (e) {\\n        Log(`❌ ${originalSymbol} 计算异常:`, e.message);\\n        continue;\\n    }\\n}\\n\\n// 4. 筛选正分和负分币种\\nvar positiveResults = allResults.filter(item => item.score > 0);\\nvar negativeResults = allResults.filter(item => item.score < 0);\\n\\n// 5. 排序\\npositiveResults.sort((a, b) => b.score - a.score);\\nnegativeResults.sort((a, b) => a.score - b.score);\\n\\n// ==================== 选取结果:前5 + 持仓多仓(type=0) ====================\\nvar topPositive = [];\\nvar topNegative = [];\\nvar supplementedLongPositions = new Set();  // 记录补充的多仓\\nvar supplementedShortPositions = new Set(); // 记录补充的空仓\\n\\n// 正向组:前5\\nfor (let i = 0; i < Math.min(5, positiveResults.length); i++) {\\n    topPositive.push(positiveResults[i]);\\n}\\n\\n// 检查多仓(type=0)是否在前5中,不在则添加(不管score多少)\\npositionCoins.forEach(coin => {\\n    const posInfo = positionMap[coin];\\n    if (posInfo && (posInfo.type === 0 || posInfo.type === PD_LONG)) {\\n        // 是多仓\\n        const symbol = coin + 'USDT';\\n        const inTop5 = topPositive.some(r => r.originalSymbol === symbol);\\n        \\n        if (!inTop5) {\\n            // 不在前5,查找这个币种的数据\\n            const coinData = allResults.find(r => r.originalSymbol === symbol);\\n            if (coinData) {\\n                topPositive.push(coinData);\\n                supplementedLongPositions.add(coin);  // 标记为补充的\\n                Log(`➕ 补充多仓到正向组: ${coin} (score=${coinData.score.toFixed(6)}, type=0, 不在前5)`);\\n            }\\n        }\\n    }\\n});\\n\\n// 负向组:后5\\nfor (let i = 0; i < Math.min(5, negativeResults.length); i++) {\\n    topNegative.push(negativeResults[i]);\\n}\\n\\n// 检查空仓(type=1)是否在后5中,不在则添加(不管score多少)\\npositionCoins.forEach(coin => {\\n    const posInfo = positionMap[coin];\\n    if (posInfo && posInfo.type === 1) {\\n        // 是空仓\\n        const symbol = coin + 'USDT';\\n        const inTop5 = topNegative.some(r => r.originalSymbol === symbol);\\n        \\n        if (!inTop5) {\\n            // 不在后5,查找这个币种的数据\\n            const coinData = allResults.find(r => r.originalSymbol === symbol);\\n            if (coinData) {\\n                topNegative.push(coinData);\\n                supplementedShortPositions.add(coin);  // 标记为补充的\\n                Log(`➕ 补充空仓到负向组: ${coin} (score=${coinData.score.toFixed(6)}, type=1, 不在后5)`);\\n            }\\n        }\\n    }\\n});\\n\\nLog(`📊 最终正向组: ${topPositive.length}个 (前5 + 所有多仓)`);\\nLog(`📊 最终负向组: ${topNegative.length}个 (后5 + 所有空仓)`);\\n\\n// 7. 格式化输出 - 修改positionStatus\\nvar positiveGroup = topPositive.map(item => {\\n    const coin = item.coin;\\n    let status = '无持仓';\\n    \\n    if (positionMap[coin]) {\\n        // 有持仓\\n        if (supplementedLongPositions.has(coin)) {\\n            // 是补充进来的多仓(不在前5)\\n            status = '持有多仓但指标强度不在前列';\\n        } else {\\n            // 在前5的多仓\\n            status = positionMap[coin].positionStatus;\\n        }\\n    }\\n    \\n    return {\\n        symbol: item.originalSymbol,\\n        processedSymbol: item.processedSymbol,\\n        score: item.score,\\n        arrangementScore: item.arrangementScore,\\n        gapScore: item.gapScore,\\n        timeSeriesScore: item.timeSeriesScore,\\n        bullCount: item.bullCount,\\n        positionStatus: status,\\n        positionInfo: positionMap[coin] || null\\n    };\\n});\\n\\nvar negativeGroup = topNegative.map(item => {\\n    const coin = item.coin;\\n    let status = '无持仓';\\n    \\n    if (positionMap[coin]) {\\n        // 有持仓\\n        if (supplementedShortPositions.has(coin)) {\\n            // 是补充进来的空仓(不在后5)\\n            status = '持有空仓但指标强度不在前列';\\n        } else {\\n            // 在后5的空仓\\n            status = positionMap[coin].positionStatus;\\n        }\\n    }\\n    \\n    return {\\n        symbol: item.originalSymbol,\\n        processedSymbol: item.processedSymbol,\\n        score: item.score,\\n        arrangementScore: item.arrangementScore,\\n        gapScore: item.gapScore,\\n        timeSeriesScore: item.timeSeriesScore,\\n        bearCount: item.bearCount,\\n        positionStatus: status,\\n        positionInfo: positionMap[coin] || null\\n    };\\n});\\n\\nLog(\\\"📈 正向组币种:\\\", positiveGroup.map(item => `${item.symbol}(${item.positionStatus})`).join(\\\", \\\"));\\nLog(\\\"📉 负向组币种:\\\", negativeGroup.map(item => `${item.symbol}(${item.positionStatus})`).join(\\\", \\\"));\\n\\n// 8. 返回结果\\nreturn {\\n    positive: positiveGroup,\\n    negative: negativeGroup,\\n    statistics: {\\n        totalSymbols: inputSymbols.length,\\n        validSymbols: allResults.length,\\n        positiveCount: positiveResults.length,\\n        negativeCount: negativeResults.length,\\n        neutralCount: allResults.length - positiveResults.length - negativeResults.length,\\n        positionCount: positionCoins.size,\\n        supplementedPositions: missingPositionSymbols.length\\n    },\\n    timestamp: new Date().toISOString()\\n};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[384,16],\"id\":\"676bcfe0-d4ee-4750-8dfd-d905948a6876\",\"name\":\"趋势强度识别\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// 1. 获取上一步的结果(已包含持仓信息)\\nvar inputData = $input.first().json;\\nvar positiveGroup = inputData.positive || [];\\nvar negativeGroup = inputData.negative || [];\\n\\n\\nLog(`📨 收到 ${positiveGroup.length} 个正向币种, ${negativeGroup.length} 个负向币种`);\\n\\n// 2. 提取币种代码的辅助函数(去掉USDT后缀)\\nfunction extractCurrency(symbol) {\\n    var currency = symbol.replace('USDT', '');\\n    return currency;\\n}\\n\\n// 3. 获取币种新闻的函数\\nfunction getCryptoNews(currency) {\\n    try {\\n        var url = \\\"https://min-api.cryptocompare.com/data/v2/news/?categories=\\\" + currency;\\n        var response = HttpQuery(url);\\n        \\n        if (!response) {\\n            Log(\\\"⚠️ 获取新闻失败: \\\" + currency + \\\" - 无响应\\\");\\n            return [];\\n        }\\n        \\n        var info = JSON.parse(response);\\n        \\n        if (info.Message === \\\"News list successfully returned\\\") {\\n            var news = [];\\n            var count = Math.min(5, info.Data.length);\\n            \\n            for (var i = 0; i < count; i++) {\\n                news.push({\\n                    title: info.Data[i].title\\n                });\\n            }\\n            Sleep(1000);\\n            \\n            return news;\\n        } else {\\n            Log(\\\"⚠️ 获取新闻失败:\\\", currency, info.Message);\\n            return [];\\n        }\\n    } catch (e) {\\n        Log(\\\"❌ 获取新闻异常:\\\", currency, e.message || e);\\n        return [];\\n    }\\n}\\n\\n// 4. 处理positive组(直接使用已有的持仓信息)\\nvar positiveWithNews = positiveGroup.map(function(coin) {\\n    try {\\n        var currency = extractCurrency(coin.symbol);\\n        Log(\\\"正在获取\\\", currency, \\\"的新闻...\\\");\\n        \\n        var news = getCryptoNews(currency);\\n        \\n        return {\\n            symbol: coin.symbol,\\n            processedSymbol: coin.processedSymbol,\\n            score: coin.score,\\n            arrangementScore: coin.arrangementScore,\\n            gapScore: coin.gapScore,\\n            timeSeriesScore: coin.timeSeriesScore,\\n            bullCount: coin.bullCount,\\n            recentNews: news,\\n            positionStatus: coin.positionStatus,\\n            positionInfo: coin.positionInfo\\n        };\\n    } catch (e) {\\n        Log(\\\"❌ 处理正向币种异常:\\\", coin.symbol, e.message || e);\\n        // 返回基础数据,不包含新闻\\n        return {\\n            symbol: coin.symbol,\\n            processedSymbol: coin.processedSymbol,\\n            score: coin.score,\\n            arrangementScore: coin.arrangementScore,\\n            gapScore: coin.gapScore,\\n            timeSeriesScore: coin.timeSeriesScore,\\n            bullCount: coin.bullCount,\\n            recentNews: [],\\n            positionStatus: coin.positionStatus,\\n            positionInfo: coin.positionInfo\\n        };\\n    }\\n});\\n\\n// 5. 处理negative组\\nvar negativeWithNews = negativeGroup.map(function(coin) {\\n    try {\\n        var currency = extractCurrency(coin.symbol);\\n        Log(\\\"正在获取\\\", currency, \\\"的新闻...\\\");\\n        \\n        var news = getCryptoNews(currency);\\n        \\n        return {\\n            symbol: coin.symbol,\\n            processedSymbol: coin.processedSymbol,\\n            score: coin.score,\\n            arrangementScore: coin.arrangementScore,\\n            gapScore: coin.gapScore,\\n            timeSeriesScore: coin.timeSeriesScore,\\n            bearCount: coin.bearCount,\\n            recentNews: news,\\n            newsCount: news.length,\\n            positionStatus: coin.positionStatus,\\n            positionInfo: coin.positionInfo\\n        };\\n    } catch (e) {\\n        Log(\\\"❌ 处理负向币种异常:\\\", coin.symbol, e.message || e);\\n        // 返回基础数据,不包含新闻\\n        return {\\n            symbol: coin.symbol,\\n            processedSymbol: coin.processedSymbol,\\n            score: coin.score,\\n            arrangementScore: coin.arrangementScore,\\n            gapScore: coin.gapScore,\\n            timeSeriesScore: coin.timeSeriesScore,\\n            bearCount: coin.bearCount,\\n            recentNews: [],\\n            newsCount: 0,\\n            positionStatus: coin.positionStatus,\\n            positionInfo: coin.positionInfo\\n        };\\n    }\\n});\\n\\n// 6. 返回结果\\nreturn [{\\n    positive: positiveWithNews,\\n    negative: negativeWithNews,\\n    statistics: inputData.statistics,\\n    timestamp: new Date().toISOString()\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[608,16],\"id\":\"448bdb05-b9d2-4f04-b4ee-dad3c0273b81\",\"name\":\"实时新闻获取\"},{\"parameters\":{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"loose\",\"version\":2},\"conditions\":[{\"id\":\"50b098c4-4f2d-4006-b76b-eba20c0c0ed5\",\"leftValue\":\"={{ $json.statistics.positiveCount + $json.statistics.negativeCount}}\",\"rightValue\":\"0\",\"operator\":{\"type\":\"string\",\"operation\":\"equals\",\"name\":\"filter.operator.equals\"}}],\"combinator\":\"and\"},\"looseTypeValidation\":true,\"options\":{}},\"type\":\"n8n-nodes-base.if\",\"typeVersion\":2.2,\"position\":[832,16],\"id\":\"f23167b5-c514-4783-b928-a9b2fb56bef0\",\"name\":\"交易判断\"},{\"parameters\":{\"logAll\":false,\"output\":\"🌈:不存在任何分组,不进行处理 :)\"},\"type\":\"n8n-nodes-base.log\",\"typeVersion\":1,\"position\":[1152,-144],\"id\":\"e9875f03-75af-4ee4-a541-40695c410380\",\"name\":\"无交易处理\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== 极简版交易执行系统 ==========\\n// 功能:解析AI决策、执行开平仓、U本位固定金额交易、安全反手操作\\n\\n// ========== 配置参数 ==========\\nconst CONFIG = {\\n    FIXED_AMOUNT_USD: $vars.Amount,  // 固定交易金额(U)\\n    DEFAULT_LEVERAGE: 10,            // 默认杠杆\\n    MAX_CLOSE_WAIT: 10,              // 平仓验证最大等待次数\\n    CLOSE_CHECK_INTERVAL: 1000,      // 平仓检查间隔(毫秒)\\n    MAX_MARKET_RETRY: 20,            // 获取市场信息最大重试次数\\n    MARKET_RETRY_INTERVAL: 500       // 市场信息重试间隔(毫秒)\\n};\\n\\n// ========== 工具函数 ==========\\n\\n/**\\n * 解析AI输出 - 提取币种和决策\\n */\\nfunction parseAIOutput(output) {\\n    try {\\n        Log('📥 开始解析AI输出...');\\n        \\n        // 如果是字符串,解析为JSON\\n        let parsed;\\n        if (typeof output === 'string') {\\n            let cleaned = output.replace(/```[a-z]*\\\\n?/gi, '').trim();\\n            parsed = JSON.parse(cleaned);\\n        } else {\\n            parsed = output;\\n        }\\n        \\n        // 检查是否是数组\\n        if (!Array.isArray(parsed)) {\\n            Log('⚠️ AI输出不是数组格式');\\n            return [];\\n        }\\n        \\n        // 转换为标准信号格式\\n        const signals = [];\\n        \\n        for (let item of parsed) {\\n            // 提取币种 (SOLUSDT -> SOL)\\n            const coin = item.symbol.replace('USDT', '');\\n            \\n            // 映射决策\\n            let signal;\\n            switch (item.decision) {\\n                case '开多':\\n                    signal = 'buy';\\n                    break;\\n                case '开空':\\n                    signal = 'sell';\\n                    break;\\n                case '平仓':\\n                    signal = 'close';\\n                    break;\\n                case '平多开空':\\n                    signal = 'close_then_sell';\\n                    break;\\n                case '平空开多':\\n                    signal = 'close_then_buy';\\n                    break;\\n                case '持有':\\n                case '观望':\\n                    signal = 'hold';\\n                    break;\\n                default:\\n                    signal = 'hold';\\n            }\\n            \\n            signals.push({\\n                coin: coin,\\n                signal: signal,\\n                originalDecision: item.decision,\\n                newsAnalysis: item.newsAnalysis,\\n                overallJudgment: item.overallJudgment,\\n                symbol: item.symbol,\\n                score: item.score || 0,\\n                currentPosition: item.currentPosition || '未知'\\n            });\\n        }\\n        \\n        Log(`✅ 成功解析 ${signals.length} 个AI信号`);\\n        return signals;\\n        \\n    } catch (e) {\\n        Log(`❌ parseAIOutput失败: ${e.message}`);\\n        Log(`原始输出: ${JSON.stringify(output)}`);\\n        return [];\\n    }\\n}\\n\\n/**\\n * 持续获取市场信息直到成功\\n */\\nfunction getMarketInfo(coin) {\\n    try {\\n        const symbol = coin + '_USDT.swap';\\n        let attempts = 0;\\n        \\n        Log(`🔍 ${coin}: 开始获取市场信息...`);\\n        \\n        while (attempts < CONFIG.MAX_MARKET_RETRY) {\\n            attempts++;\\n            \\n            try {\\n                // 先从缓存获取\\n                const markets = _G('markets');\\n                if (markets && markets[symbol]) {\\n                    return markets[symbol];\\n                }\\n                \\n                // 缓存没有,实时获取\\n                const market = exchange.GetMarkets();\\n                \\n                if (market && market[symbol]) {\\n                    // 更新缓存\\n                    let allMarkets = _G('markets') || {};\\n                    allMarkets[symbol] = market[symbol];\\n                    _G('markets', allMarkets);\\n                    \\n                    return market[symbol];\\n                }\\n                \\n                Log(`⚠️ ${coin}: 第${attempts}次获取市场信息失败,重试中...`);\\n                Sleep(CONFIG.MARKET_RETRY_INTERVAL);\\n                \\n            } catch (e) {\\n                Log(`⚠️ ${coin}: 第${attempts}次获取失败: ${e.message}`);\\n                Sleep(CONFIG.MARKET_RETRY_INTERVAL);\\n            }\\n        }\\n        \\n        Log(`❌ ${coin}: 获取市场信息失败,已重试${attempts}次`);\\n        return null;\\n        \\n    } catch (e) {\\n        Log(`❌ ${coin}: getMarketInfo异常: ${e.message}`);\\n        return null;\\n    }\\n}\\n\\n/**\\n * 获取当前价格\\n */\\nfunction getCurrentPrice(coin) {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        \\n        const ticker = exchange.GetTicker();\\n        if (!ticker) {\\n            Log(`❌ ${coin}: 获取价格失败`);\\n            return 0;\\n        }\\n        \\n        return ticker.Last;\\n    } catch (e) {\\n        Log(`❌ ${coin}: 获取价格异常: ${e.message}`);\\n        return 0;\\n    }\\n}\\n\\n/**\\n * 计算合约张数\\n * 公式:张数 = 金额(U) / 当前价格 / 合约面值(CtVal)\\n */\\nfunction calculateContractAmount(amountUSD, currentPrice, market) {\\n    try {\\n        if (!market || !market.CtVal) {\\n            Log(`❌ 缺少市场信息或合约面值`);\\n            return 0;\\n        }\\n        \\n        // 计算币的数量\\n        const coinAmount = amountUSD / currentPrice;\\n        \\n        // 转换为合约张数\\n        let contractAmount = coinAmount / market.CtVal;\\n        \\n        // 应用数量精度\\n        contractAmount = _N(contractAmount, market.AmountPrecision);\\n        \\n        // 检查最小数量\\n        if (contractAmount < market.MinQty) {\\n            Log(`⚠️ 计算张数 ${contractAmount} 小于最小值 ${market.MinQty}`);\\n            return 0;\\n        }\\n        \\n        // 检查最大数量\\n        if (contractAmount > market.MaxQty) {\\n            Log(`⚠️ 计算张数 ${contractAmount} 超过最大值 ${market.MaxQty},调整为最大值`);\\n            contractAmount = market.MaxQty;\\n        }\\n        \\n        Log(`💡 金额$${amountUSD} / 价格$${currentPrice} / 面值${market.CtVal} = ${contractAmount}张`);\\n        \\n        return contractAmount;\\n        \\n    } catch (e) {\\n        Log(`❌ 计算合约张数失败: ${e.message}`);\\n        return 0;\\n    }\\n}\\n\\n/**\\n * 检查是否有持仓\\n */\\nfunction hasPosition(coin) {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        const positions = exchange.GetPositions();\\n        return positions && positions.some(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n    } catch (e) {\\n        Log(`⚠️ 检查${coin}持仓失败: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n/**\\n * 获取当前持仓信息\\n */\\nfunction getPosition(coin) {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        const positions = exchange.GetPositions();\\n        return positions && positions.find(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n    } catch (e) {\\n        return null;\\n    }\\n}\\n\\n/**\\n * 等待持仓清空 - 平仓后验证\\n */\\nfunction waitForPositionClear(coin) {\\n    try {\\n        Log(`⏳ ${coin}: 等待持仓清空...`);\\n        \\n        let attempts = 0;\\n        \\n        while (attempts < CONFIG.MAX_CLOSE_WAIT) {\\n            Sleep(CONFIG.CLOSE_CHECK_INTERVAL);\\n            \\n            const hasPos = hasPosition(coin);\\n            \\n            if (!hasPos) {\\n                Log(`✅ ${coin}: 持仓已清空`);\\n                return true;\\n            }\\n            \\n            attempts++;\\n            Log(`⏳ ${coin}: 第${attempts}次检查,持仓仍存在...`);\\n        }\\n        \\n        Log(`❌ ${coin}: 等待持仓清空超时`);\\n        return false;\\n        \\n    } catch (e) {\\n        Log(`❌ ${coin}: 验证持仓清空失败: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n// ========== 交易执行函数 ==========\\n\\n/**\\n * 执行平仓\\n */\\nfunction executeClose(coin, reason = \\\"AI信号\\\") {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        \\n        // 取消所有未完成订单\\n        const orders = exchange.GetOrders();\\n        if (orders && orders.length > 0) {\\n            orders.forEach(o => {\\n                try {\\n                    exchange.CancelOrder(o.Id);\\n                } catch (e) {}\\n            });\\n        }\\n        \\n        // 获取持仓\\n        const pos = getPosition(coin);\\n        \\n        if (!pos) {\\n            Log(`⚠️ ${coin}: 未找到持仓`);\\n            return false;\\n        }\\n        \\n        // 获取市场信息(用于精度)\\n        const market = getMarketInfo(coin);\\n        if (!market) {\\n            Log(`❌ ${coin}: 无法获取市场信息,平仓失败`);\\n            return false;\\n        }\\n        \\n        const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n        const closeAmount = _N(Math.abs(pos.Amount), market.AmountPrecision);\\n        \\n        Log(`📤 ${coin}: 准备平仓 方向=${isLong ? '多' : '空'} 数量=${closeAmount}张`);\\n        \\n        // 设置平仓方向\\n        exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n        \\n        // 执行平仓(市价)\\n        const orderId = isLong ? exchange.Sell(-1, closeAmount) : exchange.Buy(-1, closeAmount);\\n        \\n        if (orderId) {\\n            const dirStr = isLong ? '多' : '空';\\n            Log(`✅ ${coin}: 平${dirStr}订单已提交 (${reason}) OrderID=${orderId}`);\\n            return true;\\n        } else {\\n            Log(`❌ ${coin}: 平仓订单提交失败`);\\n            return false;\\n        }\\n    } catch (e) {\\n        Log(`❌ ${coin} 平仓执行失败: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n/**\\n * 执行开仓 - U本位下单\\n */\\nfunction executeEntry(coin, isLong) {\\n    try {\\n        exchange.SetCurrency(coin + '_USDT');\\n        exchange.SetContractType(\\\"swap\\\");\\n        \\n        // 设置杠杆\\n        exchange.SetMarginLevel(CONFIG.DEFAULT_LEVERAGE);\\n        \\n        // 获取市场信息\\n        const market = getMarketInfo(coin);\\n        if (!market) {\\n            Log(`❌ ${coin}: 无法获取市场信息,开仓失败`);\\n            return false;\\n        }\\n        \\n        // 获取当前价格\\n        const currentPrice = getCurrentPrice(coin);\\n        if (currentPrice <= 0) {\\n            Log(`❌ ${coin}: 无法获取当前价格,开仓失败`);\\n            return false;\\n        }\\n        \\n        // 计算合约张数\\n        const contractAmount = calculateContractAmount(\\n            CONFIG.FIXED_AMOUNT_USD, \\n            currentPrice, \\n            market\\n        );\\n        \\n        if (contractAmount <= 0) {\\n            Log(`⚠️ ${coin}: 计算合约张数无效,开仓失败`);\\n            return false;\\n        }\\n        \\n        Log(`📥 ${coin}: 准备开仓`);\\n        Log(`   方向: ${isLong ? '多' : '空'}`);\\n        Log(`   金额: $${CONFIG.FIXED_AMOUNT_USD}`);\\n        Log(`   价格: $${currentPrice}`);\\n        Log(`   面值: ${market.CtVal}`);\\n        Log(`   张数: ${contractAmount}`);\\n        Log(`   杠杆: ${CONFIG.DEFAULT_LEVERAGE}x`);\\n        \\n        // 设置方向\\n        exchange.SetDirection(isLong ? \\\"buy\\\" : \\\"sell\\\");\\n        \\n        // 执行开仓(市价)\\n        const orderId = isLong ? exchange.Buy(-1, contractAmount) : exchange.Sell(-1, contractAmount);\\n        \\n        if (orderId) {\\n            const dirStr = isLong ? '多' : '空';\\n            const symbolKey = `${coin}_USDT.swap_maxprofit`;\\n            _G(symbolKey, 0);\\n            Log(`✅ ${coin}: 开${dirStr}订单已提交 OrderID=${orderId}`);\\n            return true;\\n        } else {\\n            Log(`❌ ${coin}: 开仓订单提交失败`);\\n            return false;\\n        }\\n    } catch (e) {\\n        Log(`❌ ${coin} 开仓执行失败: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n/**\\n * 执行反手操作 - 平仓后验证再开仓\\n */\\nfunction executeReverse(coin, isLong, reason) {\\n    try {\\n        Log(`🔄 ${coin}: 开始反手操作 - ${reason}`);\\n        \\n        // 步骤1: 先平仓\\n        const closeSuccess = executeClose(coin, reason);\\n        \\n        if (!closeSuccess) {\\n            Log(`❌ ${coin}: 平仓失败,取消反手操作`);\\n            return false;\\n        }\\n        \\n        // 步骤2: 等待并验证持仓已清空\\n        const positionCleared = waitForPositionClear(coin);\\n        \\n        if (!positionCleared) {\\n            Log(`❌ ${coin}: 持仓未清空,取消开新仓`);\\n            return false;\\n        }\\n        \\n        // 步骤3: 再次确认无持仓\\n        const stillHasPosition = hasPosition(coin);\\n        \\n        if (stillHasPosition) {\\n            Log(`❌ ${coin}: 最终检查发现仍有持仓,取消开新仓`);\\n            return false;\\n        }\\n        \\n        Log(`✅ ${coin}: 持仓已确认清空,开始开新仓`);\\n        \\n        // 步骤4: 执行开新仓\\n        const entrySuccess = executeEntry(coin, isLong);\\n        \\n        if (entrySuccess) {\\n            Log(`✅ ${coin}: 反手操作完成 - 新方向=${isLong ? '多' : '空'}`);\\n            return true;\\n        } else {\\n            Log(`❌ ${coin}: 开新仓失败,反手操作未完成`);\\n            return false;\\n        }\\n        \\n    } catch (e) {\\n        Log(`❌ ${coin}: 反手操作失败: ${e.message}`);\\n        return false;\\n    }\\n}\\n\\n// ========== 主执行逻辑 ==========\\n\\nfunction main() {\\n    // 获取AI输出\\n    const aiOutput = $input.first().json.output;\\n    \\n    Log('📨 收到AI输出');\\n    \\n    // 解析信号\\n    const signals = parseAIOutput(aiOutput);\\n    \\n    if (!signals || signals.length === 0) {\\n        Log('⚠️ 未获取到有效的AI交易信号');\\n        return { json: { processed: false, reason: 'No valid signals' } };\\n    }\\n    \\n    Log(`🤖 解析到 ${signals.length} 个交易信号`);\\n    Log(`💰 固定交易金额: $${CONFIG.FIXED_AMOUNT_USD}`);\\n    Log(`📊 默认杠杆: ${CONFIG.DEFAULT_LEVERAGE}x`);\\n    \\n    // 执行结果统计\\n    const executionResults = {};\\n    let totalExecutions = 0;\\n    \\n    // 处理每个信号\\n    for (let signalInfo of signals) {\\n        const coin = signalInfo.coin;\\n        const signal = signalInfo.signal;\\n        \\n        Log(`\\\\n${'='.repeat(60)}`);\\n        Log(`🎯 处理 ${coin} - 决策: ${signalInfo.originalDecision}`);\\n        Log(`   新闻参考: ${signalInfo.newsAnalysis}`);\\n        Log(`   决策理由: ${signalInfo.overallJudgment}`);\\n        Log(`   评分: ${signalInfo.score}`);\\n        Log(`   原有持仓: ${signalInfo.currentPosition}`);\\n        \\n        const hasPos = hasPosition(coin);\\n        let executed = false;\\n        let skipReason = '';\\n        let operations = 0;  // 记录操作次数(反手=2次)\\n        \\n        // 根据信号执行操作\\n        switch (signal) {\\n            case 'buy':\\n                if (hasPos) {\\n                    skipReason = '已有持仓';\\n                } else {\\n                    executed = executeEntry(coin, true);\\n                    if (executed) operations = 1;\\n                }\\n                break;\\n                \\n            case 'sell':\\n                if (hasPos) {\\n                    skipReason = '已有持仓';\\n                } else {\\n                    executed = executeEntry(coin, false);\\n                    if (executed) operations = 1;\\n                }\\n                break;\\n                \\n            case 'close':\\n                if (!hasPos) {\\n                    skipReason = '无持仓';\\n                } else {\\n                    executed = executeClose(coin, 'AI平仓信号');\\n                    if (executed) operations = 1;\\n                }\\n                break;\\n                \\n            case 'close_then_buy':\\n                if (!hasPos) {\\n                    skipReason = '无持仓无法反手';\\n                } else {\\n                    executed = executeReverse(coin, true, '平空开多');\\n                    if (executed) operations = 2;  // 平仓+开仓\\n                }\\n                break;\\n                \\n            case 'close_then_sell':\\n                if (!hasPos) {\\n                    skipReason = '无持仓无法反手';\\n                } else {\\n                    executed = executeReverse(coin, false, '平多开空');\\n                    if (executed) operations = 2;  // 平仓+开仓\\n                }\\n                break;\\n                \\n            case 'hold':\\n                skipReason = hasPos ? '持仓观望' : '空仓等待';\\n                break;\\n                \\n            default:\\n                skipReason = '未知信号';\\n        }\\n        \\n        // 累计执行次数\\n        totalExecutions += operations;\\n        \\n        // 记录结果\\n        executionResults[coin] = {\\n            signal: signal,\\n            decision: signalInfo.originalDecision,\\n            newsAnalysis: signalInfo.newsAnalysis,\\n            overallJudgment: signalInfo.overallJudgment,\\n            executed: executed,\\n            operations: operations,\\n            skipReason: skipReason,\\n            score: signalInfo.score,\\n            timestamp: Date.now()\\n        };\\n        \\n        // 输出结果\\n        if (executed) {\\n            Log(`✅ ${coin}: ${signalInfo.originalDecision} 执行成功 (操作${operations}次)`);\\n        } else if (skipReason) {\\n            Log(`⏭️ ${coin}: ${signalInfo.originalDecision} 跳过 - ${skipReason}`);\\n        } else {\\n            Log(`❌ ${coin}: ${signalInfo.originalDecision} 执行失败`);\\n        }\\n        \\n        Sleep(500);\\n    }\\n    \\n    // 保存执行结果\\n    const summary = {\\n        results: executionResults,\\n        timestamp: Date.now(),\\n        totalSignals: signals.length,\\n        totalExecutions: totalExecutions,\\n        fixedAmountUSD: CONFIG.FIXED_AMOUNT_USD,\\n        leverage: CONFIG.DEFAULT_LEVERAGE\\n    };\\n    \\n    _G('latestExecutionResults', summary);\\n    \\n    Log(`\\\\n${'='.repeat(60)}`);\\n    Log(`📊 执行总结:`);\\n    Log(`   信号总数: ${signals.length}`);\\n    Log(`   执行次数: ${totalExecutions}`);\\n    Log(`   固定金额: $${CONFIG.FIXED_AMOUNT_USD}`);\\n    Log(`   杠杆倍数: ${CONFIG.DEFAULT_LEVERAGE}x`);\\n    Log(`${'='.repeat(60)}`);\\n    \\n    return { \\n        json: { \\n            processed: true, \\n            totalSignals: signals.length,\\n            totalExecutions: totalExecutions,\\n            results: executionResults\\n        } \\n    };\\n        \\n    \\n}\\n\\n// 执行主函数\\nreturn main();\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[1408,160],\"id\":\"628efea5-55c9-4b0d-8fe6-1f9ac91b0813\",\"name\":\"交易执行\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"seconds\",\"secondsInterval\":15}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-512,304],\"id\":\"280457ba-a4b7-4722-95b9-034400b7adcd\",\"name\":\"止盈止损触发器\",\"notesInFlow\":false,\"logLevel\":0}],\"pinData\":{},\"connections\":{\"定时触发器\":{\"main\":[[{\"node\":\"初始设置\",\"type\":\"main\",\"index\":0}]]},\"OpenAI 模型\":{\"ai_languageModel\":[[{\"node\":\"AI 智能体\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"AI 智能体\":{\"main\":[[{\"node\":\"交易执行\",\"type\":\"main\",\"index\":0}]]},\"初始设置\":{\"main\":[[{\"node\":\"币种查询\",\"type\":\"main\",\"index\":0}]]},\"币种查询\":{\"main\":[[{\"node\":\"高流动币种查询\",\"type\":\"main\",\"index\":0}]]},\"高流动币种查询\":{\"main\":[[{\"node\":\"趋势强度识别\",\"type\":\"main\",\"index\":0}]]},\"趋势强度识别\":{\"main\":[[{\"node\":\"实时新闻获取\",\"type\":\"main\",\"index\":0}]]},\"实时新闻获取\":{\"main\":[[{\"node\":\"交易判断\",\"type\":\"main\",\"index\":0}]]},\"交易判断\":{\"main\":[[{\"node\":\"无交易处理\",\"type\":\"main\",\"index\":0}],[{\"node\":\"AI 智能体\",\"type\":\"main\",\"index\":0}]]},\"止盈止损触发器\":{\"main\":[[{\"node\":\"止盈止损监控\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"meta\":{\"templateCredsSetupCompleted\":true},\"credentials\":{},\"id\":\"c24195f3-b2f5-47a1-a0aa-5acf1a5566f9\",\"plugins\":{},\"mcpClients\":{}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"定时触发器\"}}"}