Add configurable SIM equity (default 10k) and strategy docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ OPTION_INST_FAMILY=ETH-USD_UM
|
||||
INDEX_INST_ID=ETH-USD
|
||||
|
||||
FEE_RATE=0.0005
|
||||
INITIAL_EQUITY=100000
|
||||
INITIAL_EQUITY=10000
|
||||
LEVERAGE=3
|
||||
MIN_OPTION_HOURS=12
|
||||
MIN_OPTION_LEVERAGE=100
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
- [开发方案](docs/开发方案.md)
|
||||
- [代码结构](docs/代码结构.md)
|
||||
- [商业化与授权方案](docs/商业化与授权方案.md)
|
||||
- [策略说明](docs/策略说明.md)
|
||||
|
||||
## 访问(测试机)
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from ..config import get_settings
|
||||
from ..models.db import get_db
|
||||
from ..sim.ledger import Ledger
|
||||
from ..sim.matcher import Matcher
|
||||
from .auth import require_user
|
||||
|
||||
router = APIRouter(prefix="/api/settings", tags=["settings"])
|
||||
@@ -38,7 +39,7 @@ class StrategySettingsBody(BaseModel):
|
||||
premium_exit_multiple: float | None = Field(default=None, ge=0.1, le=100)
|
||||
rest_seconds: int | None = Field(default=None, ge=0, le=3600)
|
||||
skip_weekends: bool | None = None
|
||||
initial_equity: float | None = Field(default=None, ge=1000)
|
||||
initial_equity: float | None = Field(default=None, ge=1000, le=10_000_000)
|
||||
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)
|
||||
@@ -116,8 +117,27 @@ async def put_strategy_settings(
|
||||
_user: Annotated[str, Depends(require_user)],
|
||||
) -> dict:
|
||||
db = get_db()
|
||||
s = get_settings()
|
||||
data = body.model_dump(exclude_none=True)
|
||||
equity_to_apply: float | None = None
|
||||
if "initial_equity" in data:
|
||||
new_eq = float(data["initial_equity"])
|
||||
old_eq = float(
|
||||
db.get_setting("initial_equity", str(s.initial_equity)) or s.initial_equity
|
||||
)
|
||||
if abs(new_eq - old_eq) > 1e-9:
|
||||
if Matcher(db).has_open_position():
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="有未平仓,无法重置模拟资金;请先平仓后再改",
|
||||
)
|
||||
equity_to_apply = new_eq
|
||||
for k, v in data.items():
|
||||
if k in KEYS:
|
||||
db.set_setting(k, str(v))
|
||||
if equity_to_apply is not None:
|
||||
Ledger(db).reset_equity(
|
||||
equity_to_apply,
|
||||
note=f"设置模拟资金={equity_to_apply:.2f}",
|
||||
)
|
||||
return _read_settings()
|
||||
|
||||
@@ -38,7 +38,7 @@ class Settings(BaseSettings):
|
||||
index_inst_id: str = "ETH-USD"
|
||||
|
||||
fee_rate: float = 0.0005
|
||||
initial_equity: float = 100_000.0
|
||||
initial_equity: float = 10_000.0 # SIM 模拟初始资金(USDT),设置页可改
|
||||
max_rounds: int = 3 # 已不再强管控,仅兼容旧字段
|
||||
open_hhmm: str = "16:00" # 已废弃开仓窗
|
||||
stop_open_hhmm: str = "08:00" # 已废弃开仓窗
|
||||
|
||||
@@ -48,6 +48,24 @@ class Ledger:
|
||||
self.db._conn.commit()
|
||||
return equity
|
||||
|
||||
def reset_equity(self, amount: float, *, note: str = "重置模拟资金") -> float:
|
||||
"""将权益与可用资金重置为 amount(reserved 清零)。须在无持仓时调用。"""
|
||||
now = int(time.time() * 1000)
|
||||
amt = float(amount)
|
||||
if amt < 0:
|
||||
raise ValueError("模拟资金不能为负")
|
||||
with self.db._lock:
|
||||
self.db._conn.execute(
|
||||
"UPDATE ledger_meta SET equity=?, available=?, reserved=0, updated_at_ms=? WHERE id=1",
|
||||
(amt, amt, now),
|
||||
)
|
||||
self.db._conn.execute(
|
||||
"INSERT INTO ledger_entries(group_id, kind, amount, balance_after, note, ts_ms) VALUES (?,?,?,?,?,?)",
|
||||
(None, "reset", amt, amt, note, now),
|
||||
)
|
||||
self.db._conn.commit()
|
||||
return amt
|
||||
|
||||
def get_setting_float(self, key: str, default: float) -> float:
|
||||
v = self.db.get_setting(key)
|
||||
if v is None or v == "":
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ OKX_API_KEY=... # 模拟阶段只需能拉行情的权限
|
||||
OKX_API_SECRET=...
|
||||
OKX_API_PASSPHRASE=...
|
||||
FEE_RATE=0.0005 # 示例;滑点 = 1 × FEE_RATE
|
||||
INITIAL_EQUITY=100000
|
||||
INITIAL_EQUITY=10000
|
||||
TZ=Asia/Shanghai
|
||||
```
|
||||
|
||||
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
# ETH 永续 + 期权对冲策略说明
|
||||
|
||||
> 依据当前代码逻辑整理(SIM 默认真值参数)。
|
||||
> 关联:[开发方案](./开发方案.md)、[商业化与授权方案](./商业化与授权方案.md)
|
||||
> 更新:2026-07-25
|
||||
|
||||
---
|
||||
|
||||
## 1. 策略一句话
|
||||
|
||||
用 **ATM 期权买方** 表达方向弹性,用 **反向永续** 做对冲腿;波动大时争取多轮兑现净盈利,波动小时接受权利金磨损,**到期自动全平**。
|
||||
|
||||
本质是 **概率与样本**:不追求每天固定轮次,而按行情吃机会。
|
||||
|
||||
---
|
||||
|
||||
## 2. 仓位结构(默认)
|
||||
|
||||
| 腿 | 默认数量 | 方向规则 | 成交方式 |
|
||||
|----|----------|----------|----------|
|
||||
| 永续 ETH-USDT-SWAP | **1 ETH** | 见下表 | 市价(吃买一/卖一 + 滑点) |
|
||||
| 期权 ETH-USD_UM | **2 ETH** 名义 | 只买不卖 | 开仓吃卖一,平仓吃买一 |
|
||||
|
||||
永续杠杆默认 **3×**(可配)。同时最多 **1 组**仓,禁止叠仓开下一组。
|
||||
|
||||
### 2.1 开仓方向(卖一比价)
|
||||
|
||||
比较同一 ATM 行权价的 **Call 卖一** 与 **Put 卖一**:
|
||||
|
||||
| 条件 | 期权 | 永续 |
|
||||
|------|------|------|
|
||||
| Call 卖一 > Put 卖一 | 买入 Call | 做空 |
|
||||
| Call 卖一 < Put 卖一 | 买入 Put | 做多 |
|
||||
| 相等 | 不开仓,继续等待 | — |
|
||||
|
||||
直觉:卖一更高的一侧往往隐含更强的短期方向/溢价偏好,买入该侧期权;永续反向以平衡部分 Delta。
|
||||
|
||||
---
|
||||
|
||||
## 3. 开仓机制
|
||||
|
||||
### 3.1 流程概览
|
||||
|
||||
```
|
||||
策略运行中
|
||||
→ 无持仓且不在休息期
|
||||
→ 非周末跳过(若开启)
|
||||
→ 选到期:剩余时长 ≥ min_option_hours(默认 12h)
|
||||
→ 该到期 ATM 行权价(最接近标的)
|
||||
→ Call/Put 卖一比价选方向
|
||||
→ 期权杠杆 = 标的价 ÷ 卖一权利金 ≥ min_option_leverage(默认 100)
|
||||
→ 开永续 + 开期权(一组)
|
||||
→ 锁定 initial_premium = 期权成交价 × 期权名义(不含费)
|
||||
```
|
||||
|
||||
### 3.2 选约约束
|
||||
|
||||
| 参数 | 默认 | 作用 |
|
||||
|------|------|------|
|
||||
| `min_option_hours` | 12 | 过滤过近到期,减少刚开仓就到期 |
|
||||
| `min_option_leverage` | 100 | 权利金相对标的不能太贵(现价/卖一) |
|
||||
| ATM | — | 同到期、最接近指数/标记价的行权价 |
|
||||
|
||||
无合格合约时:状态停留等待,记录「无合格期权…」,不硬开。
|
||||
|
||||
### 3.3 时间与节奏
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 开仓时间窗 | **已取消**,全天可尝试(仍受选约约束) |
|
||||
| 周六/周日 | 默认 **跳过新开仓**(上海时区,`skip_weekends`);持仓仍可平 |
|
||||
| 每日轮次上限 | **不限制**;波动大可能多轮,波动小可能一轮都难 |
|
||||
| 组间休息 | 全平后默认休息 **300 秒**(`rest_seconds`)再开下一组 |
|
||||
| 同时持仓 | 最多 1 组 |
|
||||
|
||||
### 3.4 费用(SIM)
|
||||
|
||||
- 永续、期权均按可配 `fee_rate`(默认 0.0005)计费。
|
||||
- 滑点按约 1 倍费率计入成交价,手续费另扣。
|
||||
- 开仓现金:支付期权权利金 + 开仓手续费;永续开仓主要扣费。
|
||||
|
||||
---
|
||||
|
||||
## 4. 平仓机制
|
||||
|
||||
任一触发 → **该组永续 + 期权全部平掉**。
|
||||
|
||||
### 4.1 净盈利达标(主出场)
|
||||
|
||||
**净盈利**口径(盯盘用):
|
||||
|
||||
```
|
||||
净盈利 ≈ 永续浮盈 + 期权浮盈 − 预估平仓手续费
|
||||
期权浮盈 = 当前买一 × 数量 − 初始权利金
|
||||
```
|
||||
|
||||
| 出场模式 | 默认 | 触发条件 |
|
||||
|----------|------|----------|
|
||||
| `fixed_usdt` | 目标 **15 USDT** | 净盈利 ≥ `net_profit_target` |
|
||||
| `premium_multiple` | 倍数 **1.0** | 净盈利 ≥ `initial_premium × premium_exit_multiple` |
|
||||
|
||||
说明:权利金倍数模式下,**1× = 赚回一倍权利金金额的净利**(例如权利金 20.41,目标约 20.41,不是 40.82)。
|
||||
|
||||
### 4.2 到期自动全平
|
||||
|
||||
- 到达期权到期时刻(OKX:UTC 08:00 = **上海 16:00**)→ 原因 `expiry`。
|
||||
- **绕过**买一深度 / 买一相对标记偏差闸门(与紧急全平同类),尽量卖掉。
|
||||
- 策略 **暂停时仍执行**,避免拖过期。
|
||||
- 波动小、拖到到期:权利金亏损视为 **预算内成本**,可接受。
|
||||
|
||||
### 4.3 平仓流动性闸门(常规出场)
|
||||
|
||||
常规净盈利平仓时:
|
||||
|
||||
- 期权买一深度需覆盖平仓名义;
|
||||
- 买一相对标记偏差默认 ≤ **30%**(`close_bid_mark_max_pct`);
|
||||
- 不满足 → `liquidity_wait`,继续等待,不改开仓。
|
||||
|
||||
**到期 / 紧急全平**:绕过上述闸门。
|
||||
|
||||
### 4.4 其它平仓入口
|
||||
|
||||
| 原因码 | 含义 |
|
||||
|--------|------|
|
||||
| `fixed_usdt` / `premium_multiple` | 净盈利达标 |
|
||||
| `expiry` | 到期自动全平 |
|
||||
| `emergency` | 界面紧急全平 |
|
||||
| `manual` | 手动平仓 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 策略侧重点
|
||||
|
||||
| 侧重点 | 说明 |
|
||||
|--------|------|
|
||||
| **吃波动,不赌横盘** | 买方期权需要标的走动;大波动利于达标出场 |
|
||||
| **概率与轮次** | 波动日可能多轮;磨盘日可能零轮或一轮认亏 |
|
||||
| **确定性时间规则** | 用周末跳过、到期全平,**不做**难以精准的横盘识别 |
|
||||
| **费用后净利** | 出场看扣完预估平仓费后的净盈利,避免「账面赚、平完亏」 |
|
||||
| **单组纪律** | 有仓不开下一组,防止重叠风险 |
|
||||
| **权利金是成本预算** | 小波动拖到期亏权利金,属于策略设计内损耗,不是异常 bug |
|
||||
|
||||
适合对外表述:
|
||||
|
||||
> 适合有波动的时段;震荡磨盘是主要磨损源。不怕单边大波动本身,怕没波动却持续付时间价值。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险点评估
|
||||
|
||||
### 6.1 主要风险
|
||||
|
||||
| 风险 | 等级 | 说明 | 缓解(已有/建议) |
|
||||
|------|------|------|------------------|
|
||||
| **横盘 / 低波动** | 高 | Theta 消耗权利金;永续几乎不贡献利润 | 周末跳过;到期认亏全平;接受磨损日 |
|
||||
| **方向选错** | 中高 | 卖一比价并非预测圣杯;错向时期权可能失利,永续端也可能承压 | 净盈利达标才兑现;到期强平止损时间 |
|
||||
| **开平滑点与手续费** | 中 | 双边 taker + 滑点侵蚀小目标利润 | 目标默认 15U 需覆盖费用;勿把目标设过低 |
|
||||
| **期权流动性** | 中 | 平仓买一薄、偏差大 → 等待 | 流动性闸门;到期/紧急可绕过 |
|
||||
| **杠杆与保证金** | 中 | 永续 3×,标的急跌/急涨挤压保证金 | 控制名义、保留资金缓冲;实盘注意强平线 |
|
||||
| **同到期 ATM 拥挤** | 低–中 | 临近到期价差与盘口恶化 | `min_option_hours`;到期强平 |
|
||||
| **SIM≠实盘** | 高(若直接外推) | 本地撮合、深度与成交假设简化 | 小资金实盘验证后再放大 |
|
||||
| **交易所限流 / 断连** | 中 | 行情中断导致延误开平 | 健康检查;部署侧监控 |
|
||||
|
||||
### 6.2 明确不做什么(边界)
|
||||
|
||||
- 不做卖方期权(不收权利金那套)。
|
||||
- 不做横盘「智能识别」开仓过滤。
|
||||
- 不做多组并行。
|
||||
- 当前主路径为 **SIM**;实盘需另行授权与二次确认(见商业化方案)。
|
||||
|
||||
### 6.3 损益直觉
|
||||
|
||||
- **有利波动**:期权端弹性(2 ETH 名义)常快于永续(1 ETH)的对冲损耗 → 易达净利目标。
|
||||
- **不利 / 无波动**:权利金贬值 + 费用 → 拖到到期全平,单组亏损量级约在 **权利金量级 + 双边费用**(再叠加永续期间浮亏/浮盈)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 最小资金推荐
|
||||
|
||||
以下按 **默认仓位**(永续 1 ETH、期权 2 ETH 名义、杠杆 3×)估算,标的价按 **ETH ≈ 1,800–2,200 USDT** 量级。
|
||||
|
||||
### 7.1 单组占用粗算
|
||||
|
||||
| 项目 | 约略金额(USDT) |
|
||||
|------|------------------|
|
||||
| 永续保证金(名义÷杠杆) | ≈ 1,800–2,200 ÷ 3 ≈ **600–750** |
|
||||
| 期权权利金(ATM、短期限,2 ETH) | 常见约 **15–60**(行情波动大时更高) |
|
||||
| 开平手续费 / 滑点缓冲 | **20–50** |
|
||||
| 逆向波动缓冲(避免过紧) | **300–800**(经验缓冲,非精确 VaR) |
|
||||
|
||||
### 7.2 建议档位
|
||||
|
||||
| 档位 | 建议权益(USDT) | 适用 |
|
||||
|------|------------------|------|
|
||||
| **绝对下限(不推荐)** | ≥ **1,500** | 仅能勉强覆盖保证金 + 小权利金,缓冲极薄,易心态与风控双紧 |
|
||||
| **最小可用** | ≥ **3,000** | 默认参数下可跑 SIM / 极小实盘验证,仍偏紧 |
|
||||
| **推荐起步** | ≥ **5,000** | 留出磨损日与波动缓冲,更符合「概率样本」玩法 |
|
||||
| **较舒适** | ≥ **10,000** | 参数微调、连续多日磨损时更从容 |
|
||||
|
||||
> 缩放规则:若把永续改为 `0.5 ETH`、期权 `1 ETH`,资金需求大约可按比例减半,但费用占比会上升,小目标更难赚。
|
||||
|
||||
### 7.3 与出场目标的关系
|
||||
|
||||
- 默认净利目标 **15 USDT**:在费用后属于「小而频」目标,依赖波动日多次兑现。
|
||||
- 单日/单组最坏常见情形更接近 **亏掉当期权利金 + 费用**(外加永续路径损益),资金规划应按 **多次磨损日** 而非「每组必赚 15」来留余量。
|
||||
|
||||
### 7.4 SIM 默认
|
||||
|
||||
系统默认虚拟权益 `initial_equity = 10,000` USDT(策略设置可改;保存且数值变更时在无持仓下重置账本),**不代表**实盘建议入金。
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键可配参数速查
|
||||
|
||||
| 参数 | 默认 | 类别 |
|
||||
|------|------|------|
|
||||
| `perp_qty_eth` | 1 | 仓位 |
|
||||
| `option_qty_eth` | 2 | 仓位 |
|
||||
| `leverage` | 3 | 永续 |
|
||||
| `fee_rate` | 0.0005 | 成本 |
|
||||
| `exit_mode` | fixed_usdt | 出场 |
|
||||
| `net_profit_target` | 15 | 出场 |
|
||||
| `premium_exit_multiple` | 1.0 | 出场 |
|
||||
| `rest_seconds` | 300 | 节奏 |
|
||||
| `skip_weekends` | true | 时间 |
|
||||
| `min_option_hours` | 12 | 选约 |
|
||||
| `min_option_leverage` | 100 | 选约 |
|
||||
| `close_bid_mark_max_pct` | 30 | 平仓流动性 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 修订记录
|
||||
|
||||
| 日期 | 说明 |
|
||||
|------|------|
|
||||
| 2026-07-25 | 初稿:对齐当前开平仓、周末跳过、到期全平、净盈利口径与资金建议 |
|
||||
@@ -34,6 +34,7 @@ export default function SettingsPage() {
|
||||
const [closeDevPct, setCloseDevPct] = useState(30);
|
||||
const [perpQty, setPerpQty] = useState(1);
|
||||
const [optQty, setOptQty] = useState(2);
|
||||
const [initialEquity, setInitialEquity] = useState(10000);
|
||||
const [stratOk, setStratOk] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,6 +52,7 @@ export default function SettingsPage() {
|
||||
setCloseDevPct(s.close_bid_mark_max_pct ?? 30);
|
||||
setPerpQty(s.perp_qty_eth ?? 1);
|
||||
setOptQty(s.option_qty_eth ?? 2);
|
||||
setInitialEquity(s.initial_equity ?? 10000);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
}, []);
|
||||
@@ -106,9 +108,12 @@ export default function SettingsPage() {
|
||||
close_bid_mark_max_pct: closeDevPct,
|
||||
perp_qty_eth: perpQty,
|
||||
option_qty_eth: optQty,
|
||||
initial_equity: initialEquity,
|
||||
}),
|
||||
});
|
||||
setStratOk("策略参数已保存");
|
||||
setStratOk(
|
||||
"策略参数已保存(模拟资金仅在数值变更且无持仓时重置账本)",
|
||||
);
|
||||
} catch (ex) {
|
||||
setErr(ex instanceof Error ? ex.message : String(ex));
|
||||
}
|
||||
@@ -145,6 +150,21 @@ export default function SettingsPage() {
|
||||
<section className="settings-section">
|
||||
<h3>资金</h3>
|
||||
<div className="settings-fields">
|
||||
<div className="field">
|
||||
<label htmlFor="equity">模拟资金(USDT)</label>
|
||||
<input
|
||||
id="equity"
|
||||
className="mono"
|
||||
type="number"
|
||||
step="100"
|
||||
min="1000"
|
||||
value={initialEquity}
|
||||
onChange={(e) => setInitialEquity(Number(e.target.value))}
|
||||
/>
|
||||
<p className="settings-hint">
|
||||
保存后将权益重置为该金额(须无持仓)。默认 10000。
|
||||
</p>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="lev">永续杠杆倍数</label>
|
||||
<input
|
||||
|
||||
@@ -433,6 +433,13 @@ input {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.settings-hint {
|
||||
margin: 6px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.settings-actions {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user