from __future__ import annotations from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel, Field from ..config import get_settings from ..models.db import get_db from ..sim.ledger import Ledger from .auth import require_user router = APIRouter(prefix="/api/settings", tags=["settings"]) KEYS = ( "fee_rate", "exit_move_pct", "exit_mode", "net_profit_target", "premium_exit_multiple", "rest_seconds", "initial_equity", "leverage", "min_option_hours", "min_option_leverage", "close_bid_mark_max_pct", "perp_qty_eth", "option_qty_eth", ) class StrategySettingsBody(BaseModel): fee_rate: float | None = Field(default=None, ge=0, le=0.05) exit_move_pct: float | None = Field(default=None, ge=0.1, le=50) exit_mode: str | None = Field(default=None, pattern="^(fixed_usdt|premium_multiple)$") net_profit_target: float | None = Field(default=None, ge=0.1, le=1_000_000) premium_exit_multiple: float | None = Field(default=None, ge=0.1, le=100) rest_seconds: int | None = Field(default=None, ge=0, le=3600) initial_equity: float | None = Field(default=None, ge=1000) leverage: float | None = Field(default=None, ge=1, le=125) min_option_hours: float | None = Field(default=None, ge=1, le=720) min_option_leverage: float | None = Field(default=None, ge=1, le=10000) close_bid_mark_max_pct: float | None = Field(default=None, ge=1, le=100) perp_qty_eth: float | None = Field(default=None, ge=0.01, le=100) option_qty_eth: float | None = Field(default=None, ge=0.01, le=100) def _read_settings() -> dict: db = get_db() s = get_settings() mode = str(db.get_setting("exit_mode", s.exit_mode) or s.exit_mode) if mode not in ("fixed_usdt", "premium_multiple"): mode = "fixed_usdt" return { "fee_rate": float(db.get_setting("fee_rate", str(s.fee_rate)) or s.fee_rate), "exit_move_pct": float( db.get_setting("exit_move_pct", str(s.exit_move_pct)) or s.exit_move_pct ), "exit_mode": mode, "net_profit_target": float( db.get_setting("net_profit_target", str(s.net_profit_target)) or s.net_profit_target ), "premium_exit_multiple": float( db.get_setting("premium_exit_multiple", str(s.premium_exit_multiple)) or s.premium_exit_multiple ), "rest_seconds": int( float(db.get_setting("rest_seconds", str(s.rest_seconds)) or s.rest_seconds) ), "initial_equity": float( db.get_setting("initial_equity", str(s.initial_equity)) or s.initial_equity ), "leverage": float(db.get_setting("leverage", str(s.leverage)) or s.leverage), "min_option_hours": float( db.get_setting("min_option_hours", str(s.min_option_hours)) or s.min_option_hours ), "min_option_leverage": float( db.get_setting("min_option_leverage", str(s.min_option_leverage)) or s.min_option_leverage ), "close_bid_mark_max_pct": float( db.get_setting("close_bid_mark_max_pct", str(s.close_bid_mark_max_pct)) or s.close_bid_mark_max_pct ), "perp_qty_eth": float( db.get_setting("perp_qty_eth", str(s.perp_qty_eth)) or s.perp_qty_eth ), "option_qty_eth": float( db.get_setting("option_qty_eth", str(s.option_qty_eth)) or s.option_qty_eth ), "ledger": Ledger(db).snapshot(), } @router.get("/strategy") async def get_strategy_settings(_user: Annotated[str, Depends(require_user)]) -> dict: return _read_settings() @router.put("/strategy") async def put_strategy_settings( body: StrategySettingsBody, _user: Annotated[str, Depends(require_user)], ) -> dict: db = get_db() data = body.model_dump(exclude_none=True) for k, v in data.items(): if k in KEYS: db.set_setting(k, str(v)) return _read_settings()