Extend semi-auto with ITM/ATM/OTM, offset, and 1:4 sizing.

Moneyness and perp:option units are plan-only; OTM enforces leverage >=180.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-08 11:42:57 +08:00
parent 4585dba3c3
commit 457838ef64
17 changed files with 428 additions and 41 deletions
+4
View File
@@ -298,6 +298,10 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
"semi_net_exit_target": st.get("semi_net_exit_target"),
"semi_min_option_hours": st.get("semi_min_option_hours"),
"semi_min_option_leverage": st.get("semi_min_option_leverage"),
"semi_moneyness": st.get("semi_moneyness"),
"semi_otm_max_offset": st.get("semi_otm_max_offset"),
"semi_perp_unit": st.get("semi_perp_unit"),
"semi_option_unit": st.get("semi_option_unit"),
"leverage": _pick("leverage", float(settings.leverage)),
"min_option_leverage": _pick(
"min_option_leverage", float(settings.min_option_leverage)
+4
View File
@@ -45,6 +45,10 @@ class SemiParamsBody(BaseModel):
semi_perp_exit_unit: float | None = Field(default=None, ge=0.1, le=1_000_000)
semi_min_option_hours: float | None = Field(default=None, ge=1, le=720)
semi_min_option_leverage: float | None = Field(default=None, ge=1, le=10000)
semi_moneyness: Literal["itm", "atm", "otm"] | None = None
semi_otm_max_offset: float | None = Field(default=None, ge=1, le=5000)
semi_perp_unit: float | None = Field(default=None, ge=0.01, le=100)
semi_option_unit: float | None = Field(default=None, ge=0.01, le=100)
@router.post("/semi/arm")
+34
View File
@@ -52,6 +52,10 @@ KEYS = (
"semi_perp_exit_unit",
"semi_min_option_hours",
"semi_min_option_leverage",
"semi_moneyness",
"semi_otm_max_offset",
"semi_perp_unit",
"semi_option_unit",
"close_bid_mark_max_pct",
"residual_min_premium_pct",
"residual_close_check_sec",
@@ -109,6 +113,10 @@ class StrategySettingsBody(BaseModel):
semi_perp_exit_unit: float | None = Field(default=None, ge=0.1, le=1_000_000)
semi_min_option_hours: float | None = Field(default=None, ge=1, le=720)
semi_min_option_leverage: float | None = Field(default=None, ge=1, le=10000)
semi_moneyness: str | None = Field(default=None, pattern="^(itm|atm|otm)$")
semi_otm_max_offset: float | None = Field(default=None, ge=1, le=5000)
semi_perp_unit: float | None = Field(default=None, ge=0.01, le=100)
semi_option_unit: float | None = Field(default=None, ge=0.01, le=100)
close_bid_mark_max_pct: float | None = Field(default=None, ge=1, le=100)
residual_min_premium_pct: float | None = Field(default=None, ge=1, le=100)
residual_close_check_sec: int | None = Field(default=None, ge=30, le=86400)
@@ -296,6 +304,28 @@ def _read_settings() -> dict:
)
or s.semi_min_option_leverage
),
"semi_moneyness": (
lambda m: m
if m in ("itm", "atm", "otm")
else "otm"
)(
str(
db.get_setting("semi_moneyness", s.semi_moneyness) or s.semi_moneyness
)
.strip()
.lower()
),
"semi_otm_max_offset": float(
db.get_setting("semi_otm_max_offset", str(s.semi_otm_max_offset))
or s.semi_otm_max_offset
),
"semi_perp_unit": float(
db.get_setting("semi_perp_unit", str(s.semi_perp_unit)) or s.semi_perp_unit
),
"semi_option_unit": float(
db.get_setting("semi_option_unit", str(s.semi_option_unit))
or s.semi_option_unit
),
"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
@@ -555,6 +585,10 @@ async def put_strategy_settings(
"semi_perp_exit_unit",
"semi_min_option_hours",
"semi_min_option_leverage",
"semi_moneyness",
"semi_otm_max_offset",
"semi_perp_unit",
"semi_option_unit",
"semi_auto_enabled",
)
hit = [k for k in locked_keys if k in data]