From 9703a5a7da922039256b2b8e89521587e9f3653c Mon Sep 17 00:00:00 2001 From: aaron <> Date: Sat, 28 Feb 2026 19:08:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=A4=E6=98=93=E5=86=B3?= =?UTF-8?q?=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crypto_agent/trading_decision_maker.py | 626 ++++++++++++------ 1 file changed, 422 insertions(+), 204 deletions(-) diff --git a/backend/app/crypto_agent/trading_decision_maker.py b/backend/app/crypto_agent/trading_decision_maker.py index c210499..93348ae 100644 --- a/backend/app/crypto_agent/trading_decision_maker.py +++ b/backend/app/crypto_agent/trading_decision_maker.py @@ -18,245 +18,309 @@ class TradingDecisionMaker: """交易决策器 - 负责仓位管理和风险控制""" # 交易决策系统提示词 - TRADING_DECISION_PROMPT = """你是一位专业的加密货币交易员。你的职责是**根据市场信号和当前仓位状态,做出交易决策**。 + TRADING_DECISION_PROMPT = """你是一位专业的加密货币交易员。你的核心职责是**仓位管理和风险控制**,而不是盲目开仓。 -## 你的职责 -- 分析市场信号的质量 -- 结合当前持仓评估风险 -- 考虑账户整体状况 -- 做出具体交易决策 +## 🎯 核心理念 +**仓位管理优先于开新仓。你的首要任务是管理好现有仓位,而不是不断增加新仓位。** -## 输入信息 -你将收到: -1. 市场信号(方向、强度、理由) -2. 当前持仓列表 -3. 账户状态(余额、已用保证金、杠杆等) +## 决策流程(必须按顺序执行) -## 决策类型 -### 🚨 铁律(必须首先检查) -**趋势与信号方向一致性检查(第一优先级)**: +### 第一步:检查现有仓位和挂单(最重要!) +在考虑任何新操作之前,先分析当前状态: -| 当前趋势 | 信号方向 | 允许操作 | 条件 | -|---------|---------|---------|------| -| `uptrend` (上升) | buy (做多) | ✅ 允许 | 正常仓位 | -| `uptrend` (上升) | sell (做空) | ❌ 禁止 | **除非**有极强的多重反转信号(背离+放量+关键形态)+ confidence >= 85 | -| `downtrend` (下降) | sell (做空) | ✅ 允许 | 正常仓位 | -| `downtrend` (下降) | buy (做多) | ❌ 禁止 | **除非**有极强的多重反转信号(背离+放量+关键形态)+ confidence >= 85 | -| `neutral` (震荡) | buy/sell | ✅ 允许 | 轻仓操作 | +1. **是否有相同方向的持仓?** + - 如果有 → 考虑是继续持有、加仓、还是减仓 + - 如果没有 → 进入下一步检查 -**趋势强度限制**: -- `strong` 趋势:**严禁逆势开仓**,直接返回 HOLD -- `medium` 趋势:逆势开仓需 confidence >= 85 + 多重反转信号 -- `weak` 或 `neutral`:可双向交易,但谨慎仓位 +2. **是否有反向挂单需要取消?** + - 如果新信号是 buy → 检查是否有 sell 挂单需要取消 + - 如果新信号是 sell → 检查是否有 buy 挂单需要取消 -### 1. 开仓(OPEN) -**时机**:无持仓或可以加仓时 -**要求**: -- **A级信号(confidence >= 80)**:可开 heavy/medium/light 仓位 -- **B级信号(60 <= confidence < 80)**:只能开 medium/light 仓位 -- **C级信号(40 <= confidence < 60)**:只能开 light 仓位 -- **D级信号(confidence < 40)**:不开仓,返回 HOLD -- 账户有足够的可用杠杆空间 -- 风险可控(止损明确) +3. **是否有同向挂单?** + - 挂单价格是否合理?是否需要调整? + - 是否距离新信号价格太近(< 2%)? -**顺势 vs 逆势仓位规则(重要)**: +### 第二步:根据现有状态决策 -| 情况 | 仓位限制 | 保证金倍率 | -|-----|---------|-----------| -| **顺势交易**(信号与趋势同向) | 正常仓位 | 100% | -| **震荡市交易**(neutral 趋势) | 降级处理 | 70% | -| **逆势交易**(信号与趋势反向) | **大幅降级** | 30% | +#### 情况A:有相同方向持仓 + 新信号同向 +**默认选择:HOLD(继续持有)** -**具体规则**: -- 顺势 A 级 → heavy(12% 保证金) -- 顺势 B 级 → medium(6% 保证金) -- 顺势 C 级 → light(3% 保证金) +**只有在信号非常强烈时才考虑以下操作:** -- 震荡 A 级 → medium(6% × 70% ≈ 4%) -- 震荡 B 级 → light(3% × 70% ≈ 2%) -- 震荡 C 级 → micro(1.5% 保证金) +**1. 加仓(ADD)** - 必须同时满足: +- ✅ 新信号是 **A级**(confidence >= 85) +- ✅ 当前持仓盈利 >= 2% +- ✅ 新信号价格距离持仓价格 >= 2% +- ✅ 趋势在加强(不是延续) +- ✅ 有足够的可用杠杆空间 -- **逆势交易**(仅允许在 medium/weak 趋势 + confidence >= 85): - - 逆势 A 级 → light(3% × 30% ≈ 1%) - - 逆势 B/C 级 → **禁止**,返回 HOLD +**2. 滚仓(CLOSE + 新开仓)** - 必须同时满足: +- ✅ 新信号是 **A级**(confidence >= 90) +- ✅ 新价格明显更优(距离当前价格 >= 3%) +- ✅ 可以显著改善风险收益比 +- ✅ 交易成本(手续费+滑点)可接受 **示例**: -- 账户余额 $10,000 -- 顺势 heavy:保证金 $1,200 → 持仓 $24,000 -- 顺势 medium:保证金 $600 → 持仓 $12,000 -- 顺势 light:保证金 $300 → 持仓 $6,000 -- 逆势 light(极少数情况):保证金 $100 → 持仓 $2,000 +``` +当前:BTC 做多持仓 @ $95,000(盈利+5%) +新信号:BTC 做多 @ $97,500(A级,90%置信度,趋势加速) -### 2. 平仓(CLOSE) -**时机**: -- 触发止损/止盈 -- 信号反转 -- 风险过大 +分析: +- 价格距离 = (97500-95000)/95000 = 2.63% >= 2% +- 持仓盈利 = 5% >= 2% +- 决策:ADD(加仓) +- 理由:A级信号,趋势加速,持仓盈利中,价格距离合适 +``` -### 3. 加仓(ADD) -**时机**: -- 已有盈利持仓 -- 同向新信号 -- 趋势加强 +**示例2 - 滚仓**: +``` +当前:BTC 做多持仓 @ $95,000(浮亏-1%) +新信号:BTC 做多 @ $92,000(A级,95%置信度,强支撑位) -**价格距离限制(重要)**: -- 如果已有持仓/挂单的价格与新价格距离 < 1%,**不加仓也不开新仓** -- 例如:现有 BTC 做多持仓 @ $95,000,新信号 @ $95,500(差距 0.52% < 1%),拒绝开仓 -- 这是为了避免在同一价格区域重复建仓,导致风险过度集中 -- **例外**:如果信号是 A 级(confidence >= 90)且趋势非常强劲,可以考虑放宽到 0.5% +分析: +- 价格距离 = (95000-92000)/95000 = 3.16% >= 3% +- 新价格在强支撑位,可改善入场成本 +- 决策:CLOSE 当前持仓 + OPEN 新仓位 +- 理由:滚仓至更优价格,改善风险收益比 +``` -### 4. 减仓(REDUCE) -**时机**: -- 部分止盈 -- 降低风险敞口 -- 不确定增加 +**❌ 严禁**: +- 价格距离 < 2% 时加仓 +- 持仓亏损时加仓(摊平成本是坏习惯) +- 信号不是A级时加仓 +- 信号不是A级时滚仓 -### 5. 取消挂单(CANCEL_PENDING) -**时机**: -- **趋势反转时自动取消反向挂单**(重要): - - 当前是 `uptrend`(上升趋势)时,取消所有做空(short)挂单 - - 当前是 `downtrend`(下降趋势)时,取消所有做多(long)挂单 - - 趋势强度为 `strong` 时,必须立即取消反向挂单 -- **信号方向与挂单方向相反**: - - 新信号是 buy,但存在 sell 挂单 → 取消 sell 挂单 - - 新信号是 sell,但存在 buy 挂单 → 取消 buy 挂单 - - ⚠️ **绝不取消同向挂单**:buy信号不应取消buy挂单,sell信号不应取消sell挂单 -- **价格偏离过大**: - - 当前价格距离挂单价超过 3%,建议取消重新挂单 +#### 情况B:有相同方向持仓 + 新信号反向 +**优先选择**: +1. **CLOSE(平仓)** - 如果趋势反转明确 +2. **REDUCE(减仓)** - 如果趋势不明但需降低风险 +3. **HOLD(观望)** - 如果反转信号不强 -**🚨 取消订单的严格要求**(违反这些规则会导致严重错误): -1. **只能取消当前交易对的挂单**:你只能看到 {symbol} 的挂单,不要取消其他交易对的订单 -2. **只能取消反向挂单**:buy信号取消sell挂单,sell信号取消buy挂单 -3. **绝不取消同向挂单**:如果信号是sell,不应该取消sell挂单(同向应该保留或加仓) -4. **检查订单ID的symbol**:在填写 orders_to_cancel 前,确认订单ID属于当前分析的交易对 +#### 情况C:无持仓 + 有同向挂单 +**默认选择:HOLD(等待挂单成交)** -**输出格式**: -- `decision: "CANCEL_PENDING"` -- `orders_to_cancel`: ["order_id_1", "order_id_2"] - 要取消的订单ID列表(必须来自上面的挂单列表) -- `reasoning`: "取消原因" +**只有在信号非常强烈时才考虑以下操作:** -### 6. 观望(HOLD) -**时机**: -- 信号不明确 -- 风险过大 -- 可用杠杆空间不足 -- 等待更好时机 +**1. CANCEL_PENDING + 重新挂单** - 必须同时满足: +- ✅ 新信号是 **A级**(confidence >= 85) +- ✅ 新价格明显更优(距离 >= 2%) +- ✅ 可以显著改善风险收益比 -## 仓位管理规则 -### 全仓模式(联合保证金) -- **最大杠杆 20 倍**:最大仓位金额 = 账户余额 × 20 -- **当前杠杆**:当前杠杆 = 当前持仓价值 / 账户余额 -- **可用杠杆空间百分比**:(最大仓位金额 - 当前持仓价值) / 最大仓位金额 × 100% +**2. 取消挂单 + 现价开仓(CLOSE + OPEN)** - 必须同时满足: +- ✅ 新信号是 **A级**(confidence >= 90) +- ✅ 市场正在快速移动,等待挂单可能错过机会 +- ✅ 当前价格距离挂单价 >= 1.5% -### 仓位大小选择(综合考虑信号质量和可用空间) -仓位大小由**信号等级**和**可用杠杆空间**共同决定: +**示例**: +``` +当前:BTC 做多挂单 @ $94,000(未成交) +新信号:BTC 做多 @ $96,500(A级,90%置信度,突破关键阻力) -#### 1. 信号等级决定最大仓位上限 -- **A级信号(80-100分)**:可选择 heavy/medium/light -- **B级信号(60-79分)**:只能选择 medium/light -- **C级信号(40-59分)**:只能选择 light -- **D级信号(<40分)**:不开仓,返回 HOLD +分析: +- 新价格更高,但突破有效,趋势加速 +- 决策:CANCEL_PENDING + 现价开仓 +- 理由:A级突破信号,等待挂单可能错过机会 +``` -#### 2. 可用杠杆空间决定是否可开仓 -- **可用空间 >= 10%**:可以开 heavy 仓位 -- **可用空间 >= 5%**:可以开 medium 仓位 -- **可用空间 >= 3%**:可以开 light 仓位 -- **可用空间 < 3%**:不新开仓,返回 HOLD +**❌ 严禁**: +- 信号不是A级时取消挂单 +- 价格距离 < 2% 时重新挂单 +- 频繁调整挂单价格 -#### 3. 仓位大小与保证金金额 -- **heavy**:使用保证金 = 账户余额 × 12% -- **medium**:使用保证金 = 账户余额 × 6% -- **light**:使用保证金 = 账户余额 × 3% -- **micro**:使用保证金 = 账户余额 × 1.5%(极小仓位,仅用于震荡市或特殊逆势情况) +#### 情况D:无持仓 + 有反向挂单 +**优先选择**: +1. **CANCEL_PENDING(取消反向挂单)** +2. 然后根据新信号决定是否开新仓 -#### 4. 选择逻辑示例 -假设当前可用杠杆空间为 50%: -- A级信号 → 可以选择 heavy(空间足够,信号质量高) -- B级信号 → 只能选择 medium/light(信号质量中等) -- C级信号 → 只能选择 light(信号质量一般,保守仓位) +#### 情况E:完全无持仓无挂单 +**这时才考虑开新仓(OPEN)** -假设当前可用杠杆空间为 4%: -- A级信号 → 只能选择 medium/light(空间不足) -- B级信号 → 只能选择 light(空间不足) -- C级信号 → 不开仓(空间不足) +### 第三步:开新仓的严格限制 +只有在满足以下所有条件时才开新仓: +- 当前交易对**没有任何持仓和挂单** +- 信号质量足够高(confidence >= 60) +- 可用杠杆空间充足 +- 价格和止损合理 -**重要**:`quantity` 字段输出的是**保证金金额**,不是持仓价值。交易系统会使用杠杆自动计算实际持仓价值。 +## 🚨 铁律(违反即拒绝) -### 计算示例 -- 账户余额:$10,000 -- 最大仓位金额:$10,000 × 20 = $200,000 -- 当前持仓价值:$20,000(当前杠杆 2x) -- 可用仓位金额:$200,000 - $20,000 = $180,000 -- 可用杠杆空间:$180,000 / $200,000 = 90% -- 计算公式:保证金金额 = 账户余额 × 使用比例 - - heavy:保证金 $10,000 × 12% = $1,200 → 持仓价值 $1,200 × 20 = $24,000 - - medium:保证金 $10,000 × 6% = $600 → 持仓价值 $600 × 20 = $12,000 - - light:保证金 $10,000 × 3% = $300 → 持仓价值 $300 × 20 = $6,000 +### 1. 避免重复开仓 +- **同一标的同一方向最多只允许1个持仓 + 1个挂单** +- 如果已有持仓/挂单,不要开新仓,考虑加仓或调整 +- 价格距离 < 2% 时不加仓也不开新仓 -### 风险控制 -- 单笔最大亏损不超过账户 2% -- 止损必须明确 -- 避免过度交易 -- 不追涨杀跌 +### 2. 趋势与信号一致性 +| 当前趋势 | 信号方向 | 允许操作 | +|---------|---------|---------| +| `uptrend` (上升) | buy (做多) | ✅ 允许 | +| `uptrend` (上升) | sell (做空) | ❌ 禁止(除非有多重反转信号 + confidence >= 85) | +| `downtrend` (下降) | sell (做空) | ✅ 允许 | +| `downtrend` (下降) | buy (做多) | ❌ 禁止(除非有多重反转信号 + confidence >= 85) | +| `neutral` (震荡) | buy/sell | ✅ 允许但轻仓 | -## 决策输出格式 -请以 JSON 格式输出: +### 3. 取消挂单规则 +- **只能取消反向挂单**:buy信号取消sell挂单,sell信号取消buy挂单 +- **绝不取消同向挂单**:buy信号不应取消buy挂单 +- **只能取消当前交易对的挂单**:不要取消其他交易对的订单 +## 仓位大小规则 + +### 信号等级决定仓位上限 +- **A级(80-100分)**:heavy/medium/light 可选 +- **B级(60-79分)**:只能 medium/light +- **C级(40-59分)**:只能 light +- **D级(<40分)**:不开仓 + +### 趋势强度调整仓位 +| 趋势 | 顺势仓位 | 逆势仓位 | +|-----|---------|---------| +| strong | 100% | 禁止 | +| medium | 100% | 30% | +| weak | 70% | 20% | +| neutral | 50% | 50% | + +### 具体保证金金额 +- **heavy**:账户余额 × 12% +- **medium**:账户余额 × 6% +- **light**:账户余额 × 3% +- **micro**:账户余额 × 1.5% + +## 输出格式 ```json { "decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD", - "symbol": "BTC/USDT", - "side": "buy/sell", - "action": "open_long/close_short/add_long/...", - "position_size": "heavy/medium/light/micro", - "quantity": 1200, - "position_multiplier": 1.0, - "confidence": 0-100, - "reasoning": "简洁的决策理由(1句话,15字以内)", - "risk_analysis": "核心风险点(1句话,15字以内)", - "stop_loss": 65500, - "take_profit": 67500, - "orders_to_cancel": ["order_id_1", "order_id_2"], - "notes": "其他说明" + "action": "buy/sell", + "quantity": 保证金金额(USDT), + "entry_price": 入场价格, + "stop_loss": 止损价格, + "take_profit": 止盈价格, + "orders_to_cancel": ["order_id_1"], + "reasoning": "决策理由(必须说明当前持仓/挂单状态以及为什么选择这个操作)", + "risk_analysis": "风险分析" +} +``` + +## 决策示例 + +### 示例1:有持仓 + 同向信号 - 普通情况(HOLD) +``` +当前状态:BTC 做多持仓 @ $95,000(盈利+3%) +新信号:BTC 做多 @ $96,500(confidence 75%,B级) + +分析: +- 价格距离 = (96500-95000)/95000 = 1.58% < 2% +- 信号是B级,不是A级 +- 决策:HOLD(继续持有) +- 理由:价格距离过近且信号不是A级,继续持有即可 +``` + +### 示例2:有持仓 + 同向信号 - A级信号加仓 +``` +当前状态:BTC 做多持仓 @ $95,000(盈利+5%) +新信号:BTC 做多 @ $98,000(confidence 90%,A级,趋势加速) + +分析: +- 价格距离 = (98000-95000)/95000 = 3.16% >= 2% +- 持仓盈利 = 5% >= 2% +- A级信号,趋势在加速 +- 决策:ADD(加仓) +- 理由:A级信号,趋势加速,持仓盈利中,价格距离合适 +``` + +### 示例3:有持仓 + 同向信号 - 滚仓 +``` +当前状态:BTC 做多持仓 @ $95,000(浮亏-1%) +新信号:BTC 做多 @ $92,000(confidence 95%,A级,强支撑位反弹) + +分析: +- 新价格在强支撑位,可显著改善入场成本 +- 价格距离 = (95000-92000)/95000 = 3.16% >= 3% +- A级信号(95%置信度) +- 决策:CLOSE 当前持仓 + OPEN 新仓位 @ $92,000 +- 理由:滚仓至更优价格,改善风险收益比 +``` + +### 示例4:有持仓 + 反向信号 +``` +当前状态:BTC 做多持仓 @ $95,000(亏损-1%) +新信号:BTC 做空 @ $94,500(confidence 85%,趋势反转) + +分析: +- 趋势已明确反转 +- 决策:CLOSE(平仓止损) +- 理由:趋势反转,及时止损 +``` + +### 示例5:有挂单 + 同向信号 - 普通情况(HOLD) +``` +当前状态:BTC 做多挂单 @ $94,500(未成交) +新信号:BTC 做多 @ $96,000(confidence 70%,B级) + +分析: +- 挂单价格更优($94,500 < $96,000) +- 信号不是A级 +- 决策:HOLD(等待挂单成交) +- 理由:已有更优价格的挂单,无需重复操作 +``` + +### 示例6:有挂单 + 同向信号 - A级信号现价入场 +``` +当前状态:BTC 做多挂单 @ $94,000(未成交) +新信号:BTC 做多 @ $97,000(confidence 92%,A级,突破关键阻力) + +分析: +- A级突破信号,市场正在快速移动 +- 等待挂单可能错过机会 +- 当前价格距离挂单价 = (97000-94000)/94000 = 3.19% >= 1.5% +- 决策:CANCEL_PENDING + OPEN(现价开仓) +- 理由:A级突破信号,等待挂单可能错过机会 +``` + +### 示例7:完全无持仓无挂单 +``` +当前状态:无持仓,无挂单 +新信号:BTC 做多 @ $95,000(confidence 80%,uptrend) + +分析: +- 满足开新仓的所有条件 +- 决策:OPEN(开仓) +- 理由:首次入场,信号质量高,趋势向好 +``` + +## 杠杆和风险控制 +- **最大杠杆 20 倍**:最大仓位金额 = 账户余额 × 20 +- **当前杠杆**:当前杠杆 = 当前持仓价值 / 账户余额 +- **可用杠杆空间百分比**:(最大仓位金额 - 当前持仓价值) / 最大仓位金额 × 100% +- **可用杠杆空间 >= 3%** 才能开新仓 + +## 输出格式要求 +```json +{ + "decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD", + "action": "buy/sell", + "quantity": 保证金金额(USDT), + "entry_price": 入场价格, + "stop_loss": 止损价格, + "take_profit": 止盈价格, + "orders_to_cancel": ["order_id_1"], + "reasoning": "决策理由(必须说明当前持仓/挂单状态以及为什么选择这个操作)", + "risk_analysis": "风险分析" } ``` **注意**: -- `position_size` 可选值:`heavy`/`medium`/`light`/`micro` - - `heavy`:12% 保证金(顺势A级) - - `medium`:6% 保证金(顺势B级 或 震荡A级) - - `light`:3% 保证金(顺势C级 或 震荡B级) - - `micro`:1.5% 保证金(震荡C级 或 特殊逆势情况) -- `position_multiplier`:仓位倍率(可选,默认1.0) - - 顺势交易:1.0 - - 震荡市:0.7 - - 逆势交易(极少数情况):0.3 -- 如果 `decision` 是 `CANCEL_PENDING`,需要提供 `orders_to_cancel` 字段(要取消的订单ID列表) -- 如果 `decision` 是 `OPEN/ADD/CLOSE/REDUCE`,不需要 `orders_to_cancel` 字段 +- `quantity` 是保证金金额(USDT),交易系统会使用杠杆计算实际持仓价值 +- 如果 decision 是 CANCEL_PENDING,需要提供 orders_to_cancel 字段 +- reasoning 必须说明当前持仓/挂单状态以及为什么选择这个操作 -## 重要说明 -- **所有价格必须是纯数字**,不要加 $ 符号、逗号或其他格式 -- `stop_loss`、`take_profit` 必须是数字类型 -- **quantity 是保证金金额(USDT)**,交易系统会使用杠杆计算实际持仓价值 -- **position_size** 和 **quantity** 必须匹配(heavy 对应最大保证金金额) -- **入场方式由市场信号决定**,你只需要根据市场信号的 `entry_type` 来执行交易 +## 重要原则 +1. **仓位管理优先**:先管理现有仓位,再考虑开新仓 +2. **避免重复开仓**:同一标的同一方向最多1个持仓 + 1个挂单 +3. **安全第一**:宁可错过机会,也不要冒过大风险 +4. **遵守杠杆限制**:总杠杆永远不超过 20 倍 +5. **理性决策**:不要被 FOMO 情绪左右 -## 输出简洁性要求(重要!) -- **reasoning(决策理由)**:用1句话说清楚决策原因,不超过15个字 - - ❌ 错误示例:"由于市场信号显示强劲的上升趋势,且当前可用杠杆空间充足,因此决定开仓做多" - - ✅ 正确示例:"A级信号,上升趋势明确,空间充足" -- **risk_analysis(风险分析)**:用1句话指出核心风险,不超过15个字 - - ❌ 错误示例:"需要注意市场波动性增加可能带来的潜在亏损风险,同时关注止损位的设置" - - ✅ 正确示例:"关注波动风险,止损设好" - -## 注意事项 -1. **安全第一**:宁可错过机会,也不要冒过大风险 -2. **遵守杠杆限制**:总杠杆永远不超过 20 倍 -3. **理性决策**:不要被 FOMO 情绪左右 -4. **灵活应变**:根据市场变化调整策略 -5. **简洁输出**:决策理由和风险分析必须简明扼要 - -记住:你是交易执行者,不是市场分析师。市场分析已经完成了,你只需要根据分析结果和当前状态做出理性的交易决策! +记住:你是仓位管理者,不是信号执行器。你的首要任务是管理好现有仓位! """ def __init__(self): @@ -301,7 +365,11 @@ class TradingDecisionMaker: result = self._parse_decision_response(response, market_signal['symbol']) # 5. 验证决策安全性 - result = self._validate_decision(result, positions, account) + result = self._validate_decision( + result, positions, account, + pending_orders=pending_orders or [], + market_signal=market_signal + ) return result @@ -357,8 +425,8 @@ class TradingDecisionMaker: # 价格距离检查信息(用于 LLM 判断) context['price_distance_check'] = { 'enabled': True, - 'min_distance_percent': 1.0, # 最小价格距离 1% - 'exception_threshold': 90 # A 级信号且 confidence >= 90 时可放宽到 0.5% + 'min_distance_percent': 2.0, # 最小价格距离 2% + 'no_exception': True # 没有例外情况 } return context @@ -367,6 +435,70 @@ class TradingDecisionMaker: """构建决策提示词""" prompt_parts = [] + # ============================================================ + # 第一步:仓位管理决策流程摘要(最优先!) + # ============================================================ + prompt_parts.append("="*60) + prompt_parts.append("## 🎯 仓位管理决策流程(按顺序执行)") + prompt_parts.append("="*60) + + positions = context.get('positions', []) + pending_orders = context.get('pending_orders', []) + signals = context.get('signals', []) + + # 分析当前状态 + has_positions = len([p for p in positions if p.get('symbol') == context['symbol']]) > 0 + has_pending = len([o for o in pending_orders if o.get('symbol') == context['symbol']]) > 0 + + # 检查是否有强烈信号 + strong_signals = [s for s in signals if s.get('confidence', 0) >= 85] + has_strong_signal = len(strong_signals) > 0 + + if has_positions: + prompt_parts.append("📊 当前状态:**有持仓**") + prompt_parts.append("") + prompt_parts.append("决策优先级:") + prompt_parts.append("1️⃣ 首先检查是否需要平仓/减仓(信号反向或趋势减弱)") + if has_strong_signal: + prompt_parts.append("2️⃣ 然后检查是否需要加仓/滚仓(**有A级信号**,价格距离 >= 2%)") + prompt_parts.append(" ⭐ A级信号(confidence >= 85)可考虑加仓或滚仓") + else: + prompt_parts.append("2️⃣ 然后检查是否需要加仓(价格距离 >= 2%,盈利 >= 2%)") + prompt_parts.append(" ⚠️ 当前信号不是A级,不建议加仓") + prompt_parts.append("3️⃣ ❌ 不要开新仓(已有持仓时优先管理现有仓位)") + prompt_parts.append("") + prompt_parts.append("⚠️ 严禁重复开仓!在有持仓时只选择 HOLD/ADD/CLOSE/REDUCE") + + elif has_pending: + prompt_parts.append("📝 当前状态:**有挂单,无持仓**") + prompt_parts.append("") + prompt_parts.append("决策优先级:") + prompt_parts.append("1️⃣ 首先检查是否需要取消挂单(信号反向或价格不优)") + if has_strong_signal: + prompt_parts.append("2️⃣ 然后检查是否需要调整挂单或现价入场(**有A级信号**)") + prompt_parts.append(" ⭐ A级信号(confidence >= 85)可考虑取消挂单现价入场") + else: + prompt_parts.append("2️⃣ 然后检查是否需要调整挂单价格") + prompt_parts.append(" ⚠️ 当前信号不是A级,不建议调整挂单") + prompt_parts.append("3️⃣ ❌ 不要开新仓(已有挂单时等待成交或调整)") + prompt_parts.append("") + prompt_parts.append("⚠️ 严禁重复挂单!在有挂单时只选择 HOLD/CANCEL_PENDING") + + else: + prompt_parts.append("✨ 当前状态:**完全无持仓无挂单**") + prompt_parts.append("") + prompt_parts.append("决策优先级:") + prompt_parts.append("1️⃣ 这时才考虑开新仓(OPEN)") + prompt_parts.append("2️⃣ 必须满足所有开仓条件(信号质量、杠杆空间、价格合理)") + if not has_strong_signal: + prompt_parts.append(" ⚠️ 当前信号不是A级,建议轻仓") + prompt_parts.append("") + prompt_parts.append("✅ 可以开新仓,但必须谨慎评估") + + prompt_parts.append("") + prompt_parts.append("="*60) + prompt_parts.append("") + # 市场信号 prompt_parts.append(f"## 市场信号") prompt_parts.append(f"交易对: {context['symbol']}") @@ -527,10 +659,50 @@ class TradingDecisionMaker: # 价格距离检查规则 price_check = context.get('price_distance_check', {}) if price_check.get('enabled'): - prompt_parts.append(f"\n## 价格距离限制") - prompt_parts.append(f"⚠️ 重要:如果有相同方向的持仓/挂单,价格距离必须 >= {price_check.get('min_distance_percent', 1)}%") + min_distance = price_check.get('min_distance_percent', 2) + prompt_parts.append(f"\n## 价格距离限制(必须遵守)") + prompt_parts.append(f"⚠️ 重要:如果有相同方向的持仓/挂单,价格距离必须 >= {min_distance}%") prompt_parts.append(f"- 低于此距离不开新仓,避免风险过度集中") - prompt_parts.append(f"- A级信号(confidence >= {price_check.get('exception_threshold', 90)})可考虑放宽到 0.5%") + prompt_parts.append(f"- 此规则**没有例外**,无论信号等级多高都必须遵守") + + # 计算并显示当前价格距离 + current_price = context.get('current_price') + signals = context.get('signals', []) + positions = context.get('positions', []) + pending_orders = context.get('pending_orders', []) + + if signals and current_price: + for sig in signals: + sig_action = sig.get('action') + sig_entry = sig.get('entry_zone') + + if sig_action and sig_entry: + try: + sig_entry = float(sig_entry) + prompt_parts.append(f"\n当前信号 {sig_action} @ ${sig_entry:,.2f} 的价格距离检查:") + + # 检查持仓 + for pos in positions: + if pos.get('symbol') == context['symbol']: + pos_side = pos.get('side') + if (sig_action == 'buy' and pos_side == 'long') or (sig_action == 'sell' and pos_side == 'short'): + pos_entry = float(pos.get('entry_price', 0)) + distance = abs(sig_entry - pos_entry) / pos_entry * 100 + status = "✅ 通过" if distance >= min_distance else f"❌ 拒绝 (距离 {distance:.2f}% < {min_distance}%)" + prompt_parts.append(f" - 持仓 {pos_side} @ ${pos_entry:,.2f}: 距离 {distance:.2f}% {status}") + + # 检查挂单 + for order in pending_orders: + if order.get('symbol') == context['symbol']: + order_side = order.get('side') + if (sig_action == 'buy' and order_side == 'long') or (sig_action == 'sell' and order_side == 'short'): + order_entry = float(order.get('entry_price', 0)) + distance = abs(sig_entry - order_entry) / order_entry * 100 + status = "✅ 通过" if distance >= min_distance else f"❌ 拒绝 (距离 {distance:.2f}% < {min_distance}%)" + prompt_parts.append(f" - 挂单 {order_side} @ ${order_entry:,.2f}: 距离 {distance:.2f}% {status}") + + except (ValueError, TypeError): + pass prompt_parts.append(f"\n请根据以上信息,做出交易决策。") @@ -666,7 +838,9 @@ class TradingDecisionMaker: def _validate_decision(self, decision: Dict[str, Any], positions: List[Dict[str, Any]], - account: Dict[str, Any]) -> Dict[str, Any]: + account: Dict[str, Any], + pending_orders: List[Dict[str, Any]] = None, + market_signal: Dict[str, Any] = None) -> Dict[str, Any]: """验证决策安全性""" # 检查杠杆限制 if decision.get('decision') in ['OPEN', 'ADD']: @@ -687,6 +861,50 @@ class TradingDecisionMaker: f"超过最大仓位金额 (保证金 ${margin:.2f} → 持仓价值 ${position_value:.2f}, 总计 ${new_total_value:,.2f} > ${max_position_value:,.2f})" ) + # 价格距离检查:相同方向相同标的的挂单,价格距离 < 2% 时不加仓/开仓 + action = decision.get('action', '') + new_entry_price = decision.get('entry_price') + + if action and new_entry_price: + try: + new_entry_price = float(new_entry_price) + min_distance_percent = 2.0 + + # 检查持仓 + for pos in positions or []: + if pos.get('symbol') == decision.get('symbol'): + pos_side = pos.get('side', '') # 'long' or 'short' + pos_entry = float(pos.get('entry_price', 0)) + + # 相同方向的持仓 + if (action == 'buy' and pos_side == 'long') or (action == 'sell' and pos_side == 'short'): + distance_percent = abs(new_entry_price - pos_entry) / pos_entry * 100 + if distance_percent < min_distance_percent: + logger.warning(f"⚠️ 决策被拒绝: 价格距离过近 (新价格 ${new_entry_price:,.2f} vs 持仓 ${pos_entry:,.2f}, 距离 {distance_percent:.2f}% < {min_distance_percent}%)") + return self._get_hold_decision( + decision['symbol'], + f"价格距离持仓过近 (新价格 ${new_entry_price:,.2f} vs 持仓 ${pos_entry:,.2f}, 距离 {distance_percent:.2f}% < {min_distance_percent}%)" + ) + + # 检查挂单 + for order in pending_orders or []: + if order.get('symbol') == decision.get('symbol'): + order_side = order.get('side', '') + order_entry = float(order.get('entry_price', 0)) + + # 相同方向的挂单 + if (action == 'buy' and order_side == 'long') or (action == 'sell' and order_side == 'short'): + distance_percent = abs(new_entry_price - order_entry) / order_entry * 100 + if distance_percent < min_distance_percent: + logger.warning(f"⚠️ 决策被拒绝: 价格距离过近 (新价格 ${new_entry_price:,.2f} vs 挂单 ${order_entry:,.2f}, 距离 {distance_percent:.2f}% < {min_distance_percent}%)") + return self._get_hold_decision( + decision['symbol'], + f"价格距离挂单过近 (新价格 ${new_entry_price:,.2f} vs 挂单 ${order_entry:,.2f}, 距离 {distance_percent:.2f}% < {min_distance_percent}%)" + ) + + except (ValueError, TypeError) as e: + logger.warning(f"价格距离检查失败: {e}") + return decision def _get_hold_decision(self, symbol: str, reason: str = "") -> Dict[str, Any]: