""" 实盘交易数据模型 与模拟交易使用相同的订单状态和方向枚举 """ from datetime import datetime from sqlalchemy import Column, Integer, String, Float, DateTime, JSON, Text, Enum as SQLEnum from app.models.database import Base from app.models.paper_trading import OrderStatus, OrderSide, SignalGrade, EntryType class RealOrder(Base): """实盘交易订单表""" __tablename__ = "real_orders" id = Column(Integer, primary_key=True, index=True) # 订单标识 order_id = Column(String(64), unique=True, nullable=False, index=True) # 本地订单ID exchange_order_id = Column(String(64), nullable=True, index=True) # 交易所订单ID client_order_id = Column(String(64), nullable=True, index=True) # 自定义订单ID # 交易对信息 symbol = Column(String(20), nullable=False, index=True) side = Column(SQLEnum(OrderSide), nullable=False) # 价格信息 entry_price = Column(Float, nullable=False) # 目标入场价 stop_loss = Column(Float, nullable=False) # 止损价 take_profit = Column(Float, nullable=False) # 止盈价 filled_price = Column(Float, nullable=True) # 实际成交价 exit_price = Column(Float, nullable=True) # 出场价 # 仓位信息 quantity = Column(Float, default=1000) # 仓位大小 (USDT) leverage = Column(Integer, default=10) # 杠杆倍数 size = Column(Float, nullable=True) # 合约数量(张数) # 信号信息 signal_grade = Column(SQLEnum(SignalGrade), default=SignalGrade.D) signal_type = Column(String(20), default="swing") # swing / short_term confidence = Column(Float, default=0) # 置信度 (0-100) trend = Column(String(20), nullable=True) # 趋势方向 entry_type = Column(SQLEnum(EntryType, values_callable=lambda x: [e.value for e in x]), default=EntryType.MARKET) # 订单状态 status = Column(SQLEnum(OrderStatus), default=OrderStatus.PENDING, index=True) # 盈亏信息 pnl_amount = Column(Float, default=0) # 盈亏金额 (USDT) pnl_percent = Column(Float, default=0) # 盈亏百分比 fee_amount = Column(Float, default=0) # 手续费 # 风险指标 max_drawdown = Column(Float, default=0) # 持仓期间最大回撤 max_profit = Column(Float, default=0) # 持仓期间最大盈利 # 移动止损相关 breakeven_triggered = Column(Integer, default=0) # 保本止损是否已触发 trailing_stop_triggered = Column(Integer, default=0) # 移动止损是否已触发 trailing_stop_base_profit = Column(Float, default=0) # 移动止损基准盈利 # 时间戳 created_at = Column(DateTime, default=datetime.now, index=True) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) filled_at = Column(DateTime, nullable=True) # 成交时间 closed_at = Column(DateTime, nullable=True) # 平仓时间 # 额外数据 extra_data = Column(JSON, nullable=True) # 存储额外信息(metadata是保留字) notes = Column(Text, nullable=True) # 备注 def to_dict(self) -> dict: """转换为字典""" return { 'id': self.id, 'order_id': self.order_id, 'exchange_order_id': self.exchange_order_id, 'client_order_id': self.client_order_id, 'symbol': self.symbol, 'side': self.side.value if self.side else None, 'entry_price': self.entry_price, 'stop_loss': self.stop_loss, 'take_profit': self.take_profit, 'filled_price': self.filled_price, 'exit_price': self.exit_price, 'quantity': self.quantity, 'leverage': self.leverage, 'size': self.size, 'signal_grade': self.signal_grade.value if self.signal_grade else None, 'signal_type': self.signal_type, 'confidence': self.confidence, 'trend': self.trend, 'entry_type': self.entry_type.value if self.entry_type else None, 'status': self.status.value if self.status else None, 'pnl_amount': self.pnl_amount, 'pnl_percent': self.pnl_percent, 'fee_amount': self.fee_amount, 'max_drawdown': self.max_drawdown, 'max_profit': self.max_profit, 'breakeven_triggered': bool(self.breakeven_triggered), 'trailing_stop_triggered': bool(self.trailing_stop_triggered), 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, 'filled_at': self.filled_at.isoformat() if self.filled_at else None, 'closed_at': self.closed_at.isoformat() if self.closed_at else None, 'extra_data': self.extra_data, 'notes': self.notes }