diff --git a/backend/api/routes/account.py b/backend/api/routes/account.py index 6e0ce3d..0d18e15 100644 --- a/backend/api/routes/account.py +++ b/backend/api/routes/account.py @@ -80,70 +80,71 @@ async def _ensure_exchange_sltp_for_symbol(symbol: str, account_id: int = 1): except Exception: mark_price = None - # 4) 从数据库 open trade 取止损/止盈价(优先取最近一条) + # 4) 止损/止盈价:优先从数据库 open trade 取;无 DB 记录时用币安持仓 + 配置比例计算(支持手动开仓补挂) from database.models import Trade open_trades = Trade.get_by_symbol(symbol, status='open', account_id=account_id) or [] - if not open_trades: - raise HTTPException(status_code=400, detail=f"{symbol} 数据库无 open 交易记录,无法确定止损止盈价") - # 尽量取最新一条 open trade(避免同一symbol多条 open 时取到旧记录) - try: - open_trades.sort(key=lambda x: int(x.get("id", 0) or 0), reverse=True) - except Exception: - pass - trade = open_trades[0] + sl = None + tp = None + entry_price = float(p0.get("entryPrice", 0) or 0) + qty = abs(float(amt)) + lv = float(p0.get("leverage", 0) or 0) or 10.0 - sl = trade.get("stop_loss_price") - tp = trade.get("take_profit_2") or trade.get("take_profit_price") or trade.get("take_profit_1") - try: - sl = float(sl) if sl is not None else None - except Exception: - sl = None - try: - tp = float(tp) if tp is not None else None - except Exception: - tp = None - - # 兼容旧数据库:如果 trades 表还没迁移 stop_loss_price / take_profit_price 字段, - # 则回退用 entry_price/quantity/leverage + 配置的 STOP_LOSS_PERCENT/TAKE_PROFIT_PERCENT 计算。 - if not sl or not tp: + def _ratio(v, default): try: + x = float(v) + if x > 1: + x = x / 100.0 + if x < 0: + x = default + return x + except Exception: + return default + + if open_trades: + try: + open_trades.sort(key=lambda x: int(x.get("id", 0) or 0), reverse=True) + except Exception: + pass + trade = open_trades[0] + sl = trade.get("stop_loss_price") + tp = trade.get("take_profit_2") or trade.get("take_profit_price") or trade.get("take_profit_1") + try: + sl = float(sl) if sl is not None else None + except Exception: + sl = None + try: + tp = float(tp) if tp is not None else None + except Exception: + tp = None + if entry_price <= 0 or qty <= 0: entry_price = float(trade.get("entry_price") or 0) qty = float(trade.get("quantity") or 0) - lv = float(trade.get("leverage") or 0) or float(p0.get("leverage") or 0) or 10.0 + lv = float(trade.get("leverage") or 0) or lv + + # 无 DB 记录或 DB 中 sl/tp 为空:用币安持仓的 entry/quantity/leverage + 配置比例计算 + if not sl or not tp: + try: if entry_price <= 0 or qty <= 0 or lv <= 0: raise ValueError("entry_price/quantity/leverage invalid") - - def _ratio(v, default): - try: - x = float(v) - # 兼容:若误存成 5(表示5%),则转为 0.05 - if x > 1: - x = x / 100.0 - if x < 0: - x = default - return x - except Exception: - return default - sl_pct = _ratio(TradingConfig.get_value("STOP_LOSS_PERCENT", 0.05), 0.05) tp_pct = _ratio(TradingConfig.get_value("TAKE_PROFIT_PERCENT", 0.15), 0.15) - notional = entry_price * qty margin = notional / lv sl_amount = margin * sl_pct tp_amount = margin * tp_pct - if side == "BUY": sl = entry_price - (sl_amount / qty) tp = entry_price + (tp_amount / qty) else: sl = entry_price + (sl_amount / qty) tp = entry_price - (tp_amount / qty) - if not sl or not tp or sl <= 0 or tp <= 0: raise ValueError("computed sl/tp invalid") - except Exception: - raise HTTPException(status_code=400, detail=f"{symbol} 数据库缺少止损/止盈价,且无法回退计算,无法补挂") + except Exception as e: + raise HTTPException( + status_code=400, + detail=f"{symbol} 无法确定止损止盈价(无 DB 记录且计算失败: {e}),请检查配置 STOP_LOSS_PERCENT/TAKE_PROFIT_PERCENT" + ) # 5) 取消旧的保护单(Algo 条件单),避免重复 try: