This commit is contained in:
aaron 2026-05-20 15:55:14 +08:00
parent ec9ec9f3cc
commit f6ae9c4ed6

View File

@ -35,28 +35,33 @@ def paper_trading_enabled() -> bool:
return bool(paper_trading_config().get("enabled", True)) return bool(paper_trading_config().get("enabled", True))
def default_account_equity_usdt() -> float: def _paper_cfg(config: dict | None = None) -> dict:
return max(1.0, _safe_float(paper_trading_config().get("account_equity_usdt"), 20000.0)) return config if isinstance(config, dict) else paper_trading_config()
def default_leverage() -> float: def default_account_equity_usdt(config: dict | None = None) -> float:
return max(1.0, _safe_float(paper_trading_config().get("trade_leverage"), 5.0)) return max(1.0, _safe_float(_paper_cfg(config).get("account_equity_usdt"), 20000.0))
def default_notional_usdt() -> float: def default_leverage(config: dict | None = None) -> float:
return max(1.0, _safe_float(paper_trading_config().get("trade_notional_usdt"), 5000.0)) return max(1.0, _safe_float(_paper_cfg(config).get("trade_leverage"), 5.0))
def default_margin_usdt() -> float: def default_notional_usdt(config: dict | None = None) -> float:
return round(default_notional_usdt() / default_leverage(), 8) return max(1.0, _safe_float(_paper_cfg(config).get("trade_notional_usdt"), 5000.0))
def default_fee_rate() -> float: def default_margin_usdt(config: dict | None = None) -> float:
return max(0.0, _safe_float(paper_trading_config().get("fee_rate"), 0.001)) cfg = _paper_cfg(config)
return round(default_notional_usdt(cfg) / default_leverage(cfg), 8)
def default_slippage_pct() -> float: def default_fee_rate(config: dict | None = None) -> float:
return max(0.0, _safe_float(paper_trading_config().get("slippage_pct"), 0.05)) return max(0.0, _safe_float(_paper_cfg(config).get("fee_rate"), 0.001))
def default_slippage_pct(config: dict | None = None) -> float:
return max(0.0, _safe_float(_paper_cfg(config).get("slippage_pct"), 0.05))
def _trailing_config() -> dict: def _trailing_config() -> dict:
@ -100,12 +105,12 @@ def _entry_plan(rec: dict) -> dict:
return _loads_json(rec.get("entry_plan_json"), {}) return _loads_json(rec.get("entry_plan_json"), {})
def _open_price(current_price: float) -> float: def _open_price(current_price: float, config: dict | None = None) -> float:
return round(current_price * (1 + default_slippage_pct() / 100), 12) return round(current_price * (1 + default_slippage_pct(config) / 100), 12)
def _close_price(current_price: float) -> float: def _close_price(current_price: float, config: dict | None = None) -> float:
return round(current_price * (1 - default_slippage_pct() / 100), 12) return round(current_price * (1 - default_slippage_pct(config) / 100), 12)
def _trade_pnl_pct(entry_price: float, current_price: float) -> float: def _trade_pnl_pct(entry_price: float, current_price: float) -> float:
@ -114,29 +119,30 @@ def _trade_pnl_pct(entry_price: float, current_price: float) -> float:
return round((current_price / entry_price - 1) * 100, 4) return round((current_price / entry_price - 1) * 100, 4)
def _account_return_pct(pnl_usdt: float, account_equity: float | None = None) -> float: def _account_return_pct(pnl_usdt: float, account_equity: float | None = None, config: dict | None = None) -> float:
equity = max(1.0, _safe_float(account_equity, default_account_equity_usdt())) equity = max(1.0, _safe_float(account_equity, default_account_equity_usdt(config)))
return round(_safe_float(pnl_usdt) / equity * 100, 4) return round(_safe_float(pnl_usdt) / equity * 100, 4)
def _margin_roi_pct(pnl_usdt: float, margin_usdt: float) -> float: def _margin_roi_pct(pnl_usdt: float, margin_usdt: float, config: dict | None = None) -> float:
margin = max(1.0, _safe_float(margin_usdt, default_margin_usdt())) margin = max(1.0, _safe_float(margin_usdt, default_margin_usdt(config)))
return round(_safe_float(pnl_usdt) / margin * 100, 4) return round(_safe_float(pnl_usdt) / margin * 100, 4)
def _trade_margin(trade: dict) -> float: def _trade_margin(trade: dict, config: dict | None = None) -> float:
margin = _safe_float(trade.get("margin_usdt")) margin = _safe_float(trade.get("margin_usdt"))
if margin > 0: if margin > 0:
return margin return margin
leverage = max(1.0, _safe_float(trade.get("leverage"), default_leverage())) leverage = max(1.0, _safe_float(trade.get("leverage"), default_leverage(config)))
return round(_safe_float(trade.get("notional_usdt")) / leverage, 8) return round(_safe_float(trade.get("notional_usdt")) / leverage, 8)
def _decorate_trade(trade: dict) -> dict: def _decorate_trade(trade: dict, config: dict | None = None) -> dict:
cfg = _paper_cfg(config)
item = dict(trade) item = dict(trade)
notional = _safe_float(item.get("notional_usdt"), default_notional_usdt()) notional = _safe_float(item.get("notional_usdt"), default_notional_usdt(cfg))
leverage = max(1.0, _safe_float(item.get("leverage"), default_leverage())) leverage = max(1.0, _safe_float(item.get("leverage"), default_leverage(cfg)))
margin = _trade_margin({"margin_usdt": item.get("margin_usdt"), "notional_usdt": notional, "leverage": leverage}) margin = _trade_margin({"margin_usdt": item.get("margin_usdt"), "notional_usdt": notional, "leverage": leverage}, cfg)
unrealized = round(notional * _safe_float(item.get("pnl_pct")) / 100, 8) unrealized = round(notional * _safe_float(item.get("pnl_pct")) / 100, 8)
realized = _safe_float(item.get("realized_pnl_usdt")) realized = _safe_float(item.get("realized_pnl_usdt"))
effective_pnl = realized if item.get("status") == "closed" else unrealized effective_pnl = realized if item.get("status") == "closed" else unrealized
@ -144,9 +150,9 @@ def _decorate_trade(trade: dict) -> dict:
item["leverage"] = leverage item["leverage"] = leverage
item["margin_usdt"] = margin item["margin_usdt"] = margin
item["unrealized_pnl_usdt"] = unrealized item["unrealized_pnl_usdt"] = unrealized
item["margin_roi_pct"] = _margin_roi_pct(effective_pnl, margin) item["margin_roi_pct"] = _margin_roi_pct(effective_pnl, margin, cfg)
item["account_return_pct"] = _account_return_pct(effective_pnl) item["account_return_pct"] = _account_return_pct(effective_pnl, config=cfg)
item["account_equity_usdt"] = default_account_equity_usdt() item["account_equity_usdt"] = default_account_equity_usdt(cfg)
latest_market = _safe_float(item.get("latest_market_price")) latest_market = _safe_float(item.get("latest_market_price"))
item["latest_price"] = latest_market if latest_market > 0 else _safe_float(item.get("current_price")) item["latest_price"] = latest_market if latest_market > 0 else _safe_float(item.get("current_price"))
item["latest_price_updated_at"] = item.get("latest_market_price_updated_at") or item.get("updated_at") or "" item["latest_price_updated_at"] = item.get("latest_market_price_updated_at") or item.get("updated_at") or ""
@ -507,7 +513,8 @@ def get_paper_trading_summary(days: int = 30) -> dict:
).fetchall() ).fetchall()
finally: finally:
conn.close() conn.close()
items = [_decorate_trade(dict(r)) for r in rows] cfg = paper_trading_config()
items = [_decorate_trade(dict(r), cfg) for r in rows]
open_items = [x for x in items if x.get("status") == "open"] open_items = [x for x in items if x.get("status") == "open"]
closed_items = [x for x in items if x.get("status") == "closed"] closed_items = [x for x in items if x.get("status") == "closed"]
wins = [x for x in closed_items if _safe_float(x.get("realized_pnl_pct")) > 0] wins = [x for x in closed_items if _safe_float(x.get("realized_pnl_pct")) > 0]
@ -518,7 +525,7 @@ def get_paper_trading_summary(days: int = 30) -> dict:
total_pnl = round(total_realized + open_unrealized, 4) total_pnl = round(total_realized + open_unrealized, 4)
allocated_margin = round(sum(_safe_float(x.get("margin_usdt")) for x in open_items), 4) allocated_margin = round(sum(_safe_float(x.get("margin_usdt")) for x in open_items), 4)
open_position_value = round(sum(_safe_float(x.get("notional_usdt")) for x in open_items), 4) open_position_value = round(sum(_safe_float(x.get("notional_usdt")) for x in open_items), 4)
initial_equity = default_account_equity_usdt() initial_equity = default_account_equity_usdt(cfg)
current_balance = round(initial_equity + total_pnl, 4) current_balance = round(initial_equity + total_pnl, 4)
cumulative_leverage = round(open_position_value / current_balance, 4) if current_balance > 0 else 0 cumulative_leverage = round(open_position_value / current_balance, 4) if current_balance > 0 else 0
return { return {
@ -543,11 +550,11 @@ def get_paper_trading_summary(days: int = 30) -> dict:
"open_position_value_usdt": open_position_value, "open_position_value_usdt": open_position_value,
"cumulative_leverage": cumulative_leverage, "cumulative_leverage": cumulative_leverage,
"available_equity_usdt": round(current_balance - allocated_margin, 4), "available_equity_usdt": round(current_balance - allocated_margin, 4),
"margin_usdt": default_margin_usdt(), "margin_usdt": default_margin_usdt(cfg),
"leverage": default_leverage(), "leverage": default_leverage(cfg),
"notional_usdt": default_notional_usdt(), "notional_usdt": default_notional_usdt(cfg),
"fee_rate": default_fee_rate(), "fee_rate": default_fee_rate(cfg),
"slippage_pct": default_slippage_pct(), "slippage_pct": default_slippage_pct(cfg),
} }
@ -576,8 +583,9 @@ def list_paper_trades(limit: int = 50, offset: int = 0, status: str = "") -> dic
).fetchall() ).fetchall()
finally: finally:
conn.close() conn.close()
cfg = paper_trading_config()
return { return {
"items": [_decorate_trade(dict(r)) for r in rows], "items": [_decorate_trade(dict(r), cfg) for r in rows],
"total": int(total or 0), "total": int(total or 0),
"limit": limit, "limit": limit,
"offset": offset, "offset": offset,