This commit is contained in:
aaron 2026-02-15 21:43:43 +08:00
parent 569747d8fd
commit 51565a4edf
3 changed files with 43 additions and 18 deletions

View File

@ -13,6 +13,7 @@ class OrderStatus(str, Enum):
OPEN = "open" # 持仓中
CLOSED_TP = "closed_tp" # 止盈平仓
CLOSED_SL = "closed_sl" # 止损平仓
CLOSED_BE = "closed_be" # 保本止损平仓
CLOSED_MANUAL = "closed_manual" # 手动平仓
CANCELLED = "cancelled" # 已取消
@ -77,6 +78,7 @@ class PaperOrder(Base):
# 风险指标
max_drawdown = Column(Float, default=0) # 持仓期间最大回撤
max_profit = Column(Float, default=0) # 持仓期间最大盈利
breakeven_triggered = Column(Integer, default=0) # 是否触发过保本止损0=否1=是)
# 时间戳
created_at = Column(DateTime, default=datetime.utcnow)
@ -111,6 +113,7 @@ class PaperOrder(Base):
'pnl_percent': self.pnl_percent,
'max_drawdown': self.max_drawdown,
'max_profit': self.max_profit,
'breakeven_triggered': self.breakeven_triggered,
'created_at': self.created_at.isoformat() if self.created_at else None,
'opened_at': self.opened_at.isoformat() if self.opened_at else None,
'closed_at': self.closed_at.isoformat() if self.closed_at else None,

View File

@ -431,6 +431,10 @@ class PaperTradingService:
exit_price = order.take_profit
elif current_price <= order.stop_loss:
triggered = True
# 通过标记判断是否是保本止损
if getattr(order, 'breakeven_triggered', 0) == 1:
new_status = OrderStatus.CLOSED_BE
else:
new_status = OrderStatus.CLOSED_SL
exit_price = order.stop_loss
else:
@ -441,6 +445,10 @@ class PaperTradingService:
exit_price = order.take_profit
elif current_price >= order.stop_loss:
triggered = True
# 通过标记判断是否是保本止损
if getattr(order, 'breakeven_triggered', 0) == 1:
new_status = OrderStatus.CLOSED_BE
else:
new_status = OrderStatus.CLOSED_SL
exit_price = order.stop_loss
@ -499,7 +507,7 @@ class PaperTradingService:
'signal_grade': db_order.signal_grade.value if db_order.signal_grade else None
}
status_text = "止盈" if status == OrderStatus.CLOSED_TP else "止损"
status_text = {"closed_tp": "止盈", "closed_sl": "止损", "closed_be": "保本止损"}.get(status.value, "平仓")
logger.info(f"订单{status_text}: {db_order.order_id} | {db_order.symbol} | 盈亏: {pnl_percent:+.2f}% (${pnl_amount:+.2f})")
return result
@ -534,11 +542,13 @@ class PaperTradingService:
# 保本止损逻辑:当盈利达到阈值时,将止损移动到开仓价
if self.breakeven_threshold > 0 and current_pnl_percent >= self.breakeven_threshold:
# 检查止损是否还没有移动到保本位
# 检查止损是否还没有移动到保本位(通过标记判断)
if getattr(order, 'breakeven_triggered', 0) != 1:
if order.side == OrderSide.LONG:
# 做多:止损应该低于开仓价,如果止损还在开仓价下方,则移动到开仓价
if order.stop_loss < order.filled_price:
order.stop_loss = order.filled_price
order.breakeven_triggered = 1
needs_update = True
breakeven_triggered = True
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
@ -546,6 +556,7 @@ class PaperTradingService:
# 做空:止损应该高于开仓价,如果止损还在开仓价上方,则移动到开仓价
if order.stop_loss > order.filled_price:
order.stop_loss = order.filled_price
order.breakeven_triggered = 1
needs_update = True
breakeven_triggered = True
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
@ -559,6 +570,7 @@ class PaperTradingService:
db_order.max_profit = order.max_profit
db_order.max_drawdown = order.max_drawdown
db_order.stop_loss = order.stop_loss
db_order.breakeven_triggered = order.breakeven_triggered
db.commit()
except Exception as e:
logger.error(f"更新订单极值失败: {e}")
@ -779,6 +791,7 @@ class PaperTradingService:
PaperOrder.status.in_([
OrderStatus.CLOSED_TP,
OrderStatus.CLOSED_SL,
OrderStatus.CLOSED_BE,
OrderStatus.CLOSED_MANUAL
])
)
@ -800,6 +813,7 @@ class PaperTradingService:
PaperOrder.status.in_([
OrderStatus.CLOSED_TP,
OrderStatus.CLOSED_SL,
OrderStatus.CLOSED_BE,
OrderStatus.CLOSED_MANUAL
])
)
@ -891,6 +905,7 @@ class PaperTradingService:
PaperOrder.status.in_([
OrderStatus.CLOSED_TP,
OrderStatus.CLOSED_SL,
OrderStatus.CLOSED_BE,
OrderStatus.CLOSED_MANUAL
])
).all()
@ -985,6 +1000,7 @@ class PaperTradingService:
PaperOrder.status.in_([
OrderStatus.CLOSED_TP,
OrderStatus.CLOSED_SL,
OrderStatus.CLOSED_BE,
OrderStatus.CLOSED_MANUAL
]),
PaperOrder.closed_at >= cutoff_time

View File

@ -221,6 +221,11 @@
color: #ff4444;
}
.status-badge.closed_be {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
}
.status-badge.closed_manual {
background: rgba(255, 165, 0, 0.1);
color: orange;
@ -1628,6 +1633,7 @@
'open': '持仓中',
'closed_tp': '止盈',
'closed_sl': '止损',
'closed_be': '保本止损',
'closed_manual': '手动平仓'
};
return map[status] || status;