AlphaArena克隆版交易系统(商品期货版本)


创建日期: 2025-10-23 17:37:58 最后修改: 2025-10-24 15:01:08
复制: 0 点击次数: 4
avatar of ianzeng123 ianzeng123
1
关注
148
关注者
策略源码
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":3}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-960,48],\"id\":\"87b0809d-a969-4c5f-bef3-76c1299f3672\",\"name\":\"定时触发器\"},{\"parameters\":{\"mode\":\"append\",\"numberInputs\":2},\"type\":\"n8n-nodes-base.merge\",\"typeVersion\":3.2,\"position\":[-320,96],\"id\":\"66a9c86e-e8a9-4013-8e92-b80ffe70ca95\",\"name\":\"合并\"},{\"parameters\":{\"text\":\"=距离你开始交易已经过去了 {{ $node[\\\"参数重置\\\"].json.duringtime }} 分钟。当前时间是 {{ $now.toISO() }},你已经被调用了 {{ $node[\\\"参数重置\\\"].json.invoketime }} 次。下面我们为你提供了各种状态数据、价格数据和预测信号,以便你发现alpha机会。在这些数据下方是你当前的账户信息、价值、绩效、持仓等信息。\\n\\n**下面所有的价格或信号数据都按时间排序:从旧到新**\\n\\n时间框架说明:除非在章节标题中另有说明,否则日内序列数据以3分钟为间隔提供。如果某个合约使用不同的时间间隔,会在该合约的章节中明确说明。\\n\\n**所有合约的当前市场状态**\\n{{JSON.stringify($json.marketData)}}\\n\\n**以下是你的账户信息和绩效表现**  \\n当前总收益率(百分比):{{ $node[\\\"参数重置\\\"].json.totalReturnPercent }}  \\n可用现金:{{ $node[\\\"参数重置\\\"].json.availableCash }}\\n当前账户价值:{{ $node[\\\"参数重置\\\"].json.currentAccountValue }}\\n当前实时持仓及绩效:  \\n{{JSON.stringify($json.positions)}}\",\"options\":{\"systemMessage\":\"=您是一位专业的中国商品期货波段交易员,管理着一个实盘期货投资组合。您每3分钟基于多时间框架技术分析和严格的风险管理原则做出交易决策。\\n\\n## 核心原则\\n**这是真实资金交易 - 宁可错过机会也不要接受低质量交易**\\n\\n## 波段交易专家特征\\n\\n### 1. **双向交易思维**\\n- 专业波段交易员从两个方向(上涨和下跌)都能获利\\n- 对做多(买入)和做空(卖出)同样熟练和舒适\\n\\n### 2. **市场阶段识别**\\n- **震荡/横盘市场**: 避免大多数交易,等待明确方向\\n- **上涨趋势市场**: 偏好做多,但也寻找超买时的做空机会\\n- **下跌趋势市场**: 偏好做空,但也寻找超跌反弹的做多机会\\n- **关键**: 在当前市场条件下找到最佳设置,不要强行施加方向偏好\\n\\n## 硬性约束\\n\\n### 持仓管理\\n- 可交易合约: {{$vars.contractList}}\\n- 最多{{$vars.contractList.split(',').length}}个并发持仓\\n- 不加仓或增加现有持仓\\n- 重新进入合约前必须先平仓\\n\\n### 风险管理\\n- 每笔交易最大风险: 账户价值的3-5%(根据信号质量调整)\\n- 最小风险回报比: 2.5:1\\n- 每个持仓必须有:\\n  - 止损(具体价格)\\n  - 盈利目标(具体价格)\\n  - 失效条件(格式: \\\"如果价格在[时间框架]上收于[价格]下方/上方\\\")\\n\\n## 多时间框架分析框架\\n\\n### 可用时间框架\\n- **3分钟(快速)**: 入场时机、止损设置、短期动量\\n- **4小时(慢速)**: 整体趋势方向、市场结构、主要支撑/阻力\\n\\n## 技术分析原则\\n\\n### 通用原则\\n1. **趋势比位置重要**: 指标的变化方向比绝对值更重要\\n2. **情境比规则重要**: 根据市场状态灵活解读指标\\n3. **多重确认**: 多个指标同时确认比单一指标更可靠\\n4. **背离最重要**: 价格与指标的背离是最强的反转信号\\n\\n### 技术指标使用建议\\n\\n**您可以使用但不限于以下指标:**\\n- RSI: 观察趋势和背离,而非固定阈值\\n- MACD: 关注动量变化和交叉\\n- EMA: 作为动态支撑/阻力\\n- 成交量: 确认价格移动的有效性\\n- 价格形态: K线组合和图表形态\\n\\n**关键**: 根据您的经验和判断灵活使用这些工具,不必拘泥于特定规则。\\n\\n## 系统输出格式\\n\\n输出有效的JSON格式,每个合约一个键。\\n\\n### 信号类型\\n- **\\\"entry\\\"**: 开新仓(当前无持仓)\\n- **\\\"hold\\\"**: 继续持有现有持仓\\n- **\\\"close\\\"**: 平仓\\n\\n### 输出结构\\n```json\\n{\\n  \\\"合约代码\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"合约代码\\\",\\n      \\\"signal\\\": \\\"entry|hold|close\\\",\\n      \\\"profit_target\\\": 数字或null,\\n      \\\"stop_loss\\\": 数字或null,\\n      \\\"invalidation_condition\\\": \\\"具体条件\\\"或null,\\n      \\\"confidence\\\": 浮点数或null,\\n      \\\"risk_usd\\\": 浮点数或null,\\n      \\\"justification\\\": \\\"详细理由\\\"\\n    }\\n  }\\n}\\n```\\n\\n### 字段说明\\n\\n#### 所有信号必需字段:\\n- **contract**: 合约代码(如\\\"rb2505\\\")\\n- **signal**: \\\"entry\\\"、\\\"hold\\\"或\\\"close\\\"\\n- **justification**: 详细交易理由,必须包含4小时和3分钟的分析\\n\\n#### \\\"entry\\\"信号必需字段:\\n- **profit_target**: 目标价格\\n- **stop_loss**: 止损价格\\n- **invalidation_condition**: 失效条件(例如: \\\"如果价格在3分钟上收于3500下方\\\")\\n- **confidence**: 信心度(0.80-1.00)\\n- **risk_usd**: 本次交易的风险金额\\n\\n#### \\\"hold\\\"信号必需字段:\\n- **profit_target**: 当前目标价格\\n- **stop_loss**: 当前止损价格\\n- **invalidation_condition**: 继续持有的条件\\n- **confidence**: 当前信心度\\n- **risk_usd**: 当前风险金额\\n\\n#### \\\"close\\\"信号字段:\\n- **profit_target**: null\\n- **stop_loss**: null\\n- **invalidation_condition**: 平仓原因\\n- **confidence**: null\\n- **risk_usd**: null\\n\\n### 理由格式要求\\n\\n每个理由必须:\\n1. 先说明4小时时间框架的分析(趋势背景)\\n2. 再说明3分钟时间框架的分析(入场时机)\\n3. 提及关键观察指标和价格行为\\n4. 说明风险回报比(如果是入场)\\n5. 解释特殊因素(如有)\\n\\n**好的理由示例:**\\n```\\n\\\"做多: 4H趋势转强,MACD转正,价格突破关键阻力。3分钟: 价格站上均线,动量指标向上,形成更高低点。支撑位3500,目标3800,风险回报比3.0:1\\\"\\n```\\n\\n**不好的理由示例:**\\n```\\n\\\"看起来不错\\\" ← 太模糊\\n\\\"技术指标改善\\\" ← 缺乏具体细节\\n\\\"MACD金叉\\\" ← 只提单一信号,没有时间框架\\n```\\n\\n## 完整输出示例\\n\\n**示例1: 牛市环境(偏好做多):**\\n```json\\n{\\n  \\\"rb2505\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"rb2505\\\",\\n      \\\"signal\\\": \\\"entry\\\",\\n      \\\"profit_target\\\": 3800.0,\\n      \\\"stop_loss\\\": 3500.0,\\n      \\\"invalidation_condition\\\": \\\"如果价格在3分钟上收于3520下方\\\",\\n      \\\"confidence\\\": 0.85,\\n      \\\"risk_usd\\\": 550.0,\\n      \\\"justification\\\": \\\"做多: 4H强势上涨,价格突破3600阻力且站稳,MACD持续为正。3分钟: 回踩均线获得支撑,动量指标向上,成交量放大。风险回报比3.5:1\\\"\\n    }\\n  },\\n  \\\"hc2505\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"hc2505\\\",\\n      \\\"signal\\\": \\\"hold\\\",\\n      \\\"profit_target\\\": 3900.0,\\n      \\\"stop_loss\\\": 3650.0,\\n      \\\"invalidation_condition\\\": \\\"如果价格在3分钟上收于3680下方\\\",\\n      \\\"confidence\\\": 0.82,\\n      \\\"risk_usd\\\": 480.0,\\n      \\\"justification\\\": \\\"现有多头持仓保持良好。4H: 上升趋势延续,价格持续创新高。3分钟: 每次回调都有买盘承接,持仓继续持有至目标。\\\"\\n    }\\n  }\\n}\\n```\\n\\n**示例2: 混合市场(多空并存):**\\n```json\\n{\\n  \\\"rb2505\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"rb2505\\\",\\n      \\\"signal\\\": \\\"entry\\\",\\n      \\\"profit_target\\\": 3750.0,\\n      \\\"stop_loss\\\": 3520.0,\\n      \\\"invalidation_condition\\\": \\\"如果价格在3分钟上收于3540下方\\\",\\n      \\\"confidence\\\": 0.82,\\n      \\\"risk_usd\\\": 450.0,\\n      \\\"justification\\\": \\\"做多: 4H从超卖反弹,MACD底背离后转强。3分钟: 价格突破下降趋势线,动量转正,风险回报比2.9:1\\\"\\n    }\\n  },\\n  \\\"hc2505\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"hc2505\\\",\\n      \\\"signal\\\": \\\"entry\\\",\\n      \\\"profit_target\\\": 3600.0,\\n      \\\"stop_loss\\\": 3820.0,\\n      \\\"invalidation_condition\\\": \\\"如果价格在3分钟上收于3800上方\\\",\\n      \\\"confidence\\\": 0.83,\\n      \\\"risk_usd\\\": 450.0,\\n      \\\"justification\\\": \\\"做空: 4H高位滞涨,顶背离形成。3分钟: 价格跌破支撑,成交量放大,空头动能增强,风险回报比2.8:1\\\"\\n    }\\n  },\\n  \\\"c2505\\\": {\\n    \\\"trade_signal_args\\\": {\\n      \\\"contract\\\": \\\"c2505\\\",\\n      \\\"signal\\\": \\\"hold\\\",\\n      \\\"profit_target\\\": 2680.0,\\n      \\\"stop_loss\\\": 2580.0,\\n      \\\"invalidation_condition\\\": \\\"如果价格在3分钟上收于2590下方\\\",\\n      \\\"confidence\\\": 0.78,\\n      \\\"risk_usd\\\": 400.0,\\n      \\\"justification\\\": \\\"现有多头持仓继续有效。4H: 趋势向上但动能减弱,关注是否形成顶部。3分钟: 价格维持在关键支撑上方,暂时持有观察。\\\"\\n    }\\n  }\\n}\\n```\\n\\n## 决策流程\\n\\n**系统化的分析步骤:**\\n\\n### 第1步: 整体市场评估\\n1. 浏览所有合约的4小时走势\\n2. 统计: 多少看涨、多少看跌、多少中性\\n3. 确定整体市场偏向(牛市/熊市/混合)\\n4. 决定今天的主要交易方向\\n\\n### 第2步: 现有持仓管理\\n对每个有持仓的合约:\\n1. 检查失效条件是否触发\\n2. 评估4小时趋势是否仍然支持\\n3. 检查3分钟动量是否健康\\n4. 决定: 继续持有 或 平仓离场\\n\\n### 第3步: 寻找新机会\\n对每个无持仓的合约:\\n1. 确认是否还有开仓容量(资金和持仓数量)\\n2. 判断该合约的4小时趋势方向\\n3. 如果趋势明确,评估对应方向的入场机会\\n4. 检查3分钟是否给出入场时机\\n5. 计算风险回报比,评估信心度\\n6. 只接受信心度≥0.80的设置\\n\\n### 第4步: 质量控制\\n1. 检查所有决策是否合理一致\\n2. 确认风险分配是否适当\\n3. 验证理由是否清晰完整\\n4. 输出最终JSON\\n\\n## 关键规则\\n\\n1. **持仓状态**: 开新仓前必须确认该合约当前无持仓(quantity = null)\\n2. **时间框架对齐**: 4小时和3分钟必须同时确认才能入场\\n3. **方向一致性**: 保持投资组合方向与整体市场趋势基本一致\\n4. **信号纪律**:\\n   - 有持仓的合约: 只能\\\"hold\\\"或\\\"close\\\"\\n   - 无持仓的合约: 只能\\\"entry\\\"或不操作\\n5. **保守原则**: 有疑问时,不交易\\n6. **风险控制**: 单笔交易风险不超过账户的5%\\n\\n## 禁止行为\\n\\n- ❌ 输出非JSON格式内容\\n- ❌ 在时间框架不一致时入场\\n- ❌ 对有持仓的合约使用\\\"entry\\\"信号\\n- ❌ 对无持仓的合约使用\\\"hold\\\"或\\\"close\\\"信号\\n- ❌ 接受信心度低于0.80的交易\\n- ❌ 使用模糊或不完整的理由\\n- ❌ 忽视4小时的趋势方向\\n\\n## 交易哲学\\n\\n记住:\\n- **质量>数量**: 2-3个高质量交易好过10个平庸交易\\n- **保护资本**: 资本保护永远是第一位的\\n- **耐心等待**: 最好的交易往往是不交易\\n- **相信计划**: 除非明确失效,否则不要过早平仓\\n- **保持灵活**: 市场在变,我们也要适应\\n\\n---\\n\\n**现在请接收市场数据,运用多时间框架分析,输出您的交易决策(仅JSON格式)。**\"}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[96,96],\"id\":\"99af356d-26ce-40ab-8140-e7c9b86123c4\",\"name\":\"AI 智能体\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// 初始化检查\\nif (_G('invoketime') === null) {\\n  _G('invoketime', 0);\\n  _G('STARTTIME', Date.now());\\n  const initAccount = exchange.GetAccount();\\n  _G('initmoney', initAccount.Equity);\\n}\\n\\n// 循环执行\\nconst invoketime = _G('invoketime') + 1;\\n_G('invoketime', invoketime);\\n\\nconst duringtime = Math.floor((Date.now() - _G('STARTTIME')) / 60000); // 转换为分钟\\nconst currentAccount = exchange.GetAccount();\\nconst currentAccountValue = currentAccount.Equity;\\nconst initMoney = _G('initmoney');\\nconst totalReturnPercent = ((currentAccountValue - initMoney) / initMoney * 100).toFixed(2);\\n\\nLogProfit(currentAccountValue - initMoney, \\\"&\\\")\\n\\n// 返回5个数据\\nreturn [{\\n  json: {\\n    invoketime: invoketime,\\n    duringtime: duringtime, // 添加单位\\n    totalReturnPercent: totalReturnPercent + '%',\\n    availableCash: currentAccount.Balance.toFixed(2),\\n    currentAccountValue: currentAccountValue.toFixed(2)\\n  }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-816,48],\"id\":\"af9a7b1c-3a8f-4692-a8e9-edb26098e150\",\"name\":\"参数重置\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"qwen3-max-preview\",\"mode\":\"list\",\"cachedResultName\":\"qwen3-max-preview\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[96,272],\"id\":\"29f09962-568a-4ffe-b4ba-9eea11ffe261\",\"name\":\"OpenAI 模型\",\"credentials\":{\"openAiApi\":{\"id\":\"f3da26db-84a0-4a0b-8e63-6bcc89a187e7\",\"name\":\"ali\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// 解析品种列表\\nconst contracts = $vars.contractList ? ($vars.contractList.includes(',') ? $vars.contractList.split(',') : [$vars.contractList]) : [];\\n\\n\\nif (contracts.length === 0) {\\n  return {};\\n}\\n\\nfunction getMarketDataForContract(symbol) {\\n  exchange.SetContractType(symbol);\\n  \\n  const kline3m = exchange.GetRecords(PERIOD_M3);\\n  const kline4h = exchange.GetRecords(PERIOD_H4);\\n  \\n  if (!kline3m || kline3m.length < 50 || !kline4h || kline4h.length < 50) {\\n    return { error: \\\"K线数据不足\\\" };\\n  }\\n  \\n  const ema20_3m = TA.EMA(kline3m, 20);\\n  const macd_3m = TA.MACD(kline3m, 12, 26, 9);\\n  const rsi7_3m = TA.RSI(kline3m, 7);\\n  const rsi14_3m = TA.RSI(kline3m, 14);\\n  \\n  const ema20_4h = TA.EMA(kline4h, 20);\\n  const ema50_4h = TA.EMA(kline4h, 50);\\n  const macd_4h = TA.MACD(kline4h, 12, 26, 9);\\n  const rsi14_4h = TA.RSI(kline4h, 14);\\n  const atr3_4h = TA.ATR(kline4h, 3);\\n  const atr14_4h = TA.ATR(kline4h, 14);\\n  \\n  const latest3m = kline3m[kline3m.length - 1];\\n  const latest4h = kline4h[kline4h.length - 1];\\n  const recent10_3m = kline3m.slice(-10);\\n  const recent10_4h = kline4h.slice(-10);\\n  \\n  const volumes4h = recent10_4h.map(k => k.Volume);\\n  const avgVolume4h = volumes4h.reduce((a, b) => a + b, 0) / volumes4h.length;\\n  \\n  return {\\n    symbol: symbol,\\n    current_price: latest3m.Close,\\n    current_ema20: ema20_3m[ema20_3m.length - 1],\\n    current_macd: macd_3m[2][macd_3m[2].length - 1],\\n    current_rsi_7: rsi7_3m[rsi7_3m.length - 1],\\n    intraday_3min: {\\n      mid_prices: recent10_3m.map(k => k.Close),\\n      ema_20_series: recent10_3m.map((k, i) => ema20_3m[ema20_3m.length - 10 + i]),\\n      macd_series: recent10_3m.map((k, i) => macd_3m[2][macd_3m[2].length - 10 + i]),\\n      rsi_7_series: recent10_3m.map((k, i) => rsi7_3m[rsi7_3m.length - 10 + i]),\\n      rsi_14_series: recent10_3m.map((k, i) => rsi14_3m[rsi14_3m.length - 10 + i])\\n    },\\n    longer_term_4hour: {\\n      ema_20: ema20_4h[ema20_4h.length - 1],\\n      ema_50: ema50_4h[ema50_4h.length - 1],\\n      atr_3: atr3_4h[atr3_4h.length - 1],\\n      atr_14: atr14_4h[atr14_4h.length - 1],\\n      current_volume: latest4h.Volume,\\n      average_volume: avgVolume4h,\\n      macd_series: recent10_4h.map((k, i) => macd_4h[2][macd_4h[2].length - 10 + i]),\\n      rsi_14_series: recent10_4h.map((k, i) => rsi14_4h[rsi14_4h.length - 10 + i])\\n    }\\n  };\\n}\\n\\nconst allContractsData = {};\\nfor (let i = 0; i < contracts.length; i++) {\\n  const contract = contracts[i]\\n  try {\\n    allContractsData[contract] = getMarketDataForContract(contract);\\n  } catch (e) {\\n    allContractsData[contract] = { error: e.toString() };\\n  }\\n}\\n\\nreturn { data: allContractsData };\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-592,-48],\"id\":\"4a86b3ed-d938-4894-b9df-75a942756747\",\"name\":\"市场数据获取\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"function getTPSLOrderIds(symbol, currentPrice, posType) {\\n    try {\\n        // 获取该品种的未完成订单\\n        exchange.SetContractType(symbol);\\n        \\n        const orders = exchange.GetOrders();\\n        \\n        if (!orders || orders.length === 0) {\\n            return { tpOrderId: -1, slOrderId: -1 };\\n        }\\n        \\n        let tpOrderId = -1;\\n        let slOrderId = -1;\\n        \\n        for (let order of orders) {\\n            // 多头持仓 (Type = 0 或 PD_LONG)\\n            if (posType % 2 == 0) {\\n                // 止盈:卖单价格 > 当前价\\n                if (order.Type === ORDER_TYPE_SELL && order.Price > currentPrice) {\\n                    tpOrderId = order.Id;\\n                }\\n                // 止损:卖单价格 < 当前价\\n                if (order.Type === ORDER_TYPE_SELL && order.Price < currentPrice) {\\n                    slOrderId = order.Id;\\n                }\\n            } \\n            // 空头持仓\\n            else {\\n                // 止盈:买单价格 < 当前价\\n                if (order.Type === ORDER_TYPE_BUY && order.Price < currentPrice) {\\n                    tpOrderId = order.Id;\\n                }\\n                // 止损:买单价格 > 当前价\\n                if (order.Type === ORDER_TYPE_BUY && order.Price > currentPrice) {\\n                    slOrderId = order.Id;\\n                }\\n            }\\n        }\\n        \\n        return { tpOrderId, slOrderId };\\n        \\n    } catch (e) {\\n        Log(`⚠️ 获取 ${symbol} 订单失败: ${e.message}`);\\n        return { tpOrderId: -1, slOrderId: -1 };\\n    }\\n}\\n\\nfunction getAllPositions() {\\n    // 获取当前账户权益\\n    const curequity = exchange.GetAccount().Equity;\\n    \\n    // 获取品种列表\\n    const symbols = $vars.contractList ? ($vars.contractList.includes(',') ? $vars.contractList.split(',') : [$vars.contractList]) : [];\\n    \\n    // 计算每个品种的risk_usd\\n    const risk_usd = symbols.length > 0 ? curequity / symbols.length * 0.05 : 0;\\n    \\n    // 获取所有实际持仓\\n    const rawPositions = exchange.GetPositions();\\n    \\n    // 创建持仓映射表 (品种符号 -> 持仓对象)\\n    const positionMap = {};\\n    \\n    if (rawPositions && rawPositions.length > 0) {\\n        for (let pos of rawPositions) {\\n            if (pos.Amount && Math.abs(pos.Amount) > 0) {\\n                positionMap[pos.Symbol] = pos;\\n            }\\n        }\\n    }\\n    \\n    // 为每个品种创建position信息\\n    const allPositions = [];\\n    \\n    for (let i = 0; i < symbols.length; i++) {\\n        const contract = symbols[i].trim();\\n        const pos = positionMap[contract];\\n        \\n        if (pos) {\\n            // 有持仓的情况\\n            try {\\n                // 切换到对应交易对获取ticker\\n                exchange.SetContractType(pos.Symbol);\\n                \\n                const ticker = exchange.GetTicker();\\n                const currentPrice = ticker ? ticker.Last : pos.Price;\\n                \\n                // 获取止盈止损订单ID\\n                const { tpOrderId, slOrderId } = getTPSLOrderIds(pos.Symbol, currentPrice, pos.Type);\\n                \\n                // 获取退出计划\\n                const exitPlan = _G(`exit_plan_${pos.Symbol}`) || {\\n                    profit_target: null,\\n                    stop_loss: null,\\n                    invalidation_condition: \\\"\\\"\\n                };\\n                \\n                allPositions.push({\\n                    symbol: contract,\\n                    quantity: Math.abs(pos.Amount),\\n                    entry_price: pos.Price,\\n                    current_price: currentPrice,\\n                    unrealized_pnl: _N(pos.Profit, 2),\\n                    exit_plan: exitPlan,\\n                    confidence: exitPlan?.confidence || null,\\n                    risk_usd: risk_usd,  // 使用计算出的risk_usd\\n                    sl_oid: slOrderId,      \\n                    tp_oid: tpOrderId,      \\n                    wait_for_fill: false,\\n                    entry_oid: pos.Info?.posId || -1,\\n                    notional_usd: _N(Math.abs(pos.Amount) * currentPrice, 2)\\n                });\\n            } catch (e) {\\n                Log(`⚠️ 处理 ${contract} 持仓信息失败: ${e.message}`);\\n                // 出错时也添加一个空持仓记录\\n                allPositions.push({\\n                    symbol: contract,\\n                    quantity: null,\\n                    entry_price: null,\\n                    current_price: null,\\n                    unrealized_pnl: null,\\n                    exit_plan: null,\\n                    confidence: null,\\n                    risk_usd: risk_usd,\\n                    sl_oid: null,\\n                    tp_oid: null,\\n                    wait_for_fill: false,\\n                    entry_oid: null,\\n                    notional_usd: null\\n                });\\n            }\\n        } else {\\n            // 没有持仓的情况 - 返回固定字段为null\\n            allPositions.push({\\n                symbol: contract,\\n                quantity: null,\\n                entry_price: null,\\n                current_price: null,\\n                unrealized_pnl: null,\\n                exit_plan: null,\\n                confidence: null,\\n                risk_usd: risk_usd,  // 仍然返回risk_usd\\n                sl_oid: null,\\n                tp_oid: null,\\n                wait_for_fill: false,\\n                entry_oid: null,\\n                notional_usd: null\\n            });\\n        }\\n    }\\n    \\n    return allPositions;\\n}\\n\\nconst positions = getAllPositions();\\nreturn {positions};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-592,192],\"id\":\"7d76bbba-09f9-4fbf-b87c-6e526f40207d\",\"name\":\"持仓数据获取\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// 获取输入数据\\nconst inputData = $input.all();\\n\\n// 第一个输入是市场数据,第二个是持仓数据\\nconst marketData = inputData[0].json.data;\\nconst positions = inputData[1].json;\\n\\n// 返回整理后的数据\\nreturn [{\\n  json: {\\n    marketData: marketData,\\n    positions: positions\\n  }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-112,96],\"id\":\"17317886-4e44-4532-bf0b-4dd742dc9539\",\"name\":\"数据合并\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== 工具函数 ==========\\n\\nfunction 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// ========== 数量计算函数 ==========\\nfunction calculateQuantity(entryPrice, stopLoss, riskUsd) {\\n    const riskPerUnit = Math.abs(entryPrice - stopLoss) * 10;\\n    if (riskPerUnit <= 0) return 0;\\n    const quantity = riskUsd / riskPerUnit;\\n    return quantity;\\n}\\n\\nfunction validateEntry(contract, currentPrice, profitTarget, stopLoss) {\\n    const isLong = profitTarget > stopLoss;\\n    if (isLong && (profitTarget <= currentPrice || stopLoss >= currentPrice)) return false;\\n    if (!isLong && (profitTarget >= currentPrice || stopLoss <= currentPrice)) return false;\\n    return true;\\n}\\n\\nfunction hasPosition(contract) {\\n    try {\\n        exchange.SetContractType(contract);\\n        const positions = exchange.GetPositions();\\n        return positions.some(p => p.Symbol.includes(contract) && Math.abs(p.Amount) > 0);\\n    } catch (e) {\\n        return false;\\n    }\\n}\\n\\nfunction saveExitPlan(symbol, args) {\\n    _G(`exit_plan_${symbol}`, {\\n        profit_target: args.profit_target,\\n        stop_loss: args.stop_loss,\\n        invalidation_condition: args.invalidation_condition || \\\"\\\",\\n        confidence: args.confidence,\\n        risk_usd: args.risk_usd\\n    });\\n}\\n\\n// ========== 上期所平仓规则判断 ==========\\n\\nfunction isSHFE(exchangeId) {\\n    return exchangeId === \\\"SHFE\\\";\\n}\\n\\nfunction closePositionSHFE(contract, pos, isLong, closeAmount) {\\n    const info = pos.Info;\\n    const exchangeId = info?.ExchangeID || \\\"\\\";\\n    \\n    if (!isSHFE(exchangeId)) {\\n        // 非上期所,普通平仓\\n        exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n        const orderId = isLong ? exchange.Sell(-1, closeAmount) : exchange.Buy(-1, closeAmount);\\n        return orderId;\\n    }\\n    \\n    // 上期所需要区分平今平昨\\n    const todayPosition = info.TodayPosition || 0;\\n    const ydPosition = info.YdPosition || 0;\\n    \\n    let remainingClose = closeAmount;\\n    const orderIds = [];\\n    \\n    // 优先平昨仓\\n    if (ydPosition > 0 && remainingClose > 0) {\\n        const closeYd = Math.min(ydPosition, remainingClose);\\n        exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n        const orderId = isLong ? exchange.Sell(-1, closeYd) : exchange.Buy(-1, closeYd);\\n        if (orderId) {\\n            orderIds.push(orderId);\\n            remainingClose -= closeYd;\\n        }\\n        Sleep(300);\\n    }\\n    \\n    // 再平今仓\\n    if (todayPosition > 0 && remainingClose > 0) {\\n        const closeToday = Math.min(todayPosition, remainingClose);\\n        exchange.SetDirection(isLong ? \\\"closebuy_today\\\" : \\\"closesell_today\\\");\\n        const orderId = isLong ? exchange.Sell(-1, closeToday) : exchange.Buy(-1, closeToday);\\n        if (orderId) {\\n            orderIds.push(orderId);\\n            remainingClose -= closeToday;\\n        }\\n    }\\n    \\n    return orderIds.length > 0 ? orderIds[0] : null;\\n}\\n\\n// ========== 交易执行函数 ==========\\n\\nfunction executeClose(contract) {\\n    exchange.SetContractType(contract);\\n    \\n    const orders = exchange.GetOrders();\\n    orders?.forEach(o => exchange.CancelOrder(o.Id));\\n    \\n    const pos = exchange.GetPositions().find(p => p.Symbol.includes(contract) && Math.abs(p.Amount) > 0);\\n    if (!pos) return;\\n    \\n    const isLong = pos.Amount > 0;\\n    const closeAmount = Math.abs(pos.Amount);\\n    \\n    const orderId = closePositionSHFE(contract, pos, isLong, closeAmount);\\n    \\n    if (orderId) {\\n        Log(`✅ ${contract}: 平${isLong ? '多' : '空'}成功,数量=${closeAmount}`);\\n        _G(`exit_plan_${contract}`, null);\\n    }\\n}\\n\\nfunction executeEntry(contract, args) {\\n    exchange.SetContractType(contract);\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker){\\n        Log('获取不到ticker')\\n        return\\n    };\\n    \\n    const currentPrice = ticker.Last;\\n    if (!validateEntry(contract, currentPrice, args.profit_target, args.stop_loss)) {\\n        Log('定义开仓失效')\\n        return\\n    }\\n    \\n    const quantity = calculateQuantity(currentPrice, args.stop_loss, args.risk_usd);\\n    if (quantity <= 0) {\\n        Log(`⚠️ ${contract}: 计算数量无效,跳过开仓`);\\n        return;\\n    }\\n    \\n    const isLong = args.profit_target > args.stop_loss;\\n    exchange.SetDirection(isLong ? \\\"buy\\\" : \\\"sell\\\");\\n    \\n    const orderId = isLong ? exchange.Buy(-1, quantity) : exchange.Sell(-1, quantity);\\n    \\n    if (orderId) {\\n        Sleep(1000);\\n        Log(`✅ ${contract}: 开${isLong ? '多' : '空'} 成功 数量=${quantity}`);\\n    } else {\\n        Log(`❌ ${contract}: 开仓失败`);\\n    }\\n}\\n\\n// ========== 止盈止损监控 ==========\\n\\nfunction monitorPosition(contract) {\\n    exchange.SetContractType(contract);\\n    \\n    const pos = exchange.GetPositions().find(p => p.Symbol.includes(contract) && Math.abs(p.Amount) > 0);\\n    if (!pos) return;\\n    \\n    const ticker = exchange.GetTicker();\\n    if (!ticker) return;\\n    \\n    const isLong = pos.Amount > 0;\\n    const currentPrice = ticker.Last;\\n    const pnl = (currentPrice - pos.Price) * (isLong ? 1 : -1) / pos.Price;\\n    \\n    const exitPlan = _G(`exit_plan_${contract}`);\\n    if (!exitPlan?.profit_target || !exitPlan?.stop_loss) {\\n        if (pnl >= 0.03) return closePosition(contract, pos, isLong, \\\"止盈\\\", pnl);\\n        if (pnl <= -0.01) return closePosition(contract, pos, isLong, \\\"止损\\\", pnl);\\n        return;\\n    }\\n    \\n    const shouldTP = isLong ? currentPrice >= exitPlan.profit_target : currentPrice <= exitPlan.profit_target;\\n    const shouldSL = isLong ? currentPrice <= exitPlan.stop_loss : currentPrice >= exitPlan.stop_loss;\\n    \\n    if (shouldTP) return closePosition(contract, pos, isLong, \\\"止盈\\\", pnl);\\n    if (shouldSL) return closePosition(contract, pos, isLong, \\\"止损\\\", pnl);\\n}\\n\\nfunction closePosition(contract, pos, isLong, reason, pnl) {\\n    const closeAmount = Math.abs(pos.Amount);\\n    \\n    closePositionSHFE(contract, pos, isLong, closeAmount);\\n    \\n    Log(`${reason === '止盈' ? '✅' : '❌'} ${contract} ${reason} ${(pnl * 100).toFixed(2)}%`);\\n    _G(`exit_plan_${contract}`, null);\\n}\\n\\n// ========== 主逻辑 ==========\\n\\nconst signals = parseAIOutput($input.first().json.output);\\nLog('信号:', signals);\\n\\n// AI信号表格\\nconst signalTable = {\\n    type: \\\"table\\\",\\n    title: \\\"📊 AI交易信号分析\\\",\\n    cols: [\\\"品种\\\", \\\"信号\\\", \\\"止盈目标\\\", \\\"止损价位\\\", \\\"风险金额\\\", \\\"置信度\\\", \\\"信号条件\\\"],\\n    rows: []\\n};\\n\\nfor (const [contract, data] of Object.entries(signals)) {\\n    const args = data?.trade_signal_args;\\n    if (!args?.contract || !args?.signal) continue;\\n    \\n    const hasPos = hasPosition(contract);\\n    let displaySignal = args.signal;\\n    let signalEmoji = \\\"⏸️\\\";\\n    \\n    if ((args.signal === \\\"hold\\\" && !hasPos) || (args.signal === \\\"close\\\" && !hasPos)) {\\n        displaySignal = \\\"无操作\\\";\\n        signalEmoji = \\\"⚪\\\";\\n    } else if (args.signal === \\\"entry\\\") {\\n        const isLong = args.profit_target > args.stop_loss;\\n        displaySignal = isLong ? \\\"做多入场\\\" : \\\"做空入场\\\";\\n        signalEmoji = isLong ? \\\"🟢\\\" : \\\"🔴\\\";\\n    } else if (args.signal === \\\"close\\\") {\\n        displaySignal = \\\"平仓\\\";\\n        signalEmoji = \\\"⏹️\\\";\\n    } else if (args.signal === \\\"hold\\\") {\\n        displaySignal = \\\"持仓观望\\\";\\n        signalEmoji = \\\"⏸️\\\";\\n    }\\n    \\n    const confidence = args.confidence ? (args.confidence * 100).toFixed(0) : 0;\\n    let confidenceDisplay = \\\"-\\\";\\n    if (confidence >= 70) confidenceDisplay = `🔥 ${confidence}%`;\\n    else if (confidence >= 50) confidenceDisplay = `⚡ ${confidence}%`;\\n    else if (confidence > 0) confidenceDisplay = `⚠️ ${confidence}%`;\\n    \\n    signalTable.rows.push([\\n        `💎 ${contract}`,\\n        `${signalEmoji} ${displaySignal}`,\\n        args.profit_target ? `$${args.profit_target}` : \\\"-\\\",\\n        args.stop_loss ? `$${args.stop_loss}` : \\\"-\\\",\\n        args.risk_usd ? `💰 $${args.risk_usd}` : \\\"-\\\",\\n        confidenceDisplay,\\n        args.justification\\n    ]);\\n    \\n    if (args.signal === \\\"hold\\\" && !hasPos) continue;\\n    if (args.signal === \\\"close\\\" && !hasPos) continue;\\n    if (args.signal === \\\"entry\\\" && hasPos) continue;\\n    \\n    if (args.signal !== \\\"close\\\") saveExitPlan(contract, args);\\n    \\n    if (args.signal === \\\"close\\\") executeClose(contract);\\n    else if (args.signal === \\\"hold\\\") Log(`⏸️ ${contract}: 持仓`);\\n    else if (args.signal === \\\"entry\\\") {\\n        if (!args.profit_target || !args.stop_loss || !args.risk_usd) continue;\\n        executeEntry(contract, args);\\n    }\\n}\\n\\n// 监控止盈止损并生成持仓表\\nconst positionTable = {\\n    type: \\\"table\\\",\\n    title: \\\"💼 当前持仓监控\\\",\\n    cols: [\\\"品种\\\", \\\"方向\\\", \\\"持仓量\\\", \\\"入场价\\\", \\\"当前价\\\", \\\"盈亏\\\", \\\"止盈目标\\\", \\\"止损价位\\\"],\\n    rows: []\\n};\\n\\nconst positions = exchange.GetPositions();\\nlet totalPnl = 0, positionCount = 0;\\n\\npositions?.forEach(pos => {\\n    if (Math.abs(pos.Amount) > 0) {\\n        const contract = pos.Symbol;\\n        exchange.SetContractType(contract);\\n        const ticker = exchange.GetTicker();\\n        \\n        if (ticker) {\\n            const isLong = pos.Amount > 0;\\n            const currentPrice = ticker.Last;\\n            const pnlPercent = ((currentPrice - pos.Price) * (isLong ? 1 : -1) / pos.Price * 100);\\n            \\n            let pnlDisplay = pnlPercent > 0 ? `🟢 +${pnlPercent.toFixed(2)}%` :\\n                             pnlPercent < 0 ? `🔴 ${pnlPercent.toFixed(2)}%` :\\n                             `⚪ ${pnlPercent.toFixed(2)}%`;\\n            \\n            const exitPlan = _G(`exit_plan_${contract}`);\\n            positionTable.rows.push([\\n                `💎 ${contract}`,\\n                isLong ? \\\"📈 多\\\" : \\\"📉 空\\\",\\n                pos.Amount.toFixed(2),\\n                `$${pos.Price}`,\\n                `$${currentPrice}`,\\n                pnlDisplay,\\n                exitPlan?.profit_target ? `🎯 $${exitPlan.profit_target}` : \\\"-\\\",\\n                exitPlan?.stop_loss ? `🛡️ $${exitPlan.stop_loss}` : \\\"-\\\"\\n            ]);\\n            \\n            totalPnl += pnlPercent;\\n            positionCount++;\\n        }\\n        monitorPosition(contract);\\n        Sleep(500);\\n    }\\n});\\n\\nif (positionCount > 0) {\\n    const avgPnl = totalPnl / positionCount;\\n    const summaryEmoji = avgPnl > 0 ? \\\"📊 ✅\\\" : \\\"📊 ⚠️\\\";\\n    positionTable.rows.push([\\n        `${summaryEmoji} 汇总`,\\n        `${positionCount} 个持仓`,\\n        \\\"-\\\",\\\"-\\\",\\\"-\\\",\\n        `平均: ${avgPnl > 0 ? '🟢' : '🔴'} ${avgPnl.toFixed(2)}%`,\\n        \\\"-\\\",\\\"-\\\"\\n    ]);\\n}\\n\\nLogStatus('`' + JSON.stringify(signalTable) + '`\\\\n\\\\n' + '`' + JSON.stringify(positionTable) + '`');\\n\\nreturn { json: { processed: true } };\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[448,96],\"id\":\"9b8e0f35-6686-4484-a629-01e03c485bd7\",\"name\":\"交易执行\"}],\"pinData\":{},\"connections\":{\"定时触发器\":{\"main\":[[{\"node\":\"参数重置\",\"type\":\"main\",\"index\":0}]]},\"合并\":{\"main\":[[{\"node\":\"数据合并\",\"type\":\"main\",\"index\":0}]]},\"AI 智能体\":{\"main\":[[{\"node\":\"交易执行\",\"type\":\"main\",\"index\":0}]]},\"参数重置\":{\"main\":[[{\"node\":\"市场数据获取\",\"type\":\"main\",\"index\":0},{\"node\":\"持仓数据获取\",\"type\":\"main\",\"index\":0}]]},\"OpenAI 模型\":{\"ai_languageModel\":[[{\"node\":\"AI 智能体\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"市场数据获取\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":0}]]},\"持仓数据获取\":{\"main\":[[{\"node\":\"合并\",\"type\":\"main\",\"index\":1}]]},\"数据合并\":{\"main\":[[{\"node\":\"AI 智能体\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"credentials\":{},\"id\":\"9d14748b-e818-41c3-835a-48aaf12a9a2d\",\"plugins\":{},\"mcpClients\":{}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"定时触发器\"}}"}