Move settings hints into rules; add OO amplitude filter toggle default off.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -322,6 +322,19 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
|
|||||||
"oo_amplitude_hours": _pick(
|
"oo_amplitude_hours": _pick(
|
||||||
"oo_amplitude_hours", float(settings.oo_amplitude_hours)
|
"oo_amplitude_hours", float(settings.oo_amplitude_hours)
|
||||||
),
|
),
|
||||||
|
"oo_amplitude_filter_enabled": (
|
||||||
|
str(
|
||||||
|
st.get("oo_amplitude_filter_enabled")
|
||||||
|
if "oo_amplitude_filter_enabled" in st
|
||||||
|
else db.get_setting(
|
||||||
|
"oo_amplitude_filter_enabled",
|
||||||
|
str(settings.oo_amplitude_filter_enabled),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.strip()
|
||||||
|
.lower()
|
||||||
|
in ("1", "true", "yes", "on")
|
||||||
|
),
|
||||||
"oo_min_option_hours": _pick(
|
"oo_min_option_hours": _pick(
|
||||||
"oo_min_option_hours", float(settings.oo_min_option_hours)
|
"oo_min_option_hours", float(settings.oo_min_option_hours)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ KEYS = (
|
|||||||
"hedge_mode",
|
"hedge_mode",
|
||||||
"oo_amplitude_pct",
|
"oo_amplitude_pct",
|
||||||
"oo_amplitude_hours",
|
"oo_amplitude_hours",
|
||||||
|
"oo_amplitude_filter_enabled",
|
||||||
"oo_min_option_hours",
|
"oo_min_option_hours",
|
||||||
"oo_min_leverage",
|
"oo_min_leverage",
|
||||||
"oo_reward_ratio",
|
"oo_reward_ratio",
|
||||||
@@ -123,6 +124,7 @@ class StrategySettingsBody(BaseModel):
|
|||||||
)
|
)
|
||||||
oo_amplitude_pct: float | None = Field(default=None, ge=0.1, le=50)
|
oo_amplitude_pct: float | None = Field(default=None, ge=0.1, le=50)
|
||||||
oo_amplitude_hours: float | None = Field(default=None, ge=1, le=168)
|
oo_amplitude_hours: float | None = Field(default=None, ge=1, le=168)
|
||||||
|
oo_amplitude_filter_enabled: bool | None = None
|
||||||
oo_min_option_hours: float | None = Field(default=None, ge=1, le=720)
|
oo_min_option_hours: float | None = Field(default=None, ge=1, le=720)
|
||||||
oo_min_leverage: float | None = Field(default=None, ge=1, le=10000)
|
oo_min_leverage: float | None = Field(default=None, ge=1, le=10000)
|
||||||
oo_reward_ratio: float | None = Field(default=None, ge=0.5, le=20)
|
oo_reward_ratio: float | None = Field(default=None, ge=0.5, le=20)
|
||||||
@@ -365,6 +367,13 @@ def _read_settings() -> dict:
|
|||||||
db.get_setting("oo_amplitude_hours", str(s.oo_amplitude_hours))
|
db.get_setting("oo_amplitude_hours", str(s.oo_amplitude_hours))
|
||||||
or s.oo_amplitude_hours
|
or s.oo_amplitude_hours
|
||||||
),
|
),
|
||||||
|
"oo_amplitude_filter_enabled": _as_bool(
|
||||||
|
db.get_setting(
|
||||||
|
"oo_amplitude_filter_enabled",
|
||||||
|
str(s.oo_amplitude_filter_enabled),
|
||||||
|
),
|
||||||
|
s.oo_amplitude_filter_enabled,
|
||||||
|
),
|
||||||
"oo_min_option_hours": float(
|
"oo_min_option_hours": float(
|
||||||
db.get_setting("oo_min_option_hours", str(s.oo_min_option_hours))
|
db.get_setting("oo_min_option_hours", str(s.oo_min_option_hours))
|
||||||
or s.oo_min_option_hours
|
or s.oo_min_option_hours
|
||||||
@@ -476,6 +485,7 @@ async def put_strategy_settings(
|
|||||||
"hedge_mode",
|
"hedge_mode",
|
||||||
"oo_amplitude_pct",
|
"oo_amplitude_pct",
|
||||||
"oo_amplitude_hours",
|
"oo_amplitude_hours",
|
||||||
|
"oo_amplitude_filter_enabled",
|
||||||
"oo_min_option_hours",
|
"oo_min_option_hours",
|
||||||
"oo_min_leverage",
|
"oo_min_leverage",
|
||||||
"oo_reward_ratio",
|
"oo_reward_ratio",
|
||||||
|
|||||||
@@ -82,8 +82,9 @@ class Settings(BaseSettings):
|
|||||||
martingale_max_doubles: int = 3 # 最多翻倍次数(如 2→4→8→16 为 3 次)
|
martingale_max_doubles: int = 3 # 最多翻倍次数(如 2→4→8→16 为 3 次)
|
||||||
# 对冲模式:perp_option=永期(默认)| option_option=期期
|
# 对冲模式:perp_option=永期(默认)| option_option=期期
|
||||||
hedge_mode: str = "perp_option"
|
hedge_mode: str = "perp_option"
|
||||||
oo_amplitude_pct: float = 1.5 # 振幅最大 %(回看窗内高低;超过则不开)
|
oo_amplitude_pct: float = 1.5 # 振幅最大 %(过滤开启时:超过则不开)
|
||||||
oo_amplitude_hours: float = 12.0 # 振幅回看小时
|
oo_amplitude_hours: float = 12.0 # 振幅回看小时(选约高低点;过滤关也用)
|
||||||
|
oo_amplitude_filter_enabled: bool = False # 振幅过滤开关(默认关)
|
||||||
oo_min_option_hours: float = 24.0 # 期期:最短剩余到期小时
|
oo_min_option_hours: float = 24.0 # 期期:最短剩余到期小时
|
||||||
oo_min_leverage: float = 200.0 # 期期:单腿最低杠杆
|
oo_min_leverage: float = 200.0 # 期期:单腿最低杠杆
|
||||||
oo_reward_ratio: float = 2.0 # 盈亏比:出场目标 = 预算 × 比
|
oo_reward_ratio: float = 2.0 # 盈亏比:出场目标 = 预算 × 比
|
||||||
|
|||||||
@@ -279,6 +279,16 @@ class StrategyEngine:
|
|||||||
)
|
)
|
||||||
or s.oo_amplitude_hours
|
or s.oo_amplitude_hours
|
||||||
),
|
),
|
||||||
|
"oo_amplitude_filter_enabled": str(
|
||||||
|
self.ledger.get_setting_str(
|
||||||
|
"oo_amplitude_filter_enabled",
|
||||||
|
str(s.oo_amplitude_filter_enabled),
|
||||||
|
)
|
||||||
|
or s.oo_amplitude_filter_enabled
|
||||||
|
)
|
||||||
|
.strip()
|
||||||
|
.lower()
|
||||||
|
in ("1", "true", "yes", "on"),
|
||||||
"oo_min_option_hours": float(
|
"oo_min_option_hours": float(
|
||||||
self.ledger.get_setting_float(
|
self.ledger.get_setting_float(
|
||||||
"oo_min_option_hours", s.oo_min_option_hours
|
"oo_min_option_hours", s.oo_min_option_hours
|
||||||
|
|||||||
@@ -144,12 +144,13 @@ def build_oo_pick_core(
|
|||||||
skip_expiry_ymds: set[str] | None = None,
|
skip_expiry_ymds: set[str] | None = None,
|
||||||
now: datetime | None = None,
|
now: datetime | None = None,
|
||||||
max_dev_pct: float = 1.0,
|
max_dev_pct: float = 1.0,
|
||||||
|
amplitude_filter_enabled: bool = True,
|
||||||
) -> OoPickCore | None:
|
) -> OoPickCore | None:
|
||||||
"""完整期期选约:振幅门 + 虚值双腿(贴高低≤max_dev%) + 杠杆。"""
|
"""完整期期选约:振幅门(可关)+ 虚值双腿(贴高低≤max_dev%) + 杠杆。"""
|
||||||
amp = amplitude or fetch_amplitude_hl_for_runtime(amplitude_hours)
|
amp = amplitude or fetch_amplitude_hl_for_runtime(amplitude_hours)
|
||||||
if amp is None:
|
if amp is None:
|
||||||
return None
|
return None
|
||||||
if float(amp.range_pct) > float(amplitude_pct) + 1e-12:
|
if amplitude_filter_enabled and float(amp.range_pct) > float(amplitude_pct) + 1e-12:
|
||||||
return None
|
return None
|
||||||
if spot <= 0:
|
if spot <= 0:
|
||||||
spot = float(amp.mid)
|
spot = float(amp.mid)
|
||||||
|
|||||||
@@ -192,13 +192,24 @@ def _hedge_mode() -> str:
|
|||||||
return "perp_option"
|
return "perp_option"
|
||||||
|
|
||||||
|
|
||||||
def _oo_settings() -> tuple[float, float, float, float, float]:
|
def _oo_settings() -> tuple[float, float, float, float, float, bool]:
|
||||||
"""amplitude_pct, amplitude_hours, min_option_hours, min_leverage, strike_max_dev_pct"""
|
"""amplitude_pct, amplitude_hours, min_option_hours, min_leverage, strike_max_dev_pct, amp_filter_on"""
|
||||||
s = get_settings()
|
s = get_settings()
|
||||||
try:
|
try:
|
||||||
from ..models.db import get_db
|
from ..models.db import get_db
|
||||||
|
|
||||||
db = get_db()
|
db = get_db()
|
||||||
|
filt_raw = db.get_setting(
|
||||||
|
"oo_amplitude_filter_enabled", str(s.oo_amplitude_filter_enabled)
|
||||||
|
)
|
||||||
|
filt_on = str(filt_raw or "").strip().lower() in (
|
||||||
|
"1",
|
||||||
|
"true",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
)
|
||||||
|
if filt_raw is None or filt_raw == "":
|
||||||
|
filt_on = bool(s.oo_amplitude_filter_enabled)
|
||||||
return (
|
return (
|
||||||
float(db.get_setting("oo_amplitude_pct", str(s.oo_amplitude_pct)) or s.oo_amplitude_pct),
|
float(db.get_setting("oo_amplitude_pct", str(s.oo_amplitude_pct)) or s.oo_amplitude_pct),
|
||||||
float(
|
float(
|
||||||
@@ -216,6 +227,7 @@ def _oo_settings() -> tuple[float, float, float, float, float]:
|
|||||||
)
|
)
|
||||||
or s.oo_strike_max_dev_pct
|
or s.oo_strike_max_dev_pct
|
||||||
),
|
),
|
||||||
|
filt_on,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
return (
|
return (
|
||||||
@@ -224,6 +236,7 @@ def _oo_settings() -> tuple[float, float, float, float, float]:
|
|||||||
s.oo_min_option_hours,
|
s.oo_min_option_hours,
|
||||||
s.oo_min_leverage,
|
s.oo_min_leverage,
|
||||||
s.oo_strike_max_dev_pct,
|
s.oo_strike_max_dev_pct,
|
||||||
|
bool(s.oo_amplitude_filter_enabled),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -457,7 +470,7 @@ class StrategySession:
|
|||||||
try:
|
try:
|
||||||
from ..exchange.candles import fetch_amplitude_hl_for_runtime
|
from ..exchange.candles import fetch_amplitude_hl_for_runtime
|
||||||
|
|
||||||
amp_pct, amp_hours, _, _, _ = _oo_settings()
|
amp_pct, amp_hours, _, _, _, amp_filt = _oo_settings()
|
||||||
amp = fetch_amplitude_hl_for_runtime(amp_hours)
|
amp = fetch_amplitude_hl_for_runtime(amp_hours)
|
||||||
if amp is None:
|
if amp is None:
|
||||||
return self._oo_amp
|
return self._oo_amp
|
||||||
@@ -468,7 +481,12 @@ class StrategySession:
|
|||||||
"range_pct": float(amp.range_pct),
|
"range_pct": float(amp.range_pct),
|
||||||
"hours": float(amp_hours),
|
"hours": float(amp_hours),
|
||||||
"max_pct": float(amp_pct),
|
"max_pct": float(amp_pct),
|
||||||
"ok": float(amp.range_pct) <= float(amp_pct) + 1e-12,
|
"filter_enabled": bool(amp_filt),
|
||||||
|
"ok": (
|
||||||
|
True
|
||||||
|
if not amp_filt
|
||||||
|
else float(amp.range_pct) <= float(amp_pct) + 1e-12
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return self._oo_amp
|
return self._oo_amp
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -484,7 +502,7 @@ class StrategySession:
|
|||||||
if _has_open_position():
|
if _has_open_position():
|
||||||
return self.align_to_held_position()
|
return self.align_to_held_position()
|
||||||
s = self.settings
|
s = self.settings
|
||||||
amp_pct, amp_hours, min_hours, _min_lev, max_dev = _oo_settings()
|
amp_pct, amp_hours, min_hours, _min_lev, max_dev, _amp_filt = _oo_settings()
|
||||||
idx = self.ex.fetch_index(s.index_inst_id)
|
idx = self.ex.fetch_index(s.index_inst_id)
|
||||||
mark = self.ex.fetch_mark(s.perp_inst_id) or idx
|
mark = self.ex.fetch_mark(s.perp_inst_id) or idx
|
||||||
if mark is None or mark <= 0:
|
if mark is None or mark <= 0:
|
||||||
@@ -535,7 +553,7 @@ class StrategySession:
|
|||||||
from .selection import _complete_by_expiry, option_leverage
|
from .selection import _complete_by_expiry, option_leverage
|
||||||
|
|
||||||
s = self.settings
|
s = self.settings
|
||||||
amp_pct, amp_hours, min_hours, min_lev, max_dev = _oo_settings()
|
amp_pct, amp_hours, min_hours, min_lev, max_dev, amp_filt = _oo_settings()
|
||||||
idx = self.ex.fetch_index(s.index_inst_id)
|
idx = self.ex.fetch_index(s.index_inst_id)
|
||||||
mark = self.ex.fetch_mark(s.perp_inst_id) or idx
|
mark = self.ex.fetch_mark(s.perp_inst_id) or idx
|
||||||
if mark is None or mark <= 0:
|
if mark is None or mark <= 0:
|
||||||
@@ -553,9 +571,14 @@ class StrategySession:
|
|||||||
"range_pct": float(amp.range_pct),
|
"range_pct": float(amp.range_pct),
|
||||||
"hours": float(amp_hours),
|
"hours": float(amp_hours),
|
||||||
"max_pct": float(amp_pct),
|
"max_pct": float(amp_pct),
|
||||||
"ok": float(amp.range_pct) <= float(amp_pct) + 1e-12,
|
"filter_enabled": bool(amp_filt),
|
||||||
|
"ok": (
|
||||||
|
True
|
||||||
|
if not amp_filt
|
||||||
|
else float(amp.range_pct) <= float(amp_pct) + 1e-12
|
||||||
|
),
|
||||||
}
|
}
|
||||||
if float(amp.range_pct) > float(amp_pct) + 1e-12:
|
if amp_filt and float(amp.range_pct) > float(amp_pct) + 1e-12:
|
||||||
logger.info(
|
logger.info(
|
||||||
"oo: amplitude %.3f%% > max %.3f%% (H=%.2f L=%.2f)",
|
"oo: amplitude %.3f%% > max %.3f%% (H=%.2f L=%.2f)",
|
||||||
amp.range_pct,
|
amp.range_pct,
|
||||||
@@ -852,7 +875,7 @@ class StrategySession:
|
|||||||
def oo_needs_realign(self) -> bool:
|
def oo_needs_realign(self) -> bool:
|
||||||
if self._pair is None:
|
if self._pair is None:
|
||||||
return True
|
return True
|
||||||
_amp_pct, amp_hours, min_hours, _, max_dev = _oo_settings()
|
_amp_pct, amp_hours, min_hours, _, max_dev, amp_filt = _oo_settings()
|
||||||
if (
|
if (
|
||||||
hours_until_expiry(self._pair.expiry_ymd, expiry_ms=self._pair.expiry_ms)
|
hours_until_expiry(self._pair.expiry_ymd, expiry_ms=self._pair.expiry_ms)
|
||||||
+ 1e-9
|
+ 1e-9
|
||||||
@@ -879,7 +902,12 @@ class StrategySession:
|
|||||||
"range_pct": float(amp.range_pct),
|
"range_pct": float(amp.range_pct),
|
||||||
"hours": float(amp_hours),
|
"hours": float(amp_hours),
|
||||||
"max_pct": float(_amp_pct),
|
"max_pct": float(_amp_pct),
|
||||||
"ok": float(amp.range_pct) <= float(_amp_pct) + 1e-12,
|
"filter_enabled": bool(amp_filt),
|
||||||
|
"ok": (
|
||||||
|
True
|
||||||
|
if not amp_filt
|
||||||
|
else float(amp.range_pct) <= float(_amp_pct) + 1e-12
|
||||||
|
),
|
||||||
}
|
}
|
||||||
contracts = self.ex.list_option_contracts(self.settings.option_inst_family)
|
contracts = self.ex.list_option_contracts(self.settings.option_inst_family)
|
||||||
picked = select_oo_pair(
|
picked = select_oo_pair(
|
||||||
|
|||||||
@@ -175,3 +175,18 @@ def test_amplitude_max_gate() -> None:
|
|||||||
assert ok is not None
|
assert ok is not None
|
||||||
assert ok.call.strike == 2025.0
|
assert ok.call.strike == 2025.0
|
||||||
assert ok.put.strike == 1975.0
|
assert ok.put.strike == 1975.0
|
||||||
|
# 过滤关闭:振幅 3% 超过 1.5% 上限也可过
|
||||||
|
ok_off = build_oo_pick_core(
|
||||||
|
contracts=contracts,
|
||||||
|
spot=2000,
|
||||||
|
call_ask=5,
|
||||||
|
put_ask=5,
|
||||||
|
min_hours=1,
|
||||||
|
min_leverage=1,
|
||||||
|
amplitude_hours=12,
|
||||||
|
amplitude_pct=1.5,
|
||||||
|
amplitude=amp,
|
||||||
|
max_dev_pct=1.0,
|
||||||
|
amplitude_filter_enabled=False,
|
||||||
|
)
|
||||||
|
assert ok_off is not None
|
||||||
|
|||||||
@@ -370,6 +370,7 @@ export type PlanState = {
|
|||||||
hedge_mode?: "perp_option" | "option_option";
|
hedge_mode?: "perp_option" | "option_option";
|
||||||
oo_amplitude_pct?: number;
|
oo_amplitude_pct?: number;
|
||||||
oo_amplitude_hours?: number;
|
oo_amplitude_hours?: number;
|
||||||
|
oo_amplitude_filter_enabled?: boolean;
|
||||||
oo_min_option_hours?: number;
|
oo_min_option_hours?: number;
|
||||||
oo_min_leverage?: number;
|
oo_min_leverage?: number;
|
||||||
oo_reward_ratio?: number;
|
oo_reward_ratio?: number;
|
||||||
@@ -425,12 +426,12 @@ export type StrategySettings = {
|
|||||||
hedge_mode?: "perp_option" | "option_option";
|
hedge_mode?: "perp_option" | "option_option";
|
||||||
oo_amplitude_pct?: number;
|
oo_amplitude_pct?: number;
|
||||||
oo_amplitude_hours?: number;
|
oo_amplitude_hours?: number;
|
||||||
|
oo_amplitude_filter_enabled?: boolean;
|
||||||
oo_min_option_hours?: number;
|
oo_min_option_hours?: number;
|
||||||
oo_min_leverage?: number;
|
oo_min_leverage?: number;
|
||||||
oo_reward_ratio?: number;
|
oo_reward_ratio?: number;
|
||||||
oo_budget_cushion?: number;
|
oo_budget_cushion?: number;
|
||||||
oo_strike_max_dev_pct?: number;
|
oo_strike_max_dev_pct?: number;
|
||||||
oo_strike_max_dev_pct?: number;
|
|
||||||
exchange?: string;
|
exchange?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -264,7 +264,9 @@ export default function PlanPage() {
|
|||||||
? `ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}`
|
? `ATM偏差≤${fmt(plan?.max_atm_open_offset ?? 3, 0)}`
|
||||||
: "ATM偏差关";
|
: "ATM偏差关";
|
||||||
const ooSelectLabel = [
|
const ooSelectLabel = [
|
||||||
`振幅≤${fmt(plan?.oo_amplitude_pct ?? 1.5, 1)}%/${fmt(plan?.oo_amplitude_hours ?? 12, 0)}h`,
|
plan?.oo_amplitude_filter_enabled
|
||||||
|
? `振幅≤${fmt(plan?.oo_amplitude_pct ?? 1.5, 1)}%/${fmt(plan?.oo_amplitude_hours ?? 12, 0)}h`
|
||||||
|
: `振幅回看${fmt(plan?.oo_amplitude_hours ?? 12, 0)}h(过滤关)`,
|
||||||
`贴高低≤${fmt(plan?.oo_strike_max_dev_pct ?? 1, 1)}%`,
|
`贴高低≤${fmt(plan?.oo_strike_max_dev_pct ?? 1, 1)}%`,
|
||||||
"虚值Call@高·Put@低",
|
"虚值Call@高·Put@低",
|
||||||
`剩余≥${fmt(plan?.oo_min_option_hours ?? 24, 0)}h`,
|
`剩余≥${fmt(plan?.oo_min_option_hours ?? 24, 0)}h`,
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export default function SettingsPage() {
|
|||||||
);
|
);
|
||||||
const [ooAmpPct, setOoAmpPct] = useState(1.5);
|
const [ooAmpPct, setOoAmpPct] = useState(1.5);
|
||||||
const [ooAmpHours, setOoAmpHours] = useState(12);
|
const [ooAmpHours, setOoAmpHours] = useState(12);
|
||||||
|
const [ooAmpFilterOn, setOoAmpFilterOn] = useState(false);
|
||||||
const [ooMinHours, setOoMinHours] = useState(24);
|
const [ooMinHours, setOoMinHours] = useState(24);
|
||||||
const [ooMinLev, setOoMinLev] = useState(200);
|
const [ooMinLev, setOoMinLev] = useState(200);
|
||||||
const [ooStrikeDev, setOoStrikeDev] = useState(1);
|
const [ooStrikeDev, setOoStrikeDev] = useState(1);
|
||||||
@@ -235,6 +236,7 @@ export default function SettingsPage() {
|
|||||||
);
|
);
|
||||||
setOoAmpPct(s.oo_amplitude_pct ?? 1.5);
|
setOoAmpPct(s.oo_amplitude_pct ?? 1.5);
|
||||||
setOoAmpHours(s.oo_amplitude_hours ?? 12);
|
setOoAmpHours(s.oo_amplitude_hours ?? 12);
|
||||||
|
setOoAmpFilterOn(Boolean(s.oo_amplitude_filter_enabled));
|
||||||
setOoMinHours(s.oo_min_option_hours ?? 24);
|
setOoMinHours(s.oo_min_option_hours ?? 24);
|
||||||
setOoMinLev(s.oo_min_leverage ?? 200);
|
setOoMinLev(s.oo_min_leverage ?? 200);
|
||||||
setOoStrikeDev(s.oo_strike_max_dev_pct ?? 1);
|
setOoStrikeDev(s.oo_strike_max_dev_pct ?? 1);
|
||||||
@@ -412,6 +414,7 @@ export default function SettingsPage() {
|
|||||||
hedge_mode: hedgeMode,
|
hedge_mode: hedgeMode,
|
||||||
oo_amplitude_pct: ooAmpPct,
|
oo_amplitude_pct: ooAmpPct,
|
||||||
oo_amplitude_hours: ooAmpHours,
|
oo_amplitude_hours: ooAmpHours,
|
||||||
|
oo_amplitude_filter_enabled: ooAmpFilterOn,
|
||||||
oo_min_option_hours: ooMinHours,
|
oo_min_option_hours: ooMinHours,
|
||||||
oo_min_leverage: ooMinLev,
|
oo_min_leverage: ooMinLev,
|
||||||
oo_strike_max_dev_pct: ooStrikeDev,
|
oo_strike_max_dev_pct: ooStrikeDev,
|
||||||
@@ -739,11 +742,53 @@ export default function SettingsPage() {
|
|||||||
<option value="perp_option">永期对冲(永续+期权)</option>
|
<option value="perp_option">永期对冲(永续+期权)</option>
|
||||||
<option value="option_option">期期对冲(双期权)</option>
|
<option value="option_option">期期对冲(双期权)</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
二选一。期期:近 N 小时振幅高低匹配虚值 Call/Put;达标只平盈利腿。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{isOo ? (
|
{isOo ? (
|
||||||
|
<>
|
||||||
|
<div className="field">
|
||||||
|
<label htmlFor="ooAmpFilt">振幅过滤</label>
|
||||||
|
<select
|
||||||
|
id="ooAmpFilt"
|
||||||
|
className="mono"
|
||||||
|
value={ooAmpFilterOn ? "on" : "off"}
|
||||||
|
onChange={(e) =>
|
||||||
|
setOoAmpFilterOn(e.target.value === "on")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="off">关闭(默认)</option>
|
||||||
|
<option value="on">开启</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="field">
|
||||||
|
<label htmlFor="ooAmpH">振幅回看(小时)</label>
|
||||||
|
<input
|
||||||
|
id="ooAmpH"
|
||||||
|
className="mono"
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="1"
|
||||||
|
value={ooAmpHours}
|
||||||
|
onChange={(e) =>
|
||||||
|
setOoAmpHours(Number(e.target.value))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{ooAmpFilterOn ? (
|
||||||
|
<div className="field">
|
||||||
|
<label htmlFor="ooAmp">振幅最大(%)</label>
|
||||||
|
<input
|
||||||
|
id="ooAmp"
|
||||||
|
className="mono"
|
||||||
|
type="number"
|
||||||
|
step="0.1"
|
||||||
|
min="0.1"
|
||||||
|
value={ooAmpPct}
|
||||||
|
onChange={(e) =>
|
||||||
|
setOoAmpPct(Number(e.target.value))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="ooRR">盈亏比</label>
|
<label htmlFor="ooRR">盈亏比</label>
|
||||||
<input
|
<input
|
||||||
@@ -757,11 +802,8 @@ export default function SettingsPage() {
|
|||||||
setOoRewardRatio(Number(e.target.value))
|
setOoRewardRatio(Number(e.target.value))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
目标盈利 = 以损预算 × 盈亏比(例预算 100U、比 2 → 目标
|
|
||||||
200U)。预算平分给 Call/Put,各按卖一独立定仓。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{!isOo ? (
|
{!isOo ? (
|
||||||
<div className="field">
|
<div className="field">
|
||||||
@@ -816,11 +858,6 @@ export default function SettingsPage() {
|
|||||||
</option>
|
</option>
|
||||||
<option value="actual">实际杠杆(盘口卖一)</option>
|
<option value="actual">实际杠杆(盘口卖一)</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
选约杠杆:按「指数÷期权杠杆≥」估权利金算
|
|
||||||
k,权利金再便宜也不放大仓位。
|
|
||||||
实际杠杆:按盘口卖一定仓,便宜时 k 会变大。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{!isOo ? (
|
{!isOo ? (
|
||||||
@@ -914,11 +951,6 @@ export default function SettingsPage() {
|
|||||||
<option value="off">关闭(默认)</option>
|
<option value="off">关闭(默认)</option>
|
||||||
<option value="on">开启</option>
|
<option value="on">开启</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
{riskLossPct > 3
|
|
||||||
? "亏损幅度超过 3% 时不可启用倍投。"
|
|
||||||
: "连续亏损日达阈值后翻倍。到期结算无论盈亏(含小盈利)都按亏损日计;达标平仓盈利才打断连亏。"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{martingaleOn && riskLossPct <= 3 ? (
|
{martingaleOn && riskLossPct <= 3 ? (
|
||||||
<>
|
<>
|
||||||
@@ -957,26 +989,6 @@ export default function SettingsPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p
|
|
||||||
className="hint"
|
|
||||||
style={{ margin: "0.35rem 0 0" }}
|
|
||||||
>
|
|
||||||
例:幅度 {riskLossPct}%、连亏{" "}
|
|
||||||
{martingaleStartAfter} 天起翻、最多{" "}
|
|
||||||
{martingaleMaxDoubles} 次 →{" "}
|
|
||||||
{[0, 1, 2, 3]
|
|
||||||
.filter((d) => d <= martingaleMaxDoubles)
|
|
||||||
.map(
|
|
||||||
(d) =>
|
|
||||||
`${Number(
|
|
||||||
(
|
|
||||||
riskLossPct *
|
|
||||||
2 ** d
|
|
||||||
).toPrecision(6),
|
|
||||||
)}%`,
|
|
||||||
)
|
|
||||||
.join(" → ")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -1230,35 +1242,6 @@ export default function SettingsPage() {
|
|||||||
<div className="settings-fields">
|
<div className="settings-fields">
|
||||||
{isOo ? (
|
{isOo ? (
|
||||||
<>
|
<>
|
||||||
<div className="field">
|
|
||||||
<label htmlFor="ooAmp">振幅最大(%)</label>
|
|
||||||
<input
|
|
||||||
id="ooAmp"
|
|
||||||
className="mono"
|
|
||||||
type="number"
|
|
||||||
step="0.1"
|
|
||||||
min="0.1"
|
|
||||||
value={ooAmpPct}
|
|
||||||
onChange={(e) => setOoAmpPct(Number(e.target.value))}
|
|
||||||
/>
|
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
回看窗内振幅超过该上限则不开仓。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="field">
|
|
||||||
<label htmlFor="ooAmpH">振幅回看(小时)</label>
|
|
||||||
<input
|
|
||||||
id="ooAmpH"
|
|
||||||
className="mono"
|
|
||||||
type="number"
|
|
||||||
step="1"
|
|
||||||
min="1"
|
|
||||||
value={ooAmpHours}
|
|
||||||
onChange={(e) =>
|
|
||||||
setOoAmpHours(Number(e.target.value))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="ooMinH">最短剩余到期(小时)</label>
|
<label htmlFor="ooMinH">最短剩余到期(小时)</label>
|
||||||
<input
|
<input
|
||||||
@@ -1299,10 +1282,6 @@ export default function SettingsPage() {
|
|||||||
setOoStrikeDev(Number(e.target.value))
|
setOoStrikeDev(Number(e.target.value))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
虚值 Call/Put 行权价相对振幅高/低点 |K−HL|/HL
|
|
||||||
不超过该比例(默认 1%)。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -1412,9 +1391,6 @@ export default function SettingsPage() {
|
|||||||
setOoRewardRatio(Number(e.target.value))
|
setOoRewardRatio(Number(e.target.value))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
净盈 ≥ 预算×盈亏比时只平盈利腿;亏损腿进残留,按下方回收比例或到期结算。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -1585,9 +1561,6 @@ export default function SettingsPage() {
|
|||||||
<option value="1">开启(推荐)</option>
|
<option value="1">开启(推荐)</option>
|
||||||
<option value="0">关闭</option>
|
<option value="0">关闭</option>
|
||||||
</select>
|
</select>
|
||||||
<p className="hint" style={{ margin: "0.35rem 0 0" }}>
|
|
||||||
开启后:历史上已开过某到期日(如 0803)后,不论今天还是明天都不能再开同到期,只能监控/开下一到期(如 0804)。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="fee">手续费率</label>
|
<label htmlFor="fee">手续费率</label>
|
||||||
@@ -1637,24 +1610,50 @@ export default function SettingsPage() {
|
|||||||
10000。
|
10000。
|
||||||
</li>
|
</li>
|
||||||
) : null}
|
) : null}
|
||||||
{isOo ? (
|
|
||||||
<li>
|
<li>
|
||||||
期期对冲:强制以损定仓 + 亏损幅度%;两腿 1:1 平分预算;出场用盈亏比,不使用永续名义/比例/k。
|
对冲模式二选一:永期=永续+期权;期期=近 N
|
||||||
|
小时振幅高低匹配虚值 Call/Put,达标只平盈利腿。
|
||||||
</li>
|
</li>
|
||||||
|
{isOo ? (
|
||||||
|
<>
|
||||||
|
<li>
|
||||||
|
期期定仓:强制以损定仓 + 亏损幅度%;预算平分
|
||||||
|
Call/Put,各按卖一独立定仓;出场目标 = 预算 × 盈亏比。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
振幅过滤:默认关闭。关闭时仍用「振幅回看」取高低点选约,但不因振幅超限拒开;开启后回看窗内振幅超过「振幅最大%」则不开仓。
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<li>
|
<li>
|
||||||
永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX
|
永续保证金模式:仅作用于永续(全仓/逐仓,默认全仓)。OKX
|
||||||
期权始终为现金/逐仓(cash),不受此选项影响。
|
期权始终为现金/逐仓(cash),不受此选项影响。
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
节奏杠杆:选约杠杆按「指数÷期权杠杆≥」估权利金算
|
||||||
|
k,权利金再便宜也不放大仓位;实际杠杆按盘口卖一定仓,便宜时
|
||||||
|
k 会变大。
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
<li>
|
||||||
|
倍投:默认关。仅以损定仓且亏损幅度≤3%时可开;连续亏损日达阈值后翻倍;到期结算(含小盈利)仍计亏损日,达标平仓盈利才打断连亏。
|
||||||
|
</li>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{stratSub === "select" ? (
|
{stratSub === "select" ? (
|
||||||
isOo ? (
|
isOo ? (
|
||||||
|
<>
|
||||||
<li>
|
<li>
|
||||||
期期选约:回看窗内真实高低点振幅不超过上限后,同到期虚值 Call
|
期期选约:用振幅回看窗高低点匹配同到期虚值 Call@高 /
|
||||||
贴高、Put 贴低;剩余到期与单腿杠杆门限见本页。
|
Put@低;振幅过滤在「仓位」页开关。
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
贴高低最大偏离:行权价相对高/低点 |K−HL|/HL
|
||||||
|
不超过该比例(默认 1%),否则跳过该到期。
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<li>
|
<li>
|
||||||
@@ -1679,7 +1678,7 @@ export default function SettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{isOo
|
{isOo
|
||||||
? "残留回收:达标只平盈利腿后,亏损腿权利金回升到初始比例及以上时可尝试平仓;否则到期结算。"
|
? "期期出场:净盈 ≥ 预算×盈亏比时只平盈利腿;亏损腿进残留,按回收比例或到期结算。"
|
||||||
: "残留期权回收比例:只平永续后,当买一权利金回升到初始权利金的该比例及以上时,按最新买一 IOC 限价卖掉归档期权(不扫市价);默认 20%。未达标或未完全成交则等到下次巡检或到期结算。"}
|
: "残留期权回收比例:只平永续后,当买一权利金回升到初始权利金的该比例及以上时,按最新买一 IOC 限价卖掉归档期权(不扫市价);默认 20%。未达标或未完全成交则等到下次巡检或到期结算。"}
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
@@ -1690,6 +1689,9 @@ export default function SettingsPage() {
|
|||||||
实盘下单最小间隔:LIVE 私有下单/查单间隔,默认 1s。范围
|
实盘下单最小间隔:LIVE 私有下单/查单间隔,默认 1s。范围
|
||||||
0.2–30。
|
0.2–30。
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
同到期日一天只开一次:开启后历史上已开过某到期日则不能再开同到期,只能下一到期。
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
显示手动开仓:默认关闭。开启后计划页才出现「手动开一组 /
|
显示手动开仓:默认关闭。开启后计划页才出现「手动开一组 /
|
||||||
手动全平」。策略自动运行中禁止手动开仓(须先暂停);紧急全平始终可用。
|
手动全平」。策略自动运行中禁止手动开仓(须先暂停);紧急全平始终可用。
|
||||||
|
|||||||
Reference in New Issue
Block a user