Files
eth_hedge_sim/backend/app/api/settings.py
T
dekun cce26e87b5 Add dual exit modes: fixed USDT or premium multiple.
Net PnL (after estimated close fees) drives auto close; Plan/Settings expose the choice.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 09:17:50 +08:00

113 lines
4.0 KiB
Python

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()