stock-ai-agent/backend/app/models/real_trading.py

110 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
实盘交易数据模型
与模拟交易使用相同的订单状态和方向枚举
"""
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
}