update
This commit is contained in:
parent
f6e15d4a48
commit
9783773f97
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user