updateupdate

This commit is contained in:
aaron 2026-03-04 20:40:33 +08:00
parent 5f11ec2308
commit 1809a45a5b
3 changed files with 216 additions and 387 deletions

View File

@ -54,14 +54,6 @@ class CryptoAgent:
# 模拟交易服务(始终启用)
self.paper_trading = get_paper_trading_service()
# 实盘交易服务(如果配置了 API
self.real_trading = None
try:
from app.services.real_trading_service import get_real_trading_service
self.real_trading = get_real_trading_service()
except Exception as e:
logger.warning(f"实盘交易服务初始化失败: {e}")
# 状态管理
self.last_signals: Dict[str, Dict[str, Any]] = {}
self.signal_cooldown: Dict[str, datetime] = {}
@ -87,13 +79,7 @@ class CryptoAgent:
})
logger.info(f"加密货币智能体初始化完成LLM 驱动),监控交易对: {self.symbols}")
logger.info(f"📊 交易: 始终启用")
if self.real_trading:
auto_status = "启用" if self.real_trading.get_auto_trading_status() else "禁用"
logger.info(f"实盘交易: 已配置 (自动交易: {auto_status})")
else:
logger.info(f"实盘交易: 未配置")
logger.info(f"📊 模拟交易: 始终启用")
def _on_price_update(self, symbol: str, price: float):
"""处理实时价格更新(用于模拟交易)"""
@ -555,50 +541,27 @@ class CryptoAgent:
# 获取配置
paper_trading_enabled = self.settings.paper_trading_enabled
real_trading_enabled = self.settings.real_trading_enabled
# 分别存储模拟和实盘的决策
paper_decision = None
real_decision = None
# 交易决策
if paper_trading_enabled:
logger.info(f"\n📊 【交易决策】")
positions, account, pending_orders = self._get_trading_state(use_real_trading=False)
positions, account, pending_orders = self._get_trading_state()
# 过滤只传递当前symbol的挂单给决策器避免LLM搞混
pending_orders_for_symbol = [o for o in pending_orders if o.get('symbol') == symbol]
paper_decision = await self.decision_maker.make_decision(
decision = await self.decision_maker.make_decision(
market_signal, positions, account, current_price, pending_orders_for_symbol
)
self._log_trading_decision(paper_decision)
self._log_trading_decision(decision)
# 发送交易决策通知
await self._send_trading_decision_notification(paper_decision, market_signal, current_price, is_paper=True)
await self._send_trading_decision_notification(decision, market_signal, current_price)
else:
logger.info(f"⏸️ 交易未启用")
# 实盘交易决策
if real_trading_enabled:
logger.info(f"\n💰 【实盘交易决策】")
# 检查是否开启自动交易
if self.real_trading and self.real_trading.get_auto_trading_status():
positions, account, pending_orders = self._get_trading_state(use_real_trading=True)
# 过滤只传递当前symbol的挂单给决策器避免LLM搞混
pending_orders_for_symbol = [o for o in pending_orders if o.get('symbol') == symbol]
real_decision = await self.decision_maker.make_decision(
market_signal, positions, account, current_price, pending_orders_for_symbol
)
self._log_trading_decision(real_decision)
# 发送交易决策通知
await self._send_trading_decision_notification(real_decision, market_signal, current_price, is_paper=False)
else:
logger.info(f"⏸️ 实盘自动交易未开启")
else:
logger.info(f"⏸️ 实盘交易未启用")
decision = None
# ============================================================
# 第三阶段:执行交易决策
# ============================================================
await self._execute_decisions(paper_decision, real_decision, market_signal, current_price)
await self._execute_decisions(decision, market_signal, current_price)
except Exception as e:
logger.error(f"❌ 分析 {symbol} 出错: {e}")
@ -709,24 +672,16 @@ class CryptoAgent:
if risk:
logger.info(f" 风险: {risk}")
def _get_trading_state(self, use_real_trading: bool = False) -> tuple:
def _get_trading_state(self) -> tuple:
"""
获取交易状态持仓和账户
Args:
use_real_trading: True 获取实盘状态False 获取模拟交易状态
Returns:
(positions, account, pending_orders) - 持仓列表账户状态挂单列表
"""
if use_real_trading and self.real_trading:
# 实盘交易
active_orders = self.real_trading.get_active_orders()
account = self.real_trading.get_account_status()
else:
# 模拟交易
active_orders = self.paper_trading.get_active_orders()
account = self.paper_trading.get_account_status()
# 模拟交易
active_orders = self.paper_trading.get_active_orders()
account = self.paper_trading.get_account_status()
# 分离持仓和挂单
position_list = []
@ -756,10 +711,9 @@ class CryptoAgent:
return position_list, account, pending_orders
async def _execute_decisions(self, paper_decision: Dict[str, Any],
real_decision: Dict[str, Any],
async def _execute_decisions(self, decision: Dict[str, Any],
market_signal: Dict[str, Any], current_price: float):
"""执行交易决策(模拟和实盘分别执行)"""
"""执行交易决策"""
# 选择最佳信号用于保存
best_signal = self._get_best_signal_from_market(market_signal)
@ -773,20 +727,15 @@ class CryptoAgent:
# 获取配置
paper_trading_enabled = self.settings.paper_trading_enabled
real_trading_enabled = self.settings.real_trading_enabled
# 记录执行结果
paper_executed = False
real_executed = False
# ============================================================
# 执行交易决策
# ============================================================
if paper_trading_enabled and paper_decision:
decision_type = paper_decision.get('decision', 'HOLD')
if paper_trading_enabled and decision:
decision_type = decision.get('decision', 'HOLD')
if decision_type == 'HOLD':
reasoning = paper_decision.get('reasoning', '观望')
reasoning = decision.get('reasoning', '观望')
logger.info(f"\n📊 交易决策: {reasoning}")
# HOLD决策的理由已在交易决策通知中说明无需单独通知
else:
@ -795,7 +744,7 @@ class CryptoAgent:
if decision_type in ['OPEN', 'ADD']:
# 先执行交易
logger.info(f" 准备执行交易...")
result = await self._execute_paper_trade(paper_decision, market_signal, current_price)
result = await self._execute_paper_trade(decision, market_signal, current_price)
# 检查是否成功执行
order = result.get('order') if result else None
@ -806,8 +755,7 @@ class CryptoAgent:
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)
paper_executed = True
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
# 订单创建失败,理由已在日志中记录,无需单独通知
@ -816,101 +764,28 @@ class CryptoAgent:
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 交易未执行: {reason}")
elif decision_type == 'CLOSE':
close_success = await self._execute_close(paper_decision, current_price, paper_trading=True)
close_success = await self._execute_close(decision, current_price)
# CLOSE 操作也发送执行通知
if close_success:
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 平仓未成功执行,跳过通知")
elif decision_type == 'CANCEL_PENDING':
cancel_success = await self._execute_cancel_pending(paper_decision, paper_trading=True)
cancel_success = await self._execute_cancel_pending(decision)
# CANCEL_PENDING 操作也发送执行通知
if cancel_success:
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True
await self._send_signal_notification(market_signal, decision, current_price)
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_success = await self._execute_reduce(decision)
# REDUCE 操作也发送执行通知
if reduce_success:
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 减仓未成功执行,跳过通知")
# ============================================================
# 执行实盘交易决策
# ============================================================
if real_trading_enabled and real_decision:
# 检查是否开启自动交易
if self.real_trading and self.real_trading.get_auto_trading_status():
decision_type = real_decision.get('decision', 'HOLD')
if decision_type == 'HOLD':
reasoning = real_decision.get('reasoning', '观望')
logger.info(f"\n💰 实盘交易: {reasoning}")
# HOLD决策的理由已在交易决策通知中说明无需单独通知
else:
logger.info(f"\n💰 【执行实盘交易】")
if decision_type in ['OPEN', 'ADD']:
# 先执行交易
result = await self._execute_real_trade(real_decision, market_signal, current_price)
# 检查是否成功执行
if result and result.get('success'):
# 只有成功创建订单后才发送通知
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
real_executed = True
else:
# 订单创建失败,理由已在日志中记录,无需单独通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 实盘交易未执行: {reason}")
elif decision_type == 'CLOSE':
close_success = await self._execute_close(real_decision, current_price, paper_trading=False)
# CLOSE 操作也发送执行通知
if close_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 == 'CANCEL_PENDING':
cancel_success = await self._execute_cancel_pending(real_decision, paper_trading=False)
# CANCEL_PENDING 操作也发送执行通知
if cancel_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 == '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 操作也发送执行通知
if reduce_success:
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
real_executed = True
else:
logger.warning(f" ⚠️ 实盘减仓未成功执行,跳过通知")
# 如果都没有执行,给出提示
if not paper_executed and not real_executed:
logger.info(f"\n⏸️ 所有交易均为观望,无需执行")
else:
logger.info(f"\n⏸️ 交易未启用或决策为空")
def _get_best_signal_from_market(self, market_signal: Dict[str, Any]) -> Dict[str, Any]:
"""从市场信号中获取最佳信号"""
@ -1094,15 +969,14 @@ class CryptoAgent:
async def _send_trading_decision_notification(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float,
is_paper: bool = True):
current_price: float):
"""发送交易决策通知(第二阶段)"""
try:
decision_type = decision.get('decision', 'HOLD')
symbol = market_signal.get('symbol')
# 账户类型标识
account_type = "📊 交易" if is_paper else "💰 实盘"
account_type = "📊 交易"
# 决策类型映射
decision_map = {
@ -1228,8 +1102,7 @@ class CryptoAgent:
logger.debug(traceback.format_exc())
async def _send_signal_notification(self, market_signal: Dict[str, Any],
decision: Dict[str, Any], current_price: float,
is_paper: bool = True):
decision: Dict[str, Any], current_price: float):
"""发送交易执行通知(第三阶段)"""
try:
decision_type = decision.get('decision', 'HOLD')
@ -1259,7 +1132,7 @@ class CryptoAgent:
decision_text = decision_map.get(decision_type, decision_type)
# 账户类型标识
account_type = "📊" if is_paper else "💰"
account_type = "📊"
# 方向图标
if 'long' in action.lower() or 'buy' in action.lower():
@ -1365,14 +1238,18 @@ class CryptoAgent:
action = decision.get('action', '')
position_size = decision.get('position_size', 'light')
# 直接使用 LLM 决策的 quantity
quantity = decision.get('quantity', 0)
if quantity <= 0:
logger.warning(f" ⚠️ LLM 决策的 quantity 无效: {quantity},使用默认值")
quantity = self._calculate_quantity_by_position_size(position_size, real_trading=False)
# 使用新的动态仓位计算方法
logger.info(f" 计算动态仓位: {position_size}")
margin, position_value = self.paper_trading._calculate_dynamic_position(position_size, symbol)
if margin <= 0:
logger.warning(f" ⚠️ 计算的保证金无效: {margin},无法开仓")
return False
quantity = margin # 保证金金额
logger.info(f" 准备创建订单: {symbol} {action} {position_size}")
logger.info(f" LLM 决策金额: ${quantity:.2f} USDT")
logger.info(f" 保证金: ${quantity:.2f} | 持仓价值: ${position_value:.2f}")
# 转换决策的 action 为 paper_trading 期望的格式
trading_action = self._convert_trading_action(action)
@ -1395,7 +1272,7 @@ class CryptoAgent:
'confidence': decision.get('confidence', 50),
'signal_grade': 'B', # 默认B级
'position_size': position_size,
'quantity': quantity # 使用 LLM 决策的金额
'quantity': quantity # 使用计算后的保证金金额
}
logger.debug(f" 订单数据: {order_data}")
@ -1464,66 +1341,12 @@ class CryptoAgent:
return position_config.get(position_size, 200)
async def _execute_real_trade(self, decision: Dict[str, Any], market_signal: Dict[str, Any], current_price: float):
"""执行实盘交易"""
try:
symbol = decision.get('symbol')
action = decision.get('action', '')
position_size = decision.get('position_size', 'light')
# 直接使用 LLM 决策的 quantity
quantity = decision.get('quantity', 0)
if quantity <= 0:
logger.warning(f" ⚠️ LLM 决策的 quantity 无效: {quantity},使用默认值")
quantity = self._calculate_quantity_by_position_size(position_size, real_trading=True)
logger.info(f" 实盘交易: {position_size} → 保证金 ${quantity:.2f} USDT → 持仓价值 ${quantity * 20:.2f}")
# 转换决策的 action 为 paper_trading 期望的格式
trading_action = self._convert_trading_action(action)
# 从市场信号中获取入场方式和入场价格
best_signal = self._get_best_signal_from_market(market_signal)
entry_type = best_signal.get('entry_type', 'market') if best_signal else 'market'
entry_price = best_signal.get('entry_price', current_price) if best_signal else current_price
logger.info(f" 入场方式: {entry_type} | 入场价格: ${entry_price:,.2f}")
# 转换决策为订单格式(价格字段已在 LLM 解析时转换为 float
order_data = {
'symbol': symbol,
'action': trading_action, # 使用转换后的 action
'entry_type': entry_type, # 使用信号中的入场方式
'entry_price': entry_price if entry_type == 'limit' else current_price, # limit单使用entry_pricemarket单使用current_price
'stop_loss': decision.get('stop_loss'),
'take_profit': decision.get('take_profit'),
'confidence': decision.get('confidence', 50),
'signal_grade': 'B',
'position_size': position_size,
'quantity': quantity # 使用 LLM 决策的金额
}
result = self.real_trading.create_order_from_signal(order_data, current_price)
if result.get('success'):
logger.info(f" 💰 已创建实盘订单: {result.get('order_id')} | 持仓价值: ${quantity * 20:.2f}")
await self._notify_real_order_created(symbol, decision, result)
else:
logger.warning(f" ⚠️ 创建实盘订单失败: {result.get('message', 'Unknown error')}")
# 返回结果
return result
except Exception as e:
logger.error(f"执行实盘交易失败: {e}")
async def _execute_close(self, decision: Dict[str, Any], current_price: float, paper_trading: bool = True) -> bool:
async def _execute_close(self, decision: Dict[str, Any], current_price: float) -> bool:
"""执行平仓
Args:
decision: 交易决策应包含 orders_to_close 字段
current_price: 当前价格
paper_trading: True=模拟交易, False=实盘交易
Returns:
是否成功执行平仓
@ -1532,71 +1355,59 @@ class CryptoAgent:
symbol = decision.get('symbol')
orders_to_close = decision.get('orders_to_close', [])
if paper_trading:
# 模拟交易平仓
if self.paper_trading:
logger.info(f" 🔒 平仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}")
if self.paper_trading:
logger.info(f" 🔒 平仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}")
# 如果决策中没有指定订单ID则获取该交易对的所有活跃订单
if not orders_to_close:
logger.warning(f" ⚠️ 决策中未指定 orders_to_close将平仓 {symbol} 的所有持仓")
active_orders = self.paper_trading.get_active_orders(symbol)
orders_to_close = [o.get('order_id') for o in active_orders if o.get('status') in ('OPEN', 'FILLED', 'PENDING')]
# 如果决策中没有指定订单ID则获取该交易对的所有活跃订单
if not orders_to_close:
logger.warning(f" ⚠️ 决策中未指定 orders_to_close将平仓 {symbol} 的所有持仓")
active_orders = self.paper_trading.get_active_orders(symbol)
orders_to_close = [o.get('order_id') for o in active_orders if o.get('status') in ('OPEN', 'FILLED', 'PENDING')]
if not orders_to_close:
logger.warning(f" 没有找到需要平仓的订单")
return False
if not orders_to_close:
logger.warning(f" 没有找到需要平仓的订单")
return False
logger.info(f" 待平仓订单: {orders_to_close}")
logger.info(f" 待平仓订单: {orders_to_close}")
closed_count = 0
for order_id in orders_to_close:
try:
# 先获取订单信息
order_info = self.paper_trading.get_order_by_id(order_id)
if not order_info:
logger.warning(f" ❌ 订单不存在: {order_id}")
continue
closed_count = 0
for order_id in orders_to_close:
try:
# 先获取订单信息
order_info = self.paper_trading.get_order_by_id(order_id)
if not order_info:
logger.warning(f" ❌ 订单不存在: {order_id}")
continue
status = order_info.get('status')
status = order_info.get('status')
if status == 'PENDING':
# 取消挂单
result = self.paper_trading.cancel_order(order_id)
if result.get('success'):
logger.info(f" ✅ 已取消挂单: {order_id}")
closed_count += 1
else:
logger.warning(f" ❌ 取消挂单失败: {order_id} - {result.get('message')}")
elif status in ('OPEN', 'FILLED'):
# 平仓已成交订单
result = self.paper_trading.close_order_manual(order_id, current_price)
if result:
logger.info(f" ✅ 已平仓: {order_id} @ ${current_price}")
closed_count += 1
else:
logger.warning(f" ❌ 平仓失败: {order_id}")
if status == 'PENDING':
# 取消挂单
result = self.paper_trading.cancel_order(order_id)
if result.get('success'):
logger.info(f" ✅ 已取消挂单: {order_id}")
closed_count += 1
else:
logger.warning(f" ⚠️ 订单状态无需处理: {order_id} - {status}")
except Exception as e:
logger.error(f" ❌ 处理订单 {order_id} 失败: {e}")
logger.warning(f" ❌ 取消挂单失败: {order_id} - {result.get('message')}")
elif status in ('OPEN', 'FILLED'):
# 平仓已成交订单
result = self.paper_trading.close_order_manual(order_id, current_price)
if result:
logger.info(f" ✅ 已平仓: {order_id} @ ${current_price}")
closed_count += 1
else:
logger.warning(f" ❌ 平仓失败: {order_id}")
else:
logger.warning(f" ⚠️ 订单状态无需处理: {order_id} - {status}")
except Exception as e:
logger.error(f" ❌ 处理订单 {order_id} 失败: {e}")
logger.info(f" 📊 平仓汇总: {closed_count}/{len(orders_to_close)} 个订单已处理")
return closed_count > 0
else:
logger.warning(f" 交易服务未初始化")
return False
logger.info(f" 📊 平仓汇总: {closed_count}/{len(orders_to_close)} 个订单已处理")
return closed_count > 0
else:
# 实盘平仓
if self.real_trading and self.real_trading.get_auto_trading_status():
logger.info(f" 🔒 实盘平仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}")
# TODO: 实现实盘平仓逻辑
return True
else:
logger.warning(f" 实盘交易服务未启用或自动交易未开启")
return False
logger.warning(f" 交易服务未初始化")
return False
except Exception as e:
logger.error(f"执行平仓失败: {e}")
@ -1604,12 +1415,11 @@ class CryptoAgent:
logger.error(traceback.format_exc())
return False
async def _execute_cancel_pending(self, decision: Dict[str, Any], paper_trading: bool = True) -> bool:
async def _execute_cancel_pending(self, decision: Dict[str, Any]) -> bool:
"""执行取消挂单
Args:
decision: 交易决策
paper_trading: True=模拟交易, False=实盘交易
Returns:
是否成功取消订单
@ -1621,14 +1431,13 @@ class CryptoAgent:
if not orders_to_cancel:
logger.info(f" ⚠️ 没有需要取消的订单")
return
return False
trading_service = self.paper_trading if paper_trading else self.real_trading
trading_type = "模拟" if paper_trading else "实盘"
trading_service = self.paper_trading
if not trading_service:
logger.warning(f" {trading_type}交易服务未启用")
return
logger.warning(f" 交易服务未启用")
return False
# 安全检查验证要取消的订单是否属于当前symbol且方向相反
active_orders = trading_service.get_active_orders()
@ -1681,7 +1490,7 @@ class CryptoAgent:
logger.warning(f" ⚠️ 没有有效的订单可以取消")
return False
logger.info(f" 🚫 {trading_type}取消挂单: {symbol}")
logger.info(f" 🚫 取消挂单: {symbol}")
logger.info(f" 取消订单数量: {len(valid_orders)}")
cancelled_count = 0
@ -1704,12 +1513,11 @@ class CryptoAgent:
logger.error(f"执行取消挂单失败: {e}")
return False
async def _execute_update_pending(self, decision: Dict[str, Any], paper_trading: bool = True) -> bool:
async def _execute_update_pending(self, decision: Dict[str, Any]) -> bool:
"""执行更新挂单参数
Args:
decision: 交易决策
paper_trading: True=模拟交易, False=实盘交易
Returns:
是否成功更新订单
@ -1732,11 +1540,10 @@ class CryptoAgent:
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 "实盘"
trading_service = self.paper_trading
if not trading_service:
logger.warning(f" {trading_type}交易服务未启用")
logger.warning(f" 交易服务未启用")
return False
# 安全检查验证要更新的订单是否属于当前symbol且方向相同
@ -1792,7 +1599,7 @@ class CryptoAgent:
logger.warning(f" ⚠️ 没有有效的订单可以更新")
return False
logger.info(f" 🔄 {trading_type}更新挂单: {symbol}")
logger.info(f" 🔄 更新挂单: {symbol}")
logger.info(f" 新参数: 入场=${new_entry_price:,.2f}, 止损=${new_stop_loss:,.2f}, 止盈=${new_take_profit:,.2f}")
updated_count = 0
@ -1822,27 +1629,25 @@ class CryptoAgent:
logger.error(f"执行更新挂单失败: {e}")
return False
async def _execute_reduce(self, decision: Dict[str, Any], paper_trading: bool = True) -> bool:
async def _execute_reduce(self, decision: Dict[str, Any]) -> bool:
"""执行减仓
Args:
decision: 交易决策
paper_trading: True=模拟交易, False=实盘交易
Returns:
是否成功执行减仓
"""
try:
symbol = decision.get('symbol')
trading_type = "模拟" if paper_trading else "实盘"
logger.info(f" 📤 {trading_type}减仓: {symbol}")
logger.info(f" 📤 减仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}")
trading_service = self.paper_trading if paper_trading else self.real_trading
trading_service = self.paper_trading
if not trading_service:
logger.warning(f" {trading_type}交易服务未初始化")
logger.warning(f" 交易服务未初始化")
return False
# TODO: 实现减仓逻辑
@ -1990,44 +1795,6 @@ class CryptoAgent:
'trend': signal.get('trend')
}
async def _notify_real_order_created(
self,
symbol: str,
signal: Dict[str, Any],
result: Dict[str, Any]
):
"""发送实盘订单创建通知"""
side = signal.get('action', 'buy')
side_text = "做多" if side == 'buy' else "做空"
side_icon = "🟢" if side == 'buy' else "🔴"
grade = signal.get('grade', 'N/A')
position_size = result.get('position_size', 'light')
quantity = result.get('quantity', 0) # 这是保证金金额
position_value = quantity * 20 # 持仓价值 = 保证金 × 杠杆
title = f"💰 实盘订单已创建 - {symbol}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"⭐ **信号等级**: {grade}",
f"📊 **仓位**: {position_size}",
f"💰 **持仓价值**: ${position_value:,.2f}",
f"🆔 **订单ID**: {result.get('order_id', '')[:12]}...",
f"",
f"⚠️ **真实资金交易中**",
]
content = "\n".join(content_parts)
if self.settings.feishu_enabled:
await self.feishu_paper.send_card(title, content, "red")
if self.settings.telegram_enabled:
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
if self.settings.dingtalk_enabled:
await self.dingtalk.send_action_card(title, content)
logger.info(f"已发送实盘订单创建通知: {result.get('order_id')}")
async def _notify_position_adjustment(
self,
symbol: str,
@ -2085,13 +1852,12 @@ class CryptoAgent:
market_signal: Dict[str, Any],
decision: Dict[str, Any],
current_price: float,
is_paper: bool = True,
reason: str = ""
):
"""发送有信号但未执行交易的通知"""
try:
symbol = market_signal.get('symbol')
account_type = "📊" if is_paper else "💰"
account_type = "📊"
# 获取最佳信号
best_signal = self._get_best_signal_from_market(market_signal)

View File

@ -129,45 +129,77 @@ class TradingDecisionMaker:
3. **HOLD观望** - 如果反转信号不强
#### 情况C无持仓 + 有同向挂单
**优先选择UPDATE_PENDING更新挂单参数**
**优先选择OPEN新增挂单- 金字塔式布局**
**核心原则适应新信号不要等老信号**
- 新信号代表最新的市场分析
- 应该用新信号的参数更新挂单
- 要死守过时的挂单价格
**核心原则允许合理的多挂单策略**
- 同方向可以最多有3个挂单金字塔布局
- 新挂单价格与现有挂单价格差异 > 1%
- 同价格位分散风险提高成交概率
**更新挂单的条件**
- 新信号与挂单**方向相同**都是 buy 或都是 sell
- 新信号提供了**更新的入场价止损止盈**
**允许新增挂单的条件**
- 当前同向挂单数量 < 3
- 新信号入场价与所有现有挂单价格差异 > 1%
- 新信号置信度 >= 60C级以上
- 价格没有在快速加速移动
**更新内容**根据新信号调整
- 入场价格使用新信号的 entry_price
- 止损价格使用新信号的 stop_loss
- 止盈价格使用新信号的 take_profit
- 入场方式保持 limit不要改成 market
**示例**
**示例1新增第2个挂单**
```
当前BTC 做多挂单 @ $94,000止损 $93,500止盈 $95,500
新信号BTC 做多 @ $93,800B级75%置信度止损 $93,200止盈 $95,200
当前BTC 做多挂单1 @ $94,000
新信号BTC 做多 @ $92,700B级75%置信度
分析
- 新信号更保守入场价更低
- 应该更新挂单参数以适应新的市场分析
- 决策UPDATE_PENDING更新挂单
- 理由根据新信号更新入场价 $93,800止损 $93,200止盈 $95,200
- 价格差异(94000-92700)/94000 = 1.38% > 1%
- 挂单数量1 < 3
- 决策OPEN新增挂单2 @ $92,700
- 理由金字塔布局在不同价位布置挂单
```
**示例2新增第3个挂单**
```
当前BTC 做多挂单1 @ $94,000挂单2 @ $92,700
新信号BTC 做多 @ $91,500B级70%置信度
分析
- 新价格与挂单2差异(92700-91500)/92700 = 1.29% > 1%
- 挂单数量2 < 3
- 决策OPEN新增挂单3 @ $91,500
- 理由金字塔布局继续分散挂单位置
```
**示例3达到3个挂单上限**
```
当前BTC 做多挂单1 @ $94,000挂单2 @ $92,700挂单3 @ $91,500
新信号BTC 做多 @ $90,800B级70%置信度
分析
- 已有3个挂单达到上限
- 决策HOLD
- 理由同向挂单已达3个上限不再新增
```
**示例4价格差异太小**
```
当前BTC 做多挂单1 @ $94,000
新信号BTC 做多 @ $93,200价格差异仅0.85%
分析
- 价格差异(94000-93200)/94000 = 0.85% < 1%
- 太接近现有挂单没有意义
- 决策HOLD
- 理由新价格与现有挂单太近不新增
```
** 例外情况保持 HOLD**
- 价格正在快速加速移动5m 连续大阳/阴线
- 新信号置信度 < 60D级信号质量太低
- 新信号入场价距离当前价格 >= 2%追涨杀跌风险
- 新信号是 market 入场不要改成 market 去追
- 新信号是 market 入场改成 market 去追
- 价格差异 < 1%太接近现有挂单
** 严禁**
- 同向挂单数量 >= 3个时继续新增
- 价格差异 < 1% 时新增挂单
- 取消挂单后市价追涨/杀跌
- 价格快速移动时的任何更新操作
#### 情况D无持仓 + 有反向挂单
**优先选择**
@ -177,19 +209,29 @@ class TradingDecisionMaker:
#### 情况E完全无持仓无挂单
**这时才考虑开新仓OPEN**
### 第三步:开新仓的严格限制
只有在满足以下所有条件时才开新仓
- 当前交易对**没有任何持仓和挂单**
### 第三步:开新仓的规则
**金字塔式挂单策略**
- 无持仓无挂单可以开新仓
- 无持仓 + 同向挂单 < 3可以新增挂单价格差异 > 1%
- 无持仓 + 同向挂单 >= 3不再新增
- 有持仓 + 无同向挂单可以考虑加仓
- 有持仓 + 有同向挂单优先持仓挂单次之
**开新仓条件**
- 信号质量足够高confidence >= 60
- 可用杠杆空间充足
- 价格和止损合理
- 不在价格加速移动中
## 🚨 铁律(违反即拒绝)
### 1. 避免重复开仓
- **同一标的同一方向最多只允许1个持仓 + 1个挂单**
- 如果已有持仓/挂单不要开新仓考虑加仓或调整
- 价格距离 < 2% 时不加仓也不开新仓
### 1. 金字塔挂单规则(同方向)
- **最多3个挂单**同一标的同一方向最多允许3个挂单
- **价格差异 > 1%**新挂单与现有挂单价格差异必须 > 1%
- **挂单优先**优先使用 limit 挂单慎用 market 市价
- **不要重复**价格差异 < 1% 时不要新增挂单
- **有持仓时**优先考虑持仓管理挂单次之
- **价格距离 < 2% 时不加仓**与持仓价格太近时不加仓
### 2. 趋势与信号一致性
| 当前趋势 | 信号方向 | 允许操作 |
@ -248,7 +290,7 @@ class TradingDecisionMaker:
## 输出格式
```json
{
"decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/UPDATE_PENDING/HOLD",
"decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD",
"action": "buy/sell",
"quantity": 保证金金额USDT,
"entry_price": 入场价格,
@ -256,7 +298,6 @@ 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": "风险分析"
}
@ -265,7 +306,7 @@ class TradingDecisionMaker:
**重要提示**
- `decision` `CLOSE` **必须** `orders_to_close` 中指定要平仓的订单ID列表
- `decision` `CANCEL_PENDING` **必须** `orders_to_cancel` 中指定要取消的订单ID列表
- `decision` `UPDATE_PENDING` **必须** `orders_to_update` 中指定要更新的订单ID列表并提供新的 entry_pricestop_losstake_profit
- 同方向允许最多3个挂单金字塔布局价格差异需 > 1%
- 如果需要平仓所有持仓`orders_to_close` 应包含所有持仓订单的ID
## 决策示例
@ -320,21 +361,44 @@ class TradingDecisionMaker:
- 理由趋势反转及时止损
```
### 示例5有挂单 + 同向信号 - 更新挂单参数UPDATE_PENDING
### 示例5有挂单 + 同向信号 - 金字塔布局OPEN
```
当前状态BTC 做多挂单 @ $94,500未成交订单ID: ord_456
新信号BTC 做多 @ $93,800confidence 75%B级回调至支撑位
当前状态BTC 做多挂单1 @ $94,500未成交订单ID: ord_456
新信号BTC 做多 @ $93,500confidence 75%B级回调至支撑位
分析
- 新信号提供了更优的入场价格$93,800 < $94,500
- 市场出现回调机会应该调整挂单价格
- 决策UPDATE_PENDING更新挂单参数
- orders_to_update: ["ord_456"]
- 新参数entry_price=$93,800, stop_loss=$93,200, take_profit=$95,200
- 理由根据新信号更新挂单参数适应新的市场分析
- 同向挂单数量1 < 3
- 价格差异(94500-93500)/94500 = 1.06% > 1%
- 满足金字塔布局条件
- 决策OPEN新增挂单2
- 理由金字塔布局在不同价位布置挂单提高成交概率
```
### 示例6有挂单 + 同向信号 - 价格加速时保持HOLD
### 示例6有挂单 + 同向信号 - 价格差异太小HOLD
```
当前状态BTC 做多挂单1 @ $94,500挂单2 @ $93,500
新信号BTC 做多 @ $93,000confidence 70%B级
分析
- 同向挂单数量2 < 3
- 新价格与挂单2差异(93500-93000)/93500 = 0.53% < 1%
- 太接近现有挂单
- 决策HOLD
- 理由新价格与现有挂单太近不新增
```
### 示例7有挂单 + 同向信号 - 达到上限HOLD
```
当前状态BTC 做多挂单1 @ $94,500挂单2 @ $93,500挂单3 @ $92,500
新信号BTC 做多 @ $91,500confidence 70%B级
分析
- 同向挂单数量3已达上限
- 决策HOLD
- 理由同向挂单已达3个上限不再新增
```
### 示例8有挂单 + 同向信号 - 价格加速HOLD
```
当前状态BTC 做多挂单 @ $94,000未成交
新信号BTC 做多 @ $97,000confidence 85%B级突破
@ -347,7 +411,7 @@ class TradingDecisionMaker:
- 理由价格正在加速禁止追涨杀跌等待回调
```
### 示例7:完全无持仓无挂单
### 示例9:完全无持仓无挂单
```
当前状态无持仓无挂单
新信号BTC 做多 @ $95,000confidence 80%uptrend
@ -980,7 +1044,7 @@ class TradingDecisionMaker:
market_signal: Dict[str, Any] = None) -> Dict[str, Any]:
"""验证决策安全性"""
# 检查杠杆限制
if decision.get('decision') in ['OPEN', 'ADD', 'UPDATE_PENDING']:
if decision.get('decision') in ['OPEN', 'ADD']:
balance = float(account.get('current_balance', 0))
total_position_value = float(account.get('total_position_value', 0))
max_leverage = 20
@ -999,7 +1063,6 @@ class TradingDecisionMaker:
)
# 盈亏比检查:所有交易必须满足盈亏比 >= 1:1.5
# UPDATE_PENDING 也需要验证盈亏比
action = decision.get('action', '')
entry_price = decision.get('entry_price')
stop_loss = decision.get('stop_loss')

View File

@ -358,7 +358,7 @@ class PaperTradingService:
计算逻辑
- 根据可用保证金的倍数确定持仓价值
- micro: 0.8x, light: 1.2x, medium: 2.0x, heavy: 3.0x
- micro: 0.5x, light: 1.0x, medium: 1.5x, heavy: 2.0x
- 累计持仓价值不超过可用保证金的 15
- 保证金 = 持仓价值 / 杠杆
@ -380,11 +380,11 @@ class PaperTradingService:
# 根据 position_size 确定倍数(相对于可用保证金)
position_multiplier = {
'micro': 0.8,
'light': 1.2,
'medium': 2.0,
'heavy': 3.0
}.get(position_size, 1.2)
'micro': 0.5,
'light': 1.0,
'medium': 1.5,
'heavy': 2.0
}.get(position_size, 1.0)
# 最大累计持仓价值倍数
max_total_multiplier = 15.0