This commit is contained in:
aaron 2026-02-26 00:36:45 +08:00
parent f6e15d4a48
commit 9783773f97
2 changed files with 195 additions and 10 deletions

View File

@ -519,6 +519,8 @@ class CryptoAgent:
market_signal, positions, account, current_price, pending_orders market_signal, positions, account, current_price, pending_orders
) )
self._log_trading_decision(paper_decision) self._log_trading_decision(paper_decision)
# 发送交易决策通知
await self._send_trading_decision_notification(paper_decision, market_signal, current_price, is_paper=True)
else: else:
logger.info(f"⏸️ 模拟交易未启用") logger.info(f"⏸️ 模拟交易未启用")
@ -532,6 +534,8 @@ class CryptoAgent:
market_signal, positions, account, current_price, pending_orders market_signal, positions, account, current_price, pending_orders
) )
self._log_trading_decision(real_decision) self._log_trading_decision(real_decision)
# 发送交易决策通知
await self._send_trading_decision_notification(real_decision, market_signal, current_price, is_paper=False)
else: else:
logger.info(f"⏸️ 实盘自动交易未开启") logger.info(f"⏸️ 实盘自动交易未开启")
else: else:
@ -737,26 +741,44 @@ class CryptoAgent:
if decision_type in ['OPEN', 'ADD']: if decision_type in ['OPEN', 'ADD']:
# 先执行交易 # 先执行交易
logger.info(f" 准备执行 {'模拟' if paper_trading_enabled else '实盘'} 交易...")
result = await self._execute_paper_trade(paper_decision, market_signal, current_price) result = await self._execute_paper_trade(paper_decision, market_signal, current_price)
# 检查是否成功执行 # 检查是否成功执行
order = result.get('order') if result else None order = result.get('order') if result else None
logger.info(f" 订单创建检查: order={'存在' if order else '不存在'}, result_key={'order' in (result or {})}")
if order: if order:
# 验证订单对象的有效性
if hasattr(order, 'order_id') and order.order_id:
logger.info(f" 订单验证通过: {order.order_id}")
# 只有成功创建订单后才发送通知 # 只有成功创建订单后才发送通知
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True paper_executed = True
else:
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
reason = "订单对象无效: 缺少order_id"
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason)
else: else:
# 有信号但订单创建失败,发送未执行通知 # 有信号但订单创建失败,发送未执行通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败' reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 模拟交易未执行: {reason}")
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason) await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason)
logger.warning(f" ⚠️ 模拟交易未执行,已发送通知") logger.warning(f" ⚠️ 模拟交易未执行: {reason}")
elif decision_type == 'CLOSE': elif decision_type == 'CLOSE':
await self._execute_close(paper_decision, paper_trading=True) await self._execute_close(paper_decision, paper_trading=True)
# CLOSE 操作也发送执行通知
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True paper_executed = True
elif decision_type == 'CANCEL_PENDING': elif decision_type == 'CANCEL_PENDING':
await self._execute_cancel_pending(paper_decision, paper_trading=True) await self._execute_cancel_pending(paper_decision, paper_trading=True)
# CANCEL_PENDING 操作也发送执行通知
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True paper_executed = True
elif decision_type == 'REDUCE': elif decision_type == 'REDUCE':
await self._execute_reduce(paper_decision, paper_trading=True) await self._execute_reduce(paper_decision, paper_trading=True)
# REDUCE 操作也发送执行通知
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True paper_executed = True
# ============================================================ # ============================================================
@ -790,12 +812,18 @@ class CryptoAgent:
logger.warning(f" ⚠️ 实盘交易未执行,已发送通知") logger.warning(f" ⚠️ 实盘交易未执行,已发送通知")
elif decision_type == 'CLOSE': elif decision_type == 'CLOSE':
await self._execute_close(real_decision, paper_trading=False) await self._execute_close(real_decision, paper_trading=False)
# CLOSE 操作也发送执行通知
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
real_executed = True real_executed = True
elif decision_type == 'CANCEL_PENDING': elif decision_type == 'CANCEL_PENDING':
await self._execute_cancel_pending(real_decision, paper_trading=False) await self._execute_cancel_pending(real_decision, paper_trading=False)
# CANCEL_PENDING 操作也发送执行通知
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
real_executed = True real_executed = True
elif decision_type == 'REDUCE': elif decision_type == 'REDUCE':
await self._execute_reduce(real_decision, paper_trading=False) await self._execute_reduce(real_decision, paper_trading=False)
# REDUCE 操作也发送执行通知
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
real_executed = True real_executed = True
# 如果都没有执行,给出提示 # 如果都没有执行,给出提示
@ -976,6 +1004,134 @@ class CryptoAgent:
import traceback import traceback
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
async def _send_trading_decision_notification(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float,
is_paper: bool = True):
"""发送交易决策通知(第二阶段)"""
try:
decision_type = decision.get('decision', 'HOLD')
symbol = market_signal.get('symbol')
# 账户类型标识
account_type = "📊 模拟" if is_paper else "💰 实盘"
# 决策类型映射
decision_map = {
'OPEN': '开仓',
'CLOSE': '平仓',
'ADD': '加仓',
'REDUCE': '减仓',
'CANCEL_PENDING': '取消挂单',
'HOLD': '观望'
}
decision_text = decision_map.get(decision_type, decision_type)
# 根据决策类型设置颜色
color_map = {
'OPEN': 'green',
'ADD': 'green',
'CLOSE': 'orange',
'REDUCE': 'orange',
'CANCEL_PENDING': 'red',
'HOLD': 'gray'
}
color = color_map.get(decision_type, 'blue')
# 构建标题
title = f"{account_type} {symbol} 交易决策: {decision_text}"
# 获取最佳信号用于显示
best_signal = self._get_best_signal_from_market(market_signal)
signal_confidence = best_signal.get('confidence', 0) if best_signal else 0
signal_action = best_signal.get('action', '') if best_signal else ''
# 方向图标
if 'buy' in signal_action.lower() or 'long' in signal_action.lower():
action_icon = '🟢'
action_text = '做多'
elif 'sell' in signal_action.lower() or 'short' in signal_action.lower():
action_icon = '🔴'
action_text = '做空'
else:
action_icon = ''
action_text = '观望'
# 构建内容
content_parts = [
f"{action_icon} **市场信号**: {action_text} | 信心度: {signal_confidence}%",
f"",
f"🎯 **交易决策**: {decision_text}",
f"",
]
# 添加决策详情
if decision_type != 'HOLD':
reasoning = decision.get('reasoning', '')
risk_analysis = decision.get('risk_analysis', '')
position_size = decision.get('position_size', 'N/A')
# 仓位图标
position_map = {'heavy': '🔥 重仓', 'medium': '📊 中仓', 'light': '🌱 轻仓', 'micro': '🌱 微仓'}
position_display = position_map.get(position_size, position_size)
content_parts.extend([
f"📊 **仓位**: {position_display}",
f"💭 **决策理由**: {reasoning}",
])
if risk_analysis:
content_parts.append(f"⚠️ **风险**: {risk_analysis}")
# 添加价格信息(如果有)
quantity = decision.get('quantity', 0)
if isinstance(quantity, (int, float)) and quantity > 0:
leverage = 20 # 固定20倍杠杆
position_value = quantity * leverage
content_parts.append(f"💰 **持仓价值**: ${position_value:,.2f} (保证金 ${quantity:.2f})")
stop_loss = decision.get('stop_loss')
take_profit = decision.get('take_profit')
if stop_loss:
content_parts.append(f"🛑 **止损**: ${stop_loss}")
if take_profit:
content_parts.append(f"🎯 **止盈**: ${take_profit}")
# 取消挂单时显示要取消的订单
if decision_type == 'CANCEL_PENDING':
orders_to_cancel = decision.get('orders_to_cancel', [])
if orders_to_cancel:
content_parts.append(f"🚫 **取消订单**: {len(orders_to_cancel)}")
for order_id in orders_to_cancel[:3]: # 最多显示3个
content_parts.append(f" - {order_id}")
if len(orders_to_cancel) > 3:
content_parts.append(f" - ... 还有 {len(orders_to_cancel) - 3}")
else:
# HOLD 决策
reasoning = decision.get('reasoning', '综合评估后选择观望')
content_parts.append(f"💭 **理由**: {reasoning}")
content_parts.append("")
content_parts.append(f"⏰ 当前价格: ${current_price:,.2f}")
content = "\n".join(content_parts)
# 发送通知
if self.settings.feishu_enabled:
await self.feishu.send_card(title, content, color)
if self.settings.telegram_enabled:
# Telegram 使用文本格式
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
logger.info(f" 📤 已发送交易决策通知: {decision_text}")
except Exception as e:
logger.warning(f"发送交易决策通知失败: {e}")
import traceback
logger.debug(traceback.format_exc())
async def _send_signal_notification(self, market_signal: Dict[str, Any], async def _send_signal_notification(self, market_signal: Dict[str, Any],
decision: Dict[str, Any], current_price: float, decision: Dict[str, Any], current_price: float,
is_paper: bool = True): is_paper: bool = True):
@ -1147,18 +1303,25 @@ class CryptoAgent:
logger.debug(f" 订单数据: {order_data}") logger.debug(f" 订单数据: {order_data}")
logger.info(f" 正在调用 create_order_from_signal...")
result = self.paper_trading.create_order_from_signal(order_data, current_price) result = self.paper_trading.create_order_from_signal(order_data, current_price)
logger.debug(f" 创建订单结果: {result}") logger.info(f" create_order_from_signal 返回结果: {result}")
# 记录订单 # 记录订单
order = result.get('order') order = result.get('order')
if order: if order:
# quantity 是保证金金额,持仓价值 = 保证金 × 20 # quantity 是保证金金额,持仓价值 = 保证金 × 20
position_value = quantity * 20 position_value = quantity * 20
logger.info(f" 📝 已创建模拟订单: {order.order_id} | 仓位: {position_size} | 持仓价值: ${position_value:.2f}") logger.info(f" ✅ 已创建模拟订单: {order.order_id} | 仓位: {position_size} | 持仓价值: ${position_value:.2f}")
logger.info(f" 订单状态: {order.status.value} | 入场价: ${order.entry_price:,.2f}")
else: else:
logger.warning(f" ⚠️ 创建模拟订单失败: {result}") # 订单创建失败,记录详细原因
reason = result.get('message', '未知原因')
cancelled_info = result.get('cancelled_orders', [])
logger.warning(f" ❌ 创建模拟订单失败: {reason}")
if cancelled_info:
logger.warning(f" 已取消的反向订单: {len(cancelled_info)}")
# 返回结果 # 返回结果
return result return result

View File

@ -224,6 +224,15 @@ class PaperTradingService:
# 获取信号等级 # 获取信号等级
grade = signal.get('signal_grade') or signal.get('grade', 'D') grade = signal.get('signal_grade') or signal.get('grade', 'D')
# 验证信号等级是否有效
valid_grades = ['A', 'B', 'C', 'D']
if grade not in valid_grades:
msg = f"无效的信号等级: {grade},有效值为: {', '.join(valid_grades)}"
logger.warning(f"{msg}: {signal.get('symbol')}")
result['message'] = msg
return result
if grade == 'D': if grade == 'D':
msg = "D级信号不符合开仓条件" msg = "D级信号不符合开仓条件"
logger.info(f"{msg}: {signal.get('symbol')}") logger.info(f"{msg}: {signal.get('symbol')}")
@ -297,19 +306,32 @@ class PaperTradingService:
db.commit() db.commit()
db.refresh(order) db.refresh(order)
# 验证订单是否成功写入数据库
if not order.order_id:
raise ValueError("订单提交后没有获得order_id")
# 添加到活跃订单缓存 # 添加到活跃订单缓存
self.active_orders[order.order_id] = order self.active_orders[order.order_id] = order
# 验证缓存添加成功
if order.order_id not in self.active_orders:
logger.error(f"订单已写入数据库但未添加到缓存: {order_id}")
raise ValueError("订单未添加到缓存")
entry_type_text = "现价" if entry_type == EntryType.MARKET else "挂单" entry_type_text = "现价" if entry_type == EntryType.MARKET else "挂单"
status_text = "已开仓" if status == OrderStatus.OPEN else "等待触发" status_text = "已开仓" if status == OrderStatus.OPEN else "等待触发"
logger.info(f"创建模拟订单: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text}") logger.info(f"创建模拟订单成功: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text}")
logger.info(f" 保证金: ${margin:,.0f} | 杠杆: {self.leverage}x | 持仓价值: ${position_value:,.0f} | 当前订单数: {len(self.active_orders)}/{self.max_orders}") logger.info(f" 保证金: ${margin:,.0f} | 杠杆: {self.leverage}x | 持仓价值: ${position_value:,.0f} | 当前订单数: {len(self.active_orders)}/{self.max_orders}")
result['order'] = order result['order'] = order
return result return result
except Exception as e: except Exception as e:
logger.error(f"创建模拟订单失败: {e}") logger.error(f"❌ 创建模拟订单失败: {e}")
logger.error(f" 订单数据: symbol={symbol}, side={side}, entry_price={entry_price}, margin={margin}")
import traceback
logger.error(f" 异常详情: {traceback.format_exc()}")
db.rollback() db.rollback()
result['message'] = f"订单创建失败: {str(e)}"
return result return result
finally: finally:
db.close() db.close()