Support LIVE exchange funds and perp margin mode.
Hide sim equity when LIVE is selected; OKX funds bar uses exchange balances; add cross/isolated for perp only (options stay cash). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -115,9 +115,13 @@ async def funds_summary(_user: Annotated[str, Depends(require_user)]) -> dict[st
|
||||
trading_usdt = bal.get("trading_usdt")
|
||||
funding_usdc = bal.get("funding_usdc")
|
||||
trading_usdc = bal.get("trading_usdc")
|
||||
parts = [funding_usdt, trading_usdt, funding_usdc, trading_usdc]
|
||||
vals = [float(x) for x in parts if x is not None]
|
||||
total = round(sum(vals), 2) if vals else None
|
||||
r = float(rate) if rate and rate > 0 else 1.0
|
||||
total = round(
|
||||
(funding_usdt or 0.0)
|
||||
+ (trading_usdt or 0.0)
|
||||
+ ((funding_usdc or 0.0) + (trading_usdc or 0.0)) * r,
|
||||
2,
|
||||
)
|
||||
except Exception as e:
|
||||
return {
|
||||
"ok": False,
|
||||
@@ -128,11 +132,13 @@ async def funds_summary(_user: Annotated[str, Depends(require_user)]) -> dict[st
|
||||
finally:
|
||||
client.close()
|
||||
else:
|
||||
# 非 OKX LIVE:回退本地账本
|
||||
led = Ledger(db).snapshot()
|
||||
trading_usdt = float(led["equity"])
|
||||
total = trading_usdt
|
||||
|
||||
# 非 OKX LIVE:暂无统一资金接口,不回退模拟账本(避免实盘显示假资金)
|
||||
return {
|
||||
"ok": False,
|
||||
"mode": mode,
|
||||
"exchange": exchange,
|
||||
"detail": f"{exchange} 实盘资金摘要暂未接入,请用 OKX 或交易所 App 查看",
|
||||
}
|
||||
return {
|
||||
"ok": True,
|
||||
"mode": mode,
|
||||
|
||||
@@ -37,6 +37,7 @@ KEYS = (
|
||||
"skip_weekends",
|
||||
"initial_equity",
|
||||
"leverage",
|
||||
"perp_margin_mode",
|
||||
"min_option_hours",
|
||||
"min_option_leverage",
|
||||
"atm_open_offset_enabled",
|
||||
@@ -61,6 +62,7 @@ class StrategySettingsBody(BaseModel):
|
||||
skip_weekends: bool | None = None
|
||||
initial_equity: float | None = Field(default=None, ge=1000, le=10_000_000)
|
||||
leverage: float | None = Field(default=None, ge=1, le=125)
|
||||
perp_margin_mode: str | None = Field(default=None, pattern="^(cross|isolated)$")
|
||||
min_option_hours: float | None = Field(default=None, ge=1, le=720)
|
||||
min_option_leverage: float | None = Field(default=None, ge=1, le=10000)
|
||||
atm_open_offset_enabled: bool | None = None
|
||||
@@ -117,6 +119,20 @@ def _read_settings() -> dict:
|
||||
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),
|
||||
"perp_margin_mode": (
|
||||
mm
|
||||
if (
|
||||
mm := str(
|
||||
db.get_setting("perp_margin_mode", s.perp_margin_mode)
|
||||
or s.perp_margin_mode
|
||||
or "cross"
|
||||
)
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
in ("cross", "isolated")
|
||||
else "cross"
|
||||
),
|
||||
"min_option_hours": float(
|
||||
db.get_setting("min_option_hours", str(s.min_option_hours))
|
||||
or s.min_option_hours
|
||||
|
||||
@@ -68,6 +68,8 @@ class Settings(BaseSettings):
|
||||
live_order_interval_sec: float = 1.0 # LIVE 私有下单/查单最小间隔(秒)
|
||||
skip_weekends: bool = True # 上海时区周六日禁止新开仓(已有仓仍可平)
|
||||
leverage: float = 3.0 # 永续杠杆
|
||||
# 永续保证金模式:cross=全仓(默认)| isolated=逐仓;期权仍固定 cash(OKX 逐仓/现金)
|
||||
perp_margin_mode: str = "cross"
|
||||
min_option_hours: float = 12.0 # 期权最小剩余小时
|
||||
min_option_leverage: float = 100.0 # 现价/卖一权利金 下限
|
||||
atm_open_offset_enabled: bool = False # 开仓 ATM 偏差限制开关(默认关)
|
||||
|
||||
@@ -37,6 +37,16 @@ class OkxLiveExecutor(Matcher):
|
||||
self._trade = OkxTradeClient()
|
||||
return self._trade
|
||||
|
||||
def _perp_margin_mode(self) -> str:
|
||||
"""永续全仓/逐仓;期权始终 cash,不受此设置影响。"""
|
||||
s = get_settings()
|
||||
raw = str(
|
||||
self.ledger.get_setting_str("perp_margin_mode", s.perp_margin_mode)
|
||||
or s.perp_margin_mode
|
||||
or "cross"
|
||||
).strip().lower()
|
||||
return "isolated" if raw == "isolated" else "cross"
|
||||
|
||||
def _guard_live(self) -> str | None:
|
||||
ok, reason = live_ready()
|
||||
if not ok:
|
||||
@@ -145,9 +155,10 @@ class OkxLiveExecutor(Matcher):
|
||||
else:
|
||||
side, pos_side = "sell", "short"
|
||||
leverage = self.ledger.get_setting_float("leverage", s.leverage)
|
||||
mgn = self._perp_margin_mode()
|
||||
try:
|
||||
client.set_leverage(
|
||||
perp_inst, leverage, mgn_mode="cross", pos_side=pos_side
|
||||
perp_inst, leverage, mgn_mode=mgn, pos_side=pos_side
|
||||
)
|
||||
except Exception as e_lev:
|
||||
logger.warning("okx set_leverage failed: %s", e_lev)
|
||||
@@ -155,7 +166,7 @@ class OkxLiveExecutor(Matcher):
|
||||
inst_id=perp_inst,
|
||||
side=side,
|
||||
sz=str(perp_sz),
|
||||
td_mode="cross",
|
||||
td_mode=mgn,
|
||||
pos_side=pos_side,
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -642,7 +653,7 @@ class OkxLiveExecutor(Matcher):
|
||||
inst_id=perp_inst,
|
||||
side=side,
|
||||
sz=str(perp_sz),
|
||||
td_mode="cross",
|
||||
td_mode=self._perp_margin_mode(),
|
||||
pos_side=pos_side,
|
||||
reduce_only=True,
|
||||
)
|
||||
@@ -927,7 +938,7 @@ class OkxLiveExecutor(Matcher):
|
||||
inst_id=perp_inst,
|
||||
side=side,
|
||||
sz=str(perp_sz),
|
||||
td_mode="cross",
|
||||
td_mode=self._perp_margin_mode(),
|
||||
pos_side=pos_side,
|
||||
reduce_only=True,
|
||||
)
|
||||
|
||||
@@ -228,6 +228,7 @@ class Database:
|
||||
"skip_weekends": str(s.skip_weekends),
|
||||
"max_rounds": str(s.max_rounds),
|
||||
"leverage": str(s.leverage),
|
||||
"perp_margin_mode": str(s.perp_margin_mode),
|
||||
"min_option_hours": str(s.min_option_hours),
|
||||
"min_option_leverage": str(s.min_option_leverage),
|
||||
"atm_open_offset_enabled": str(s.atm_open_offset_enabled),
|
||||
|
||||
@@ -83,6 +83,13 @@ class StrategyEngine:
|
||||
rest_sec = self.ledger.get_setting_int("rest_seconds", s.rest_seconds)
|
||||
skip_weekends = self.ledger.get_setting_bool("skip_weekends", s.skip_weekends)
|
||||
leverage = self.ledger.get_setting_float("leverage", s.leverage)
|
||||
perp_mm = str(
|
||||
self.ledger.get_setting_str("perp_margin_mode", s.perp_margin_mode)
|
||||
or s.perp_margin_mode
|
||||
or "cross"
|
||||
).strip().lower()
|
||||
if perp_mm not in ("cross", "isolated"):
|
||||
perp_mm = "cross"
|
||||
min_hours = self.ledger.get_setting_float("min_option_hours", s.min_option_hours)
|
||||
min_opt_lev = self.ledger.get_setting_float(
|
||||
"min_option_leverage", s.min_option_leverage
|
||||
@@ -140,6 +147,7 @@ class StrategyEngine:
|
||||
"premium_exit_multiple": prem_mult,
|
||||
"exit_target_usdt": exit_amt,
|
||||
"leverage": leverage,
|
||||
"perp_margin_mode": perp_mm,
|
||||
"perp_qty_eth": perp_qty,
|
||||
"option_qty_eth": opt_qty,
|
||||
"min_option_hours": min_hours,
|
||||
|
||||
Reference in New Issue
Block a user