update
This commit is contained in:
parent
7817632847
commit
14f1695350
@ -927,11 +927,11 @@ class CryptoAgent:
|
||||
await self._send_signal_notification(market_signal, decision, current_price)
|
||||
else:
|
||||
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
|
||||
# 订单创建失败,理由已在日志中记录,无需单独通知
|
||||
await self._notify_execution_failure(market_signal, decision, "订单对象无效(缺少order_id)", prefix="[模拟盘]")
|
||||
else:
|
||||
# 订单创建失败,理由已在日志中记录,无需单独通知
|
||||
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
||||
logger.warning(f" ⚠️ 交易未执行: {reason}")
|
||||
await self._notify_execution_failure(market_signal, decision, reason, prefix="[模拟盘]")
|
||||
elif decision_type == 'CLOSE':
|
||||
close_success = await self._execute_close(decision, current_price)
|
||||
# CLOSE 操作也发送执行通知
|
||||
@ -1271,8 +1271,10 @@ class CryptoAgent:
|
||||
|
||||
async def _send_signal_notification(self, market_signal: Dict[str, Any],
|
||||
decision: Dict[str, Any], current_price: float,
|
||||
prefix: str = ""):
|
||||
"""发送交易执行通知(第三阶段)"""
|
||||
prefix: str = "", hl_order_status: str = None):
|
||||
"""发送交易执行通知(第三阶段)
|
||||
hl_order_status: Hyperliquid 限价单实际状态 'resting'|'filled'|None
|
||||
"""
|
||||
try:
|
||||
decision_type = decision.get('decision', 'HOLD')
|
||||
|
||||
@ -1292,7 +1294,11 @@ class CryptoAgent:
|
||||
quantity = decision.get('quantity', 'N/A')
|
||||
stop_loss = decision.get('stop_loss', '')
|
||||
take_profit = decision.get('take_profit', '')
|
||||
confidence = decision.get('confidence', 0)
|
||||
# confidence 优先从决策本身读取,否则从市场信号的最佳信号读取
|
||||
confidence = decision.get('confidence')
|
||||
if confidence is None:
|
||||
_best = self._get_best_signal_from_market(market_signal)
|
||||
confidence = _best.get('confidence', 0) if _best else 0
|
||||
|
||||
# 决策类型映射
|
||||
decision_map = {
|
||||
@ -1320,17 +1326,31 @@ class CryptoAgent:
|
||||
# 从市场信号中获取入场方式(需要在构建标题之前)
|
||||
best_signal = self._get_best_signal_from_market(market_signal)
|
||||
entry_type = best_signal.get('entry_type', 'market') if best_signal else 'market'
|
||||
entry_type_text = '现价单' if entry_type == 'market' else '挂单'
|
||||
entry_type_icon = '⚡' if entry_type == 'market' else '⏳'
|
||||
|
||||
# 对 Hyperliquid 限价单:用实际订单状态决定显示
|
||||
# resting=真的在挂单中, filled=已立即成交, None=非HL或市价单
|
||||
if hl_order_status == 'resting':
|
||||
entry_type_text = '挂单'
|
||||
entry_type_icon = '⏳'
|
||||
elif hl_order_status == 'filled':
|
||||
entry_type_text = '现价成交'
|
||||
entry_type_icon = '⚡'
|
||||
else:
|
||||
entry_type_text = '现价单' if entry_type == 'market' else '挂单'
|
||||
entry_type_icon = '⚡' if entry_type == 'market' else '⏳'
|
||||
|
||||
# 仓位图标
|
||||
position_map = {'heavy': '🔥 重仓', 'medium': '📊 中仓', 'light': '🌱 轻仓'}
|
||||
position_display = position_map.get(position_size, '🌱 轻仓')
|
||||
|
||||
# 构建卡片标题和颜色 - 考虑入场方式,添加 [执行] 前缀区分
|
||||
# 挂单时标题显示"挂单",现价单时显示"开仓"/"平仓"等
|
||||
# 构建卡片标题:Hyperliquid 限价单区分实际状态
|
||||
if decision_type == 'OPEN':
|
||||
decision_title = '挂单' if entry_type == 'limit' else '开仓'
|
||||
if hl_order_status == 'resting':
|
||||
decision_title = '挂单中'
|
||||
elif hl_order_status == 'filled':
|
||||
decision_title = '开仓(立即成交)'
|
||||
else:
|
||||
decision_title = '挂单' if entry_type == 'limit' else '开仓'
|
||||
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
|
||||
color = "green"
|
||||
elif decision_type == 'CLOSE':
|
||||
@ -1338,7 +1358,12 @@ class CryptoAgent:
|
||||
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
|
||||
color = "orange"
|
||||
elif decision_type == 'ADD':
|
||||
decision_title = '挂单' if entry_type == 'limit' else '加仓'
|
||||
if hl_order_status == 'resting':
|
||||
decision_title = '加仓挂单中'
|
||||
elif hl_order_status == 'filled':
|
||||
decision_title = '加仓(立即成交)'
|
||||
else:
|
||||
decision_title = '挂单' if entry_type == 'limit' else '加仓'
|
||||
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
|
||||
color = "green"
|
||||
elif decision_type == 'REDUCE':
|
||||
@ -1403,6 +1428,37 @@ class CryptoAgent:
|
||||
except Exception as e:
|
||||
logger.warning(f"发送交易执行通知失败: {e}")
|
||||
|
||||
async def _notify_execution_failure(self, market_signal: Dict[str, Any],
|
||||
decision: Dict[str, Any], reason: str,
|
||||
prefix: str = ""):
|
||||
"""发送执行失败通知(决策给出了 OPEN/ADD 但实际未能开仓)"""
|
||||
try:
|
||||
symbol = market_signal.get('symbol', '')
|
||||
decision_type = decision.get('decision', 'OPEN')
|
||||
action = decision.get('action', '')
|
||||
title_prefix = f"{prefix} " if prefix else ""
|
||||
|
||||
action_text = "做多" if 'buy' in action.lower() else ("做空" if 'sell' in action.lower() else action)
|
||||
decision_text = {'OPEN': '开仓', 'ADD': '加仓'}.get(decision_type, decision_type)
|
||||
|
||||
title = f"{title_prefix}⚠️ {symbol} {decision_text}未执行"
|
||||
content = "\n".join([
|
||||
f"🔴 **决策**: {decision_text}({action_text})",
|
||||
f"❌ **未执行原因**: {reason}",
|
||||
f"🕐 **时间**: {datetime.now().strftime('%H:%M:%S')}",
|
||||
])
|
||||
|
||||
if self.settings.feishu_enabled:
|
||||
await self.feishu_paper.send_card(title, content, "red")
|
||||
if self.settings.telegram_enabled:
|
||||
await self.telegram.send_message(f"{title}\n\n{content}")
|
||||
if self.settings.dingtalk_enabled:
|
||||
await self.dingtalk.send_action_card(title, content)
|
||||
|
||||
logger.info(f" 📤 已发送执行失败通知: {reason}")
|
||||
except Exception as e:
|
||||
logger.warning(f"发送执行失败通知失败: {e}")
|
||||
|
||||
async def _execute_paper_trade(self, decision: Dict[str, Any], market_signal: Dict[str, Any], current_price: float):
|
||||
"""执行模拟交易"""
|
||||
try:
|
||||
@ -1871,7 +1927,11 @@ class CryptoAgent:
|
||||
|
||||
if result.get('success'):
|
||||
logger.info(f" ✅ Hyperliquid 交易成功")
|
||||
await self._send_signal_notification(market_signal, decision, current_price, prefix="[Hyperliquid]")
|
||||
# 根据实际订单状态决定通知文案:resting=真挂单,filled=已成交
|
||||
order_status = result.get('verified_order_status', 'filled')
|
||||
await self._send_signal_notification(market_signal, decision, current_price,
|
||||
prefix="[Hyperliquid]",
|
||||
hl_order_status=order_status)
|
||||
# 止盈止损设置失败时单独告警
|
||||
if result.get('tp_sl_warning'):
|
||||
await self._notify_hyperliquid_error(symbol, "设置止盈止损", result['tp_sl_warning'])
|
||||
@ -1948,27 +2008,48 @@ class CryptoAgent:
|
||||
price=entry_price
|
||||
)
|
||||
|
||||
# 如果开仓成功,设置止盈止损
|
||||
# 如果开仓成功,处理止盈止损 + 验证订单实际状态
|
||||
if result.get('success'):
|
||||
order_status = result.get('order_status', 'filled') # market单默认filled
|
||||
|
||||
# 限价单:如果立即成交(filled),验证持仓是否存在
|
||||
if entry_type == 'limit' and order_status == 'filled':
|
||||
position = self.hyperliquid.get_position_for_symbol(symbol)
|
||||
if position:
|
||||
logger.info(f" ✅ 限价单立即成交,持仓确认: {symbol} size={position['size']}")
|
||||
else:
|
||||
logger.warning(f" ⚠️ 限价单显示 filled 但未查到持仓,可能已被平仓或数据延迟")
|
||||
|
||||
# 限价单:如果仍在挂单中(resting),验证订单是否在挂单列表
|
||||
elif entry_type == 'limit' and order_status == 'resting':
|
||||
order_id = result.get('order_id')
|
||||
open_orders = self.hyperliquid.get_open_orders(symbol)
|
||||
if any(o.get('order_id') == order_id for o in open_orders):
|
||||
logger.info(f" ✅ 限价单已挂出并确认可见: oid={order_id}")
|
||||
else:
|
||||
logger.warning(f" ⚠️ 限价单 oid={order_id} 未在挂单列表中查到(可能已成交或延迟)")
|
||||
|
||||
# 将实际状态写回 result,供通知层使用
|
||||
result['verified_order_status'] = order_status
|
||||
|
||||
tp_price = decision.get('take_profit')
|
||||
sl_price = decision.get('stop_loss')
|
||||
|
||||
if tp_price or sl_price:
|
||||
# 判断方向
|
||||
is_long = (side == 'buy')
|
||||
# 只有已成交的订单才设置止盈止损(挂单中的不设,等成交后再设)
|
||||
if order_status != 'resting':
|
||||
is_long = (side == 'buy')
|
||||
tp_sl_result = self.hyperliquid.set_tp_sl(
|
||||
symbol=symbol,
|
||||
is_long=is_long,
|
||||
size=size,
|
||||
tp_price=tp_price,
|
||||
sl_price=sl_price
|
||||
)
|
||||
|
||||
# 设置止盈止损
|
||||
tp_sl_result = self.hyperliquid.set_tp_sl(
|
||||
symbol=symbol,
|
||||
is_long=is_long,
|
||||
size=size,
|
||||
tp_price=tp_price,
|
||||
sl_price=sl_price
|
||||
)
|
||||
|
||||
if not tp_sl_result.get('success'):
|
||||
logger.warning(f" ⚠️ 设置止盈止损失败: {tp_sl_result.get('error')}")
|
||||
result['tp_sl_warning'] = tp_sl_result.get('error', '设置止盈止损失败')
|
||||
if not tp_sl_result.get('success'):
|
||||
logger.warning(f" ⚠️ 设置止盈止损失败: {tp_sl_result.get('error')}")
|
||||
result['tp_sl_warning'] = tp_sl_result.get('error', '设置止盈止损失败')
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ -181,6 +181,11 @@ class HyperliquidTradingService:
|
||||
logger.error(f"❌ Hyperliquid 市价单错误: {error_statuses}")
|
||||
return {"success": False, "error": str(error_statuses), "result": result}
|
||||
|
||||
# statuses 为空 → 静默拒绝
|
||||
if not statuses:
|
||||
logger.error(f"❌ Hyperliquid 市价单:返回 statuses 为空,订单未成功提交")
|
||||
return {"success": False, "error": "Empty order statuses (order not placed)", "result": result}
|
||||
|
||||
side = "买入" if is_buy else "卖出"
|
||||
order_type = "平仓" if reduce_only else "开仓"
|
||||
logger.info(f"✅ Hyperliquid 市价单: {order_type} {side} {symbol} {size}")
|
||||
@ -224,16 +229,42 @@ class HyperliquidTradingService:
|
||||
|
||||
# 检查单个订单状态
|
||||
statuses = result.get("response", {}).get("data", {}).get("statuses", [])
|
||||
|
||||
# 有错误 → 失败
|
||||
error_statuses = [s for s in statuses if "error" in s]
|
||||
if error_statuses:
|
||||
logger.error(f"❌ Hyperliquid 限价单错误: {error_statuses}")
|
||||
return {"success": False, "error": str(error_statuses), "result": result}
|
||||
|
||||
side = "买入" if is_buy else "卖出"
|
||||
logger.info(f"✅ Hyperliquid 限价单: {side} {symbol} {size} @ ${price}")
|
||||
# statuses 为空 → Hyperliquid 静默拒绝,视为失败
|
||||
if not statuses:
|
||||
logger.error(f"❌ Hyperliquid 限价单:返回 statuses 为空,订单未成功提交")
|
||||
return {"success": False, "error": "Empty order statuses (order not placed)", "result": result}
|
||||
|
||||
# 判断订单实际状态:resting(挂单中)还是 filled(立即成交)
|
||||
first_status = statuses[0]
|
||||
if "resting" in first_status:
|
||||
order_id = first_status["resting"].get("oid")
|
||||
order_status = "resting"
|
||||
side = "买入" if is_buy else "卖出"
|
||||
logger.info(f"✅ Hyperliquid 限价单已挂出: {side} {symbol} {size} @ ${price} (oid={order_id})")
|
||||
elif "filled" in first_status:
|
||||
order_status = "filled"
|
||||
filled_info = first_status["filled"]
|
||||
avg_px = filled_info.get("avgPx", price)
|
||||
logger.info(f"✅ Hyperliquid 限价单立即成交: {symbol} {size} @ ${avg_px}")
|
||||
order_id = filled_info.get("oid")
|
||||
else:
|
||||
# 未知状态,记录并视为成功但标记 unknown
|
||||
order_status = "unknown"
|
||||
order_id = None
|
||||
logger.warning(f"⚠️ Hyperliquid 限价单状态未知: {first_status}")
|
||||
|
||||
side = "买入" if is_buy else "卖出"
|
||||
return {
|
||||
"success": True,
|
||||
"order_status": order_status, # "resting" | "filled" | "unknown"
|
||||
"order_id": order_id,
|
||||
"symbol": symbol,
|
||||
"side": "buy" if is_buy else "sell",
|
||||
"size": size,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user