This commit is contained in:
aaron 2026-02-28 19:34:12 +08:00
parent 9703a5a7da
commit 1d329a816c
4 changed files with 31 additions and 31 deletions

View File

@ -13,7 +13,7 @@ from app.services.db_service import db_service
from app.utils.logger import logger from app.utils.logger import logger
router = APIRouter(prefix="/api/paper-trading", tags=["模拟交易"]) router = APIRouter(prefix="/api/paper-trading", tags=["交易"])
class CloseOrderRequest(BaseModel): class CloseOrderRequest(BaseModel):
@ -402,11 +402,11 @@ async def reset_paper_trading():
return { return {
"success": True, "success": True,
"message": f"模拟交易数据已重置,删除 {result['deleted_count']} 条订单", "message": f"交易数据已重置,删除 {result['deleted_count']} 条订单",
"result": result "result": result
} }
except Exception as e: except Exception as e:
logger.error(f"重置模拟交易数据失败: {e}") logger.error(f"重置交易数据失败: {e}")
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))

View File

@ -284,7 +284,7 @@ class CryptoAgent:
logger.info(f" 监控交易对: {', '.join(self.symbols)}") logger.info(f" 监控交易对: {', '.join(self.symbols)}")
logger.info(f" 运行模式: 每5分钟整点执行") logger.info(f" 运行模式: 每5分钟整点执行")
logger.info(f" 分析引擎: LLM 自主分析") logger.info(f" 分析引擎: LLM 自主分析")
logger.info(f" 模拟交易: 已启用") logger.info(f" 交易模式: 自动交易已启用")
logger.info("=" * 60 + "\n") logger.info("=" * 60 + "\n")
# 更新状态为运行中 # 更新状态为运行中
@ -292,7 +292,7 @@ class CryptoAgent:
# 注意:不再启动独立的价格监控 # 注意:不再启动独立的价格监控
# 价格监控由 main.py 中的 price_monitor_loop 统一处理,避免重复检查 # 价格监控由 main.py 中的 price_monitor_loop 统一处理,避免重复检查
logger.info(f"模拟交易已启用(由后台统一监控)") logger.info(f"交易已启用(由后台统一监控)")
# 发送启动通知(卡片格式) # 发送启动通知(卡片格式)
title = "🚀 加密货币智能体已启动" title = "🚀 加密货币智能体已启动"
@ -560,9 +560,9 @@ class CryptoAgent:
paper_decision = None paper_decision = None
real_decision = None real_decision = None
# 模拟交易决策 # 交易决策
if paper_trading_enabled: if paper_trading_enabled:
logger.info(f"\n📊 【模拟交易决策】") logger.info(f"\n📊 【交易决策】")
positions, account, pending_orders = self._get_trading_state(use_real_trading=False) positions, account, pending_orders = self._get_trading_state(use_real_trading=False)
# 过滤只传递当前symbol的挂单给决策器避免LLM搞混 # 过滤只传递当前symbol的挂单给决策器避免LLM搞混
pending_orders_for_symbol = [o for o in pending_orders if o.get('symbol') == symbol] pending_orders_for_symbol = [o for o in pending_orders if o.get('symbol') == symbol]
@ -573,7 +573,7 @@ class CryptoAgent:
# 发送交易决策通知 # 发送交易决策通知
await self._send_trading_decision_notification(paper_decision, market_signal, current_price, is_paper=True) await self._send_trading_decision_notification(paper_decision, market_signal, current_price, is_paper=True)
else: else:
logger.info(f"⏸️ 模拟交易未启用") logger.info(f"⏸️ 交易未启用")
# 实盘交易决策 # 实盘交易决策
if real_trading_enabled: if real_trading_enabled:
@ -779,22 +779,22 @@ class CryptoAgent:
real_executed = False real_executed = False
# ============================================================ # ============================================================
# 执行模拟交易决策 # 执行交易决策
# ============================================================ # ============================================================
if paper_trading_enabled and paper_decision: if paper_trading_enabled and paper_decision:
decision_type = paper_decision.get('decision', 'HOLD') decision_type = paper_decision.get('decision', 'HOLD')
if decision_type == 'HOLD': if decision_type == 'HOLD':
reasoning = paper_decision.get('reasoning', '观望') reasoning = paper_decision.get('reasoning', '观望')
logger.info(f"\n📊 模拟交易: {reasoning}") logger.info(f"\n📊 交易决策: {reasoning}")
# 有信号但决策为 HOLD发送未执行通知 # 有信号但决策为 HOLD发送未执行通知
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True) await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True)
else: else:
logger.info(f"\n📊 【执行模拟交易】") logger.info(f"\n📊 【执行交易】")
if decision_type in ['OPEN', 'ADD']: if decision_type in ['OPEN', 'ADD']:
# 先执行交易 # 先执行交易
logger.info(f" 准备执行 {'模拟' if paper_trading_enabled else '实盘'} 交易...") logger.info(f" 准备执行交易...")
result = await self._execute_paper_trade(paper_decision, market_signal, current_price) result = await self._execute_paper_trade(paper_decision, market_signal, current_price)
# 检查是否成功执行 # 检查是否成功执行
@ -815,9 +815,9 @@ class CryptoAgent:
else: else:
# 有信号但订单创建失败,发送未执行通知 # 有信号但订单创建失败,发送未执行通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败' reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 模拟交易未执行: {reason}") 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" ⚠️ 模拟交易未执行: {reason}") 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 操作也发送执行通知 # CLOSE 操作也发送执行通知
@ -1073,7 +1073,7 @@ class CryptoAgent:
symbol = market_signal.get('symbol') symbol = market_signal.get('symbol')
# 账户类型标识 # 账户类型标识
account_type = "📊 模拟" if is_paper else "💰 实盘" account_type = "📊 交易" if is_paper else "💰 实盘"
# 决策类型映射 # 决策类型映射
decision_map = { decision_map = {
@ -1342,7 +1342,7 @@ class CryptoAgent:
logger.warning(f" ⚠️ LLM 决策的 quantity 无效: {quantity},使用默认值") logger.warning(f" ⚠️ LLM 决策的 quantity 无效: {quantity},使用默认值")
quantity = self._calculate_quantity_by_position_size(position_size, real_trading=False) quantity = self._calculate_quantity_by_position_size(position_size, real_trading=False)
logger.info(f" 准备创建模拟订单: {symbol} {action} {position_size}") logger.info(f" 准备创建订单: {symbol} {action} {position_size}")
logger.info(f" LLM 决策金额: ${quantity:.2f} USDT") logger.info(f" LLM 决策金额: ${quantity:.2f} USDT")
# 转换决策的 action 为 paper_trading 期望的格式 # 转换决策的 action 为 paper_trading 期望的格式
@ -1381,13 +1381,13 @@ class CryptoAgent:
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}") logger.info(f" 订单状态: {order.status.value} | 入场价: ${order.entry_price:,.2f}")
else: else:
# 订单创建失败,记录详细原因 # 订单创建失败,记录详细原因
reason = result.get('message', '未知原因') reason = result.get('message', '未知原因')
cancelled_info = result.get('cancelled_orders', []) cancelled_info = result.get('cancelled_orders', [])
logger.warning(f" ❌ 创建模拟订单失败: {reason}") logger.warning(f" ❌ 创建订单失败: {reason}")
if cancelled_info: if cancelled_info:
logger.warning(f" 已取消的反向订单: {len(cancelled_info)}") logger.warning(f" 已取消的反向订单: {len(cancelled_info)}")
@ -1395,7 +1395,7 @@ class CryptoAgent:
return result return result
except Exception as e: except Exception as e:
logger.error(f"执行模拟交易失败: {e}") logger.error(f"执行交易失败: {e}")
import traceback import traceback
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
@ -1499,13 +1499,13 @@ class CryptoAgent:
symbol = decision.get('symbol') symbol = decision.get('symbol')
if paper_trading: if paper_trading:
# 模拟平仓 # 平仓
if self.paper_trading: if self.paper_trading:
# TODO: 实现模拟平仓逻辑 # TODO: 实现平仓逻辑
logger.info(f" 🔒 模拟平仓: {symbol}") logger.info(f" 🔒 平仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}") logger.info(f" 理由: {decision.get('reasoning', '')}")
else: else:
logger.warning(f" 模拟交易服务未初始化") logger.warning(f" 交易服务未初始化")
else: else:
# 实盘平仓 # 实盘平仓
if self.real_trading and self.real_trading.get_auto_trading_status(): if self.real_trading and self.real_trading.get_auto_trading_status():

View File

@ -454,7 +454,7 @@ async def _print_system_status():
logger.info(f" 监控: {', '.join(config['symbols'])}") logger.info(f" 监控: {', '.join(config['symbols'])}")
if 'paper_trading_enabled' in config: if 'paper_trading_enabled' in config:
pt_status = "已启用" if config['paper_trading_enabled'] else "未启用" pt_status = "已启用" if config['paper_trading_enabled'] else "未启用"
logger.info(f" 模拟交易: {pt_status}") logger.info(f" 交易: {pt_status}")
if 'analysis_interval' in config: if 'analysis_interval' in config:
logger.info(f" 分析间隔: {config['analysis_interval']}") logger.info(f" 分析间隔: {config['analysis_interval']}")
@ -651,7 +651,7 @@ app.include_router(chat.router, prefix="/api/chat", tags=["对话"])
app.include_router(stock.router, prefix="/api/stock", tags=["股票数据"]) app.include_router(stock.router, prefix="/api/stock", tags=["股票数据"])
app.include_router(skills.router, prefix="/api/skills", tags=["技能管理"]) app.include_router(skills.router, prefix="/api/skills", tags=["技能管理"])
app.include_router(llm.router, tags=["LLM模型"]) app.include_router(llm.router, tags=["LLM模型"])
app.include_router(paper_trading.router, tags=["模拟交易"]) app.include_router(paper_trading.router, tags=["交易"])
app.include_router(real_trading.router, tags=["实盘交易"]) app.include_router(real_trading.router, tags=["实盘交易"])
app.include_router(stocks.router, prefix="/api/stocks", tags=["美股分析"]) app.include_router(stocks.router, prefix="/api/stocks", tags=["美股分析"])
app.include_router(signals.router, tags=["信号管理"]) app.include_router(signals.router, tags=["信号管理"])

View File

@ -56,7 +56,7 @@ class PaperTradingService:
# 加载活跃订单到内存 # 加载活跃订单到内存
self._load_active_orders() self._load_active_orders()
logger.info(f"模拟交易服务初始化完成(自动平反向持仓: {'启用' if self.auto_close_opposite else '禁用'}" logger.info(f"交易服务初始化完成(自动平反向持仓: {'启用' if self.auto_close_opposite else '禁用'}"
f"保本止损阈值: {self.breakeven_threshold}%" f"保本止损阈值: {self.breakeven_threshold}%"
f"移动止损: {'启用' if self.trailing_stop_enabled else '禁用'}" f"移动止损: {'启用' if self.trailing_stop_enabled else '禁用'}"
f"触发倍数: {self.trailing_stop_threshold_multiplier}x" f"触发倍数: {self.trailing_stop_threshold_multiplier}x"
@ -332,13 +332,13 @@ class PaperTradingService:
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}") logger.error(f" 订单数据: symbol={symbol}, side={side}, entry_price={entry_price}, margin={margin}")
import traceback import traceback
logger.error(f" 异常详情: {traceback.format_exc()}") logger.error(f" 异常详情: {traceback.format_exc()}")
@ -1676,7 +1676,7 @@ class PaperTradingService:
# 构建报告 # 构建报告
lines = [ lines = [
f"📊 <b>模拟交易 {hours} 小时报告</b>", f"📊 <b>交易 {hours} 小时报告</b>",
"", "",
"━━━━━━ 总体情况 ━━━━━━", "━━━━━━ 总体情况 ━━━━━━",
f"总交易数: {total_stats['total_trades']} | 胜率: {total_stats['win_rate']}%", f"总交易数: {total_stats['total_trades']} | 胜率: {total_stats['win_rate']}%",
@ -2033,7 +2033,7 @@ class PaperTradingService:
# 清空内存缓存 # 清空内存缓存
self.active_orders.clear() self.active_orders.clear()
logger.info(f"模拟交易数据已重置,删除 {deleted} 条订单(总计 {total_count} 条)") logger.info(f"交易数据已重置,删除 {deleted} 条订单(总计 {total_count} 条)")
return { return {
'deleted_count': deleted, 'deleted_count': deleted,
@ -2041,7 +2041,7 @@ class PaperTradingService:
} }
except Exception as e: except Exception as e:
db.rollback() db.rollback()
logger.error(f"重置模拟交易数据失败: {e}") logger.error(f"重置交易数据失败: {e}")
raise raise
finally: finally:
db.close() db.close()