diff --git a/app/db/paper_trading.py b/app/db/paper_trading.py index 2bf9fd1..7950aa6 100644 --- a/app/db/paper_trading.py +++ b/app/db/paper_trading.py @@ -35,28 +35,33 @@ def paper_trading_enabled() -> bool: return bool(paper_trading_config().get("enabled", True)) -def default_account_equity_usdt() -> float: - return max(1.0, _safe_float(paper_trading_config().get("account_equity_usdt"), 20000.0)) +def _paper_cfg(config: dict | None = None) -> dict: + return config if isinstance(config, dict) else paper_trading_config() -def default_leverage() -> float: - return max(1.0, _safe_float(paper_trading_config().get("trade_leverage"), 5.0)) +def default_account_equity_usdt(config: dict | None = None) -> float: + return max(1.0, _safe_float(_paper_cfg(config).get("account_equity_usdt"), 20000.0)) -def default_notional_usdt() -> float: - return max(1.0, _safe_float(paper_trading_config().get("trade_notional_usdt"), 5000.0)) +def default_leverage(config: dict | None = None) -> float: + return max(1.0, _safe_float(_paper_cfg(config).get("trade_leverage"), 5.0)) -def default_margin_usdt() -> float: - return round(default_notional_usdt() / default_leverage(), 8) +def default_notional_usdt(config: dict | None = None) -> float: + return max(1.0, _safe_float(_paper_cfg(config).get("trade_notional_usdt"), 5000.0)) -def default_fee_rate() -> float: - return max(0.0, _safe_float(paper_trading_config().get("fee_rate"), 0.001)) +def default_margin_usdt(config: dict | None = None) -> float: + cfg = _paper_cfg(config) + return round(default_notional_usdt(cfg) / default_leverage(cfg), 8) -def default_slippage_pct() -> float: - return max(0.0, _safe_float(paper_trading_config().get("slippage_pct"), 0.05)) +def default_fee_rate(config: dict | None = None) -> float: + 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: @@ -100,12 +105,12 @@ def _entry_plan(rec: dict) -> dict: return _loads_json(rec.get("entry_plan_json"), {}) -def _open_price(current_price: float) -> float: - return round(current_price * (1 + default_slippage_pct() / 100), 12) +def _open_price(current_price: float, config: dict | None = None) -> float: + return round(current_price * (1 + default_slippage_pct(config) / 100), 12) -def _close_price(current_price: float) -> float: - return round(current_price * (1 - default_slippage_pct() / 100), 12) +def _close_price(current_price: float, config: dict | None = None) -> float: + return round(current_price * (1 - default_slippage_pct(config) / 100), 12) 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) -def _account_return_pct(pnl_usdt: float, account_equity: float | None = None) -> float: - equity = max(1.0, _safe_float(account_equity, default_account_equity_usdt())) +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(config))) return round(_safe_float(pnl_usdt) / equity * 100, 4) -def _margin_roi_pct(pnl_usdt: float, margin_usdt: float) -> float: - margin = max(1.0, _safe_float(margin_usdt, default_margin_usdt())) +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(config))) 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")) if margin > 0: 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) -def _decorate_trade(trade: dict) -> dict: +def _decorate_trade(trade: dict, config: dict | None = None) -> dict: + cfg = _paper_cfg(config) item = dict(trade) - notional = _safe_float(item.get("notional_usdt"), default_notional_usdt()) - leverage = max(1.0, _safe_float(item.get("leverage"), default_leverage())) - margin = _trade_margin({"margin_usdt": item.get("margin_usdt"), "notional_usdt": notional, "leverage": leverage}) + notional = _safe_float(item.get("notional_usdt"), default_notional_usdt(cfg)) + 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}, cfg) unrealized = round(notional * _safe_float(item.get("pnl_pct")) / 100, 8) realized = _safe_float(item.get("realized_pnl_usdt")) effective_pnl = realized if item.get("status") == "closed" else unrealized @@ -144,9 +150,9 @@ def _decorate_trade(trade: dict) -> dict: item["leverage"] = leverage item["margin_usdt"] = margin item["unrealized_pnl_usdt"] = unrealized - item["margin_roi_pct"] = _margin_roi_pct(effective_pnl, margin) - item["account_return_pct"] = _account_return_pct(effective_pnl) - item["account_equity_usdt"] = default_account_equity_usdt() + item["margin_roi_pct"] = _margin_roi_pct(effective_pnl, margin, cfg) + item["account_return_pct"] = _account_return_pct(effective_pnl, config=cfg) + item["account_equity_usdt"] = default_account_equity_usdt(cfg) 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_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() finally: 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"] 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] @@ -518,7 +525,7 @@ def get_paper_trading_summary(days: int = 30) -> dict: total_pnl = round(total_realized + open_unrealized, 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) - initial_equity = default_account_equity_usdt() + initial_equity = default_account_equity_usdt(cfg) current_balance = round(initial_equity + total_pnl, 4) cumulative_leverage = round(open_position_value / current_balance, 4) if current_balance > 0 else 0 return { @@ -543,11 +550,11 @@ def get_paper_trading_summary(days: int = 30) -> dict: "open_position_value_usdt": open_position_value, "cumulative_leverage": cumulative_leverage, "available_equity_usdt": round(current_balance - allocated_margin, 4), - "margin_usdt": default_margin_usdt(), - "leverage": default_leverage(), - "notional_usdt": default_notional_usdt(), - "fee_rate": default_fee_rate(), - "slippage_pct": default_slippage_pct(), + "margin_usdt": default_margin_usdt(cfg), + "leverage": default_leverage(cfg), + "notional_usdt": default_notional_usdt(cfg), + "fee_rate": default_fee_rate(cfg), + "slippage_pct": default_slippage_pct(cfg), } @@ -576,8 +583,9 @@ def list_paper_trades(limit: int = 50, offset: int = 0, status: str = "") -> dic ).fetchall() finally: conn.close() + cfg = paper_trading_config() 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), "limit": limit, "offset": offset,