update
This commit is contained in:
parent
c080962373
commit
5f11ec2308
@ -831,6 +831,14 @@ class CryptoAgent:
|
||||
paper_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 取消挂单未成功执行,跳过通知")
|
||||
elif decision_type == 'UPDATE_PENDING':
|
||||
update_success = await self._execute_update_pending(paper_decision, paper_trading=True)
|
||||
# UPDATE_PENDING 操作也发送执行通知
|
||||
if update_success:
|
||||
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
|
||||
paper_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 更新挂单未成功执行,跳过通知")
|
||||
elif decision_type == 'REDUCE':
|
||||
reduce_success = await self._execute_reduce(paper_decision, paper_trading=True)
|
||||
# REDUCE 操作也发送执行通知
|
||||
@ -883,6 +891,14 @@ class CryptoAgent:
|
||||
real_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 实盘取消挂单未成功执行,跳过通知")
|
||||
elif decision_type == 'UPDATE_PENDING':
|
||||
update_success = await self._execute_update_pending(real_decision, paper_trading=False)
|
||||
# UPDATE_PENDING 操作也发送执行通知
|
||||
if update_success:
|
||||
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
|
||||
real_executed = True
|
||||
else:
|
||||
logger.warning(f" ⚠️ 实盘更新挂单未成功执行,跳过通知")
|
||||
elif decision_type == 'REDUCE':
|
||||
reduce_success = await self._execute_reduce(real_decision, paper_trading=False)
|
||||
# REDUCE 操作也发送执行通知
|
||||
@ -1688,6 +1704,124 @@ class CryptoAgent:
|
||||
logger.error(f"执行取消挂单失败: {e}")
|
||||
return False
|
||||
|
||||
async def _execute_update_pending(self, decision: Dict[str, Any], paper_trading: bool = True) -> bool:
|
||||
"""执行更新挂单参数
|
||||
|
||||
Args:
|
||||
decision: 交易决策
|
||||
paper_trading: True=模拟交易, False=实盘交易
|
||||
|
||||
Returns:
|
||||
是否成功更新订单
|
||||
"""
|
||||
try:
|
||||
symbol = decision.get('symbol')
|
||||
decision_action = decision.get('action', '') # buy/sell
|
||||
orders_to_update = decision.get('orders_to_update', [])
|
||||
|
||||
# 获取新参数
|
||||
new_entry_price = decision.get('entry_price')
|
||||
new_stop_loss = decision.get('stop_loss')
|
||||
new_take_profit = decision.get('take_profit')
|
||||
|
||||
if not orders_to_update:
|
||||
logger.info(f" ⚠️ 没有需要更新的订单")
|
||||
return False
|
||||
|
||||
if not all([new_entry_price, new_stop_loss, new_take_profit]):
|
||||
logger.warning(f" ⚠️ 更新参数不完整: entry_price={new_entry_price}, stop_loss={new_stop_loss}, take_profit={new_take_profit}")
|
||||
return False
|
||||
|
||||
trading_service = self.paper_trading if paper_trading else self.real_trading
|
||||
trading_type = "模拟" if paper_trading else "实盘"
|
||||
|
||||
if not trading_service:
|
||||
logger.warning(f" {trading_type}交易服务未启用")
|
||||
return False
|
||||
|
||||
# 安全检查:验证要更新的订单是否属于当前symbol且方向相同
|
||||
active_orders = trading_service.get_active_orders()
|
||||
valid_orders = []
|
||||
invalid_orders = []
|
||||
wrong_direction_orders = []
|
||||
|
||||
for order_id in orders_to_update:
|
||||
# 查找订单
|
||||
order = next((o for o in active_orders if o.order_id == order_id), None)
|
||||
if not order:
|
||||
logger.warning(f" ⚠️ 订单不存在: {order_id}")
|
||||
invalid_orders.append(order_id)
|
||||
continue
|
||||
|
||||
# 检查订单是否已成交(只能更新未成交的挂单)
|
||||
if order.filled_price and order.filled_price > 0:
|
||||
logger.warning(f" ⚠️ 订单已成交,无法更新: {order_id}")
|
||||
invalid_orders.append(order_id)
|
||||
continue
|
||||
|
||||
# 检查订单是否属于当前symbol
|
||||
if order.symbol != symbol:
|
||||
logger.error(f" ❌ 安全拦截:订单 {order_id} 属于 {order.symbol},不是当前分析标的 {symbol}")
|
||||
invalid_orders.append(order_id)
|
||||
continue
|
||||
|
||||
# 检查订单方向是否与决策相同
|
||||
order_side = order.side.value # LONG/SHORT
|
||||
# 决策是buy时应该更新LONG(做多),决策是sell时应该更新SHORT(做空)
|
||||
should_update = False
|
||||
if decision_action == 'buy' and order_side == 'LONG':
|
||||
should_update = True
|
||||
elif decision_action == 'sell' and order_side == 'SHORT':
|
||||
should_update = True
|
||||
|
||||
if not should_update:
|
||||
logger.error(f" ❌ 安全拦截:订单 {order_id} 方向为 {order_side},与决策 {decision_action} 方向不一致")
|
||||
wrong_direction_orders.append(order_id)
|
||||
invalid_orders.append(order_id)
|
||||
continue
|
||||
|
||||
valid_orders.append(order)
|
||||
|
||||
if invalid_orders:
|
||||
logger.error(f" 🚫 拒绝更新 {len(invalid_orders)} 个不符合条件的订单")
|
||||
|
||||
if wrong_direction_orders:
|
||||
logger.error(f" ⚠️ {len(wrong_direction_orders)} 个方向不一致的订单被拦截")
|
||||
|
||||
if not valid_orders:
|
||||
logger.warning(f" ⚠️ 没有有效的订单可以更新")
|
||||
return False
|
||||
|
||||
logger.info(f" 🔄 {trading_type}更新挂单: {symbol}")
|
||||
logger.info(f" 新参数: 入场=${new_entry_price:,.2f}, 止损=${new_stop_loss:,.2f}, 止盈=${new_take_profit:,.2f}")
|
||||
|
||||
updated_count = 0
|
||||
for order in valid_orders:
|
||||
try:
|
||||
# 更新订单参数
|
||||
result = trading_service.update_order(
|
||||
order.order_id,
|
||||
entry_price=new_entry_price,
|
||||
stop_loss=new_stop_loss,
|
||||
take_profit=new_take_profit
|
||||
)
|
||||
|
||||
if result and result.get('success'):
|
||||
updated_count += 1
|
||||
logger.info(f" ✅ 订单更新成功: {order.order_id}")
|
||||
logger.info(f" 旧参数: 入场=${order.entry_price:,.2f}, 止损=${order.stop_loss:,.2f}, 止盈=${order.take_profit:,.2f}")
|
||||
else:
|
||||
logger.warning(f" ⚠️ 更新订单失败: {order.order_id} | {result.get('message', '')}")
|
||||
except Exception as e:
|
||||
logger.error(f" ❌ 更新订单异常: {order.order_id} | {e}")
|
||||
|
||||
logger.info(f" 📊 成功更新 {updated_count}/{len(valid_orders)} 个订单")
|
||||
return updated_count > 0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"执行更新挂单失败: {e}")
|
||||
return False
|
||||
|
||||
async def _execute_reduce(self, decision: Dict[str, Any], paper_trading: bool = True) -> bool:
|
||||
"""执行减仓
|
||||
|
||||
|
||||
@ -129,34 +129,45 @@ class TradingDecisionMaker:
|
||||
3. **HOLD(观望)** - 如果反转信号不强
|
||||
|
||||
#### 情况C:无持仓 + 有同向挂单
|
||||
**优先选择:HOLD(等待挂单成交)**
|
||||
**优先选择:UPDATE_PENDING(更新挂单参数)**
|
||||
|
||||
**❌ 严禁取消挂单追涨**:
|
||||
- 即使有新信号,也不要取消挂单去市价追涨
|
||||
- 挂单价格通常更优,耐心等待
|
||||
- 价格快速移动时更应该观望,而不是追涨
|
||||
**核心原则:适应新信号,不要等老信号**
|
||||
- 新信号代表最新的市场分析
|
||||
- 应该用新信号的参数更新挂单
|
||||
- 不要死守过时的挂单价格
|
||||
|
||||
**只有以下极端情况才考虑调整**:
|
||||
- ✅ 趋势明确反转(如从涨转跌)
|
||||
- ✅ 新价格更优且距离 >= 3%(仍用 limit,不用 market)
|
||||
- ✅ 挂单已无意义(如突破后回踩确认)
|
||||
**更新挂单的条件**:
|
||||
- ✅ 新信号与挂单**方向相同**(都是 buy 或都是 sell)
|
||||
- ✅ 新信号提供了**更新的入场价、止损、止盈**
|
||||
- ✅ 新信号置信度 >= 60(C级以上)
|
||||
|
||||
**更新内容**(根据新信号调整):
|
||||
- 入场价格:使用新信号的 entry_price
|
||||
- 止损价格:使用新信号的 stop_loss
|
||||
- 止盈价格:使用新信号的 take_profit
|
||||
- 入场方式:保持 limit(不要改成 market)
|
||||
|
||||
**示例**:
|
||||
```
|
||||
当前:BTC 做多挂单 @ $94,000(未成交)
|
||||
新信号:BTC 做多 @ $96,500(B级,75%置信度)
|
||||
当前:BTC 做多挂单 @ $94,000,止损 $93,500,止盈 $95,500
|
||||
新信号:BTC 做多 @ $93,800(B级,75%置信度),止损 $93,200,止盈 $95,200
|
||||
|
||||
分析:
|
||||
- 新价格更高,但挂单价格更优
|
||||
- 价格正在快速移动($94,000 → $96,500)
|
||||
- 决策:HOLD(继续等待挂单成交)
|
||||
- 理由:挂单价格更优,不要追涨,耐心等待
|
||||
- 新信号更保守,入场价更低
|
||||
- 应该更新挂单参数以适应新的市场分析
|
||||
- 决策:UPDATE_PENDING(更新挂单)
|
||||
- 理由:根据新信号更新入场价 $93,800,止损 $93,200,止盈 $95,200
|
||||
```
|
||||
|
||||
**⚠️ 例外情况(保持 HOLD)**:
|
||||
- 价格正在快速加速移动(5m 连续大阳/阴线)
|
||||
- 新信号置信度 < 60(D级信号,质量太低)
|
||||
- 新信号入场价距离当前价格 >= 2%(追涨杀跌风险)
|
||||
- 新信号是 market 入场(不要改成 market 去追)
|
||||
|
||||
**❌ 严禁**:
|
||||
- 取消挂单后市价追涨/杀跌
|
||||
- 价格距离 < 3% 时调整挂单
|
||||
- 价格快速移动时任何操作
|
||||
- 价格快速移动时的任何更新操作
|
||||
|
||||
#### 情况D:无持仓 + 有反向挂单
|
||||
**优先选择**:
|
||||
@ -237,7 +248,7 @@ class TradingDecisionMaker:
|
||||
## 输出格式
|
||||
```json
|
||||
{
|
||||
"decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD",
|
||||
"decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/UPDATE_PENDING/HOLD",
|
||||
"action": "buy/sell",
|
||||
"quantity": 保证金金额(USDT),
|
||||
"entry_price": 入场价格,
|
||||
@ -245,6 +256,7 @@ class TradingDecisionMaker:
|
||||
"take_profit": 止盈价格,
|
||||
"orders_to_close": ["order_id_1"], // CLOSE/REDUCE 时指定要平仓的订单ID
|
||||
"orders_to_cancel": ["order_id_1"], // CANCEL_PENDING 时指定要取消的订单ID
|
||||
"orders_to_update": ["order_id_1"], // UPDATE_PENDING 时指定要更新的订单ID
|
||||
"reasoning": "决策理由(必须说明当前持仓/挂单状态以及为什么选择这个操作)",
|
||||
"risk_analysis": "风险分析"
|
||||
}
|
||||
@ -253,6 +265,7 @@ class TradingDecisionMaker:
|
||||
**重要提示**:
|
||||
- 当 `decision` 为 `CLOSE` 时,**必须**在 `orders_to_close` 中指定要平仓的订单ID列表
|
||||
- 当 `decision` 为 `CANCEL_PENDING` 时,**必须**在 `orders_to_cancel` 中指定要取消的订单ID列表
|
||||
- 当 `decision` 为 `UPDATE_PENDING` 时,**必须**在 `orders_to_update` 中指定要更新的订单ID列表,并提供新的 entry_price、stop_loss、take_profit
|
||||
- 如果需要平仓所有持仓,`orders_to_close` 应包含所有持仓订单的ID
|
||||
|
||||
## 决策示例
|
||||
@ -307,29 +320,31 @@ class TradingDecisionMaker:
|
||||
- 理由:趋势反转,及时止损
|
||||
```
|
||||
|
||||
### 示例5:有挂单 + 同向信号 - 普通情况(HOLD)
|
||||
### 示例5:有挂单 + 同向信号 - 更新挂单参数(UPDATE_PENDING)
|
||||
```
|
||||
当前状态:BTC 做多挂单 @ $94,500(未成交)
|
||||
新信号:BTC 做多 @ $96,000(confidence 70%,B级)
|
||||
当前状态:BTC 做多挂单 @ $94,500(未成交),订单ID: ord_456
|
||||
新信号:BTC 做多 @ $93,800(confidence 75%,B级,回调至支撑位)
|
||||
|
||||
分析:
|
||||
- 挂单价格更优($94,500 < $96,000)
|
||||
- 信号不是A级
|
||||
- 价格正在快速移动
|
||||
- 决策:HOLD(等待挂单成交)
|
||||
- 理由:挂单价格更优,不要追涨
|
||||
- 新信号提供了更优的入场价格($93,800 < $94,500)
|
||||
- 市场出现回调机会,应该调整挂单价格
|
||||
- 决策:UPDATE_PENDING(更新挂单参数)
|
||||
- orders_to_update: ["ord_456"]
|
||||
- 新参数:entry_price=$93,800, stop_loss=$93,200, take_profit=$95,200
|
||||
- 理由:根据新信号更新挂单参数,适应新的市场分析
|
||||
```
|
||||
|
||||
### 示例6:有挂单 + 同向信号 - 不要追涨
|
||||
### 示例6:有挂单 + 同向信号 - 价格加速时保持(HOLD)
|
||||
```
|
||||
当前状态:BTC 做多挂单 @ $94,000(未成交)
|
||||
新信号:BTC 做多 @ $97,000(confidence 85%,B级,突破)
|
||||
|
||||
分析:
|
||||
- 新价格更高,但挂单价格更优
|
||||
- 价格快速移动($94,000 → $97,000)
|
||||
- 决策:HOLD(等待挂单成交或等待回调)
|
||||
- 理由:不要追涨,挂单价格更优
|
||||
- 价格快速移动($94,000 → $97,000,涨幅3.2%)
|
||||
- 5m 连续2根大阳线,正在加速
|
||||
- 新信号入场价距离当前价格 >= 2%
|
||||
- 决策:HOLD(不要追涨)
|
||||
- 理由:价格正在加速,禁止追涨杀跌,等待回调
|
||||
```
|
||||
|
||||
### 示例7:完全无持仓无挂单
|
||||
@ -965,7 +980,7 @@ class TradingDecisionMaker:
|
||||
market_signal: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||
"""验证决策安全性"""
|
||||
# 检查杠杆限制
|
||||
if decision.get('decision') in ['OPEN', 'ADD']:
|
||||
if decision.get('decision') in ['OPEN', 'ADD', 'UPDATE_PENDING']:
|
||||
balance = float(account.get('current_balance', 0))
|
||||
total_position_value = float(account.get('total_position_value', 0))
|
||||
max_leverage = 20
|
||||
@ -984,6 +999,7 @@ class TradingDecisionMaker:
|
||||
)
|
||||
|
||||
# 盈亏比检查:所有交易必须满足盈亏比 >= 1:1.5
|
||||
# UPDATE_PENDING 也需要验证盈亏比
|
||||
action = decision.get('action', '')
|
||||
entry_price = decision.get('entry_price')
|
||||
stop_loss = decision.get('stop_loss')
|
||||
|
||||
@ -1264,6 +1264,103 @@ class PaperTradingService:
|
||||
'message': '取消挂单失败'
|
||||
}
|
||||
|
||||
def update_order(self, order_id: str, entry_price: float = None,
|
||||
stop_loss: float = None, take_profit: float = None) -> Dict[str, Any]:
|
||||
"""
|
||||
更新挂单参数
|
||||
|
||||
Args:
|
||||
order_id: 订单ID
|
||||
entry_price: 新的入场价格
|
||||
stop_loss: 新的止损价格
|
||||
take_profit: 新的止盈价格
|
||||
|
||||
Returns:
|
||||
更新结果字典
|
||||
"""
|
||||
if order_id not in self.active_orders:
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'订单不存在: {order_id}'
|
||||
}
|
||||
|
||||
order = self.active_orders[order_id]
|
||||
|
||||
# 只能更新挂单状态的订单
|
||||
if order.status != OrderStatus.PENDING:
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'只能更新挂单状态的订单,当前状态: {order.status.value}'
|
||||
}
|
||||
|
||||
# 至少需要一个参数
|
||||
if entry_price is None and stop_loss is None and take_profit is None:
|
||||
return {
|
||||
'success': False,
|
||||
'message': '至少需要提供一个更新参数(entry_price, stop_loss, 或 take_profit)'
|
||||
}
|
||||
|
||||
result = self._update_pending_order(order, entry_price, stop_loss, take_profit)
|
||||
if result:
|
||||
return {
|
||||
'success': True,
|
||||
'order_id': order_id,
|
||||
'message': '挂单已更新'
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'success': False,
|
||||
'message': '更新挂单失败'
|
||||
}
|
||||
|
||||
def _update_pending_order(self, order: PaperOrder, entry_price: float = None,
|
||||
stop_loss: float = None, take_profit: float = None) -> Dict[str, Any]:
|
||||
"""更新挂单参数"""
|
||||
db = db_service.get_session()
|
||||
try:
|
||||
# 记录旧值用于日志
|
||||
old_entry = order.entry_price
|
||||
old_sl = order.stop_loss
|
||||
old_tp = order.take_profit
|
||||
|
||||
# 更新参数
|
||||
if entry_price is not None:
|
||||
order.entry_price = entry_price
|
||||
if stop_loss is not None:
|
||||
order.stop_loss = stop_loss
|
||||
if take_profit is not None:
|
||||
order.take_profit = take_profit
|
||||
|
||||
db.merge(order)
|
||||
db.commit()
|
||||
|
||||
# 更新内存缓存
|
||||
self.active_orders[order.order_id] = order
|
||||
|
||||
logger.info(f"挂单已更新: {order.order_id} | {order.symbol}")
|
||||
logger.info(f" 入场: ${old_entry:,.2f} → ${order.entry_price:,.2f}")
|
||||
if stop_loss is not None:
|
||||
logger.info(f" 止损: ${old_sl:,.2f} → ${order.stop_loss:,.2f}")
|
||||
if take_profit is not None:
|
||||
logger.info(f" 止盈: ${old_tp:,.2f} → ${order.take_profit:,.2f}")
|
||||
|
||||
return {
|
||||
'order_id': order.order_id,
|
||||
'symbol': order.symbol,
|
||||
'side': order.side.value,
|
||||
'status': 'updated',
|
||||
'entry_price': order.entry_price,
|
||||
'stop_loss': order.stop_loss,
|
||||
'take_profit': order.take_profit,
|
||||
'message': '挂单已更新'
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"更新挂单失败: {e}")
|
||||
db.rollback()
|
||||
return None
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def _cancel_pending_order(self, order: PaperOrder) -> Dict[str, Any]:
|
||||
"""取消挂单"""
|
||||
db = db_service.get_session()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user