Support dual breakout targets for options-options hedges.
Replace single S* with up/down targets across UI, preview, persist, monitor, and alerts so ranging breakouts can close the winner either way. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -324,8 +324,14 @@
|
||||
fillExpSelect($("hp-oo-exp-select"), d);
|
||||
renderListStrikes();
|
||||
renderTStrikes();
|
||||
if (d.index_px && $("hp-target") && !$("hp-target").value) {
|
||||
$("hp-target").value = d.index_px;
|
||||
if (d.index_px) {
|
||||
const idx = Number(d.index_px);
|
||||
if ($("hp-target-up") && !$("hp-target-up").value) {
|
||||
$("hp-target-up").value = String(Math.round(idx * 1.03));
|
||||
}
|
||||
if ($("hp-target-down") && !$("hp-target-down").value) {
|
||||
$("hp-target-down").value = String(Math.round(idx * 0.97));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,7 +589,8 @@
|
||||
if ($("hp-contracts")) $("hp-contracts").value = "";
|
||||
if ($("hp-tp")) $("hp-tp").value = "";
|
||||
if ($("hp-sl")) $("hp-sl").value = "";
|
||||
if ($("hp-target")) $("hp-target").value = "";
|
||||
if ($("hp-target-up")) $("hp-target-up").value = "";
|
||||
if ($("hp-target-down")) $("hp-target-down").value = "";
|
||||
if ($("hp-sel-inst")) $("hp-sel-inst").textContent = "—";
|
||||
if ($("hp-premium-line")) $("hp-premium-line").textContent = "";
|
||||
if ($("hp-oo-sheets-a")) {
|
||||
@@ -606,12 +613,16 @@
|
||||
let body;
|
||||
if (isOo) {
|
||||
if (!state.legA || !state.legB) throw new Error("请选用两条期权腿");
|
||||
const target = Number(($("hp-target") && $("hp-target").value) || 0);
|
||||
if (!target) throw new Error("请填写目标价");
|
||||
const up = Number(($("hp-target-up") && $("hp-target-up").value) || 0);
|
||||
const down = Number(($("hp-target-down") && $("hp-target-down").value) || 0);
|
||||
if (!up || !down) throw new Error("请填写上破与下破目标价");
|
||||
if (up <= down) throw new Error("上破目标价必须大于下破目标价");
|
||||
body = {
|
||||
plan_type: "options_options",
|
||||
target_price: target,
|
||||
index_px: (state.chain && state.chain.index_px) || target,
|
||||
target_price_up: up,
|
||||
target_price_down: down,
|
||||
target_price: up,
|
||||
index_px: (state.chain && state.chain.index_px) || (up + down) / 2,
|
||||
leg_a: legPayload(state.legA, ooSheets("hp-oo-sheets-a")),
|
||||
leg_b: legPayload(state.legB, ooSheets("hp-oo-sheets-b")),
|
||||
};
|
||||
@@ -862,6 +873,8 @@
|
||||
oo_expiry_loss: "期期到期亏损",
|
||||
oo_expiry_win: "期期到期盈利",
|
||||
target_win_leg: "期期平盈利腿",
|
||||
target_up_win_leg: "期期上破·平盈利腿",
|
||||
target_down_win_leg: "期期下破·平盈利腿",
|
||||
expiry: "到期",
|
||||
manual: "人工结束",
|
||||
partial_fail: "半腿失败",
|
||||
@@ -930,7 +943,12 @@
|
||||
fmt(p.perp_size, 4) +
|
||||
"</div>";
|
||||
} else {
|
||||
html += "<div><span class=\"muted\">目标价 S*</span> " + fmt(p.target_price) + "</div>";
|
||||
html +=
|
||||
"<div><span class=\"muted\">目标价</span> 上破 " +
|
||||
fmt(p.target_price_up || p.target_price) +
|
||||
" · 下破 " +
|
||||
fmt(p.target_price_down || p.target_price) +
|
||||
"</div>";
|
||||
}
|
||||
html +=
|
||||
"<div><span class=\"muted\">权利金合计</span> " +
|
||||
@@ -1084,12 +1102,16 @@
|
||||
let body;
|
||||
if (isOo) {
|
||||
if (!state.legA || !state.legB) throw new Error("请选用两条期权腿");
|
||||
const target = Number(($("hp-target") && $("hp-target").value) || 0);
|
||||
if (!target) throw new Error("请填写目标价");
|
||||
const up = Number(($("hp-target-up") && $("hp-target-up").value) || 0);
|
||||
const down = Number(($("hp-target-down") && $("hp-target-down").value) || 0);
|
||||
if (!up || !down) throw new Error("请填写上破与下破目标价");
|
||||
if (up <= down) throw new Error("上破目标价必须大于下破目标价");
|
||||
body = {
|
||||
plan_type: "options_options",
|
||||
underlying: state.underlying,
|
||||
target_price: target,
|
||||
target_price_up: up,
|
||||
target_price_down: down,
|
||||
target_price: up,
|
||||
leg_a: legPayload(state.legA, ooSheets("hp-oo-sheets-a")),
|
||||
leg_b: legPayload(state.legB, ooSheets("hp-oo-sheets-b")),
|
||||
};
|
||||
|
||||
@@ -3403,6 +3403,22 @@ html[data-theme="light"] .opt-be-dist-down {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.hedge-plan-page-wrap .hp-target-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin: 6px 0;
|
||||
}
|
||||
.hedge-plan-page-wrap .hp-target-row label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.hedge-plan-page-wrap .hp-target-row input {
|
||||
width: 110px;
|
||||
}
|
||||
.hedge-plan-page-wrap .hp-contracts-cell {
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -223,12 +223,14 @@ def _hedge_ratio(opt_pnl: float, perp_pnl: float) -> Optional[float]:
|
||||
|
||||
def build_options_options_preview(
|
||||
*,
|
||||
target_price: float,
|
||||
target_price: float | None = None,
|
||||
target_price_up: float | None = None,
|
||||
target_price_down: float | None = None,
|
||||
index_px: float,
|
||||
leg_a: dict[str, Any],
|
||||
leg_b: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""期期情景:目标价 / 到期现价 / 到期两边."""
|
||||
"""期期情景:上破/下破目标价 / 到期现价 / 最大保费损耗."""
|
||||
|
||||
def _leg_pnl(leg: dict[str, Any], spot: float) -> float:
|
||||
return option_expiry_pnl(
|
||||
@@ -240,11 +242,25 @@ def build_options_options_preview(
|
||||
premium_paid=float(leg.get("premium_paid") or 0),
|
||||
)
|
||||
|
||||
# 兼容旧单目标:若未传上下目标则用 target_price 填两边
|
||||
up = target_price_up if target_price_up is not None else target_price
|
||||
down = target_price_down if target_price_down is not None else target_price
|
||||
if up is None or down is None:
|
||||
raise ValueError("缺少上破/下破目标价")
|
||||
up_f = float(up)
|
||||
down_f = float(down)
|
||||
|
||||
prem = float(leg_a.get("premium_paid") or 0) + float(leg_b.get("premium_paid") or 0)
|
||||
a_t = _leg_pnl(leg_a, target_price)
|
||||
b_t = _leg_pnl(leg_b, target_price)
|
||||
at_target = a_t + b_t
|
||||
win_leg = "a" if a_t >= b_t else "b"
|
||||
a_up = _leg_pnl(leg_a, up_f)
|
||||
b_up = _leg_pnl(leg_b, up_f)
|
||||
at_up = a_up + b_up
|
||||
win_up = "a" if a_up >= b_up else "b"
|
||||
|
||||
a_dn = _leg_pnl(leg_a, down_f)
|
||||
b_dn = _leg_pnl(leg_b, down_f)
|
||||
at_dn = a_dn + b_dn
|
||||
win_dn = "a" if a_dn >= b_dn else "b"
|
||||
|
||||
a_flat = _leg_pnl(leg_a, index_px)
|
||||
b_flat = _leg_pnl(leg_b, index_px)
|
||||
flat_total = a_flat + b_flat
|
||||
@@ -253,17 +269,30 @@ def build_options_options_preview(
|
||||
return {
|
||||
"plan_type": "options_options",
|
||||
"premium_paid": round(prem, 6),
|
||||
"target_price": target_price,
|
||||
"winner_at_target": win_leg,
|
||||
"target_price": up_f, # 兼容旧字段,取上破
|
||||
"target_price_up": up_f,
|
||||
"target_price_down": down_f,
|
||||
"winner_at_up": win_up,
|
||||
"winner_at_down": win_dn,
|
||||
"winner_at_target": win_up,
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "target",
|
||||
"label": "到达目标价",
|
||||
"spot": target_price,
|
||||
"leg_a_pnl": round(a_t, 4),
|
||||
"leg_b_pnl": round(b_t, 4),
|
||||
"total": round(at_target, 4),
|
||||
"note": f"盈利方≈腿{win_leg.upper()}(可平);亏损方默认到期",
|
||||
"id": "target_up",
|
||||
"label": "上破目标",
|
||||
"spot": up_f,
|
||||
"leg_a_pnl": round(a_up, 4),
|
||||
"leg_b_pnl": round(b_up, 4),
|
||||
"total": round(at_up, 4),
|
||||
"note": f"盈利方≈腿{win_up.upper()}(可平);亏损方默认到期",
|
||||
},
|
||||
{
|
||||
"id": "target_down",
|
||||
"label": "下破目标",
|
||||
"spot": down_f,
|
||||
"leg_a_pnl": round(a_dn, 4),
|
||||
"leg_b_pnl": round(b_dn, 4),
|
||||
"total": round(at_dn, 4),
|
||||
"note": f"盈利方≈腿{win_dn.upper()}(可平);亏损方默认到期",
|
||||
},
|
||||
{
|
||||
"id": "expiry_flat",
|
||||
@@ -285,9 +314,11 @@ def build_options_options_preview(
|
||||
},
|
||||
],
|
||||
"summary": {
|
||||
"at_target_total": round(at_target, 4),
|
||||
"at_target_up_total": round(at_up, 4),
|
||||
"at_target_down_total": round(at_dn, 4),
|
||||
"at_target_total": round(at_up, 4),
|
||||
"expiry_flat_total": round(expiry_loss, 4),
|
||||
"premium_paid": round(prem, 4),
|
||||
"premium_paid": round(prem, 6),
|
||||
"expiry_is_loss": flat_total <= 0,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -70,6 +70,20 @@ def init_hedge_plan_tables(conn: sqlite3.Connection) -> None:
|
||||
conn.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_hedge_plan_legs_plan ON hedge_plan_legs(plan_id)"
|
||||
)
|
||||
_ensure_column(conn, "hedge_plans", "target_price_up", "REAL")
|
||||
_ensure_column(conn, "hedge_plans", "target_price_down", "REAL")
|
||||
|
||||
|
||||
def _ensure_column(conn: sqlite3.Connection, table: str, col: str, typedef: str) -> None:
|
||||
rows = conn.execute(f"PRAGMA table_info({table})").fetchall()
|
||||
names: set[str] = set()
|
||||
for r in rows:
|
||||
try:
|
||||
names.add(str(r["name"]))
|
||||
except (TypeError, KeyError, IndexError):
|
||||
names.add(str(r[1]))
|
||||
if col not in names:
|
||||
conn.execute(f"ALTER TABLE {table} ADD COLUMN {col} {typedef}")
|
||||
|
||||
|
||||
def count_active_plans(conn: sqlite3.Connection, plan_type: Optional[str] = None) -> int:
|
||||
|
||||
@@ -235,12 +235,29 @@ def _tick_po(cfg: dict[str, Any], conn: Any, plan: dict[str, Any], legs: list[di
|
||||
def _tick_oo_target(
|
||||
cfg: dict[str, Any], conn: Any, plan: dict[str, Any], legs: list[dict[str, Any]]
|
||||
) -> Optional[dict[str, Any]]:
|
||||
target = _sf(plan.get("target_price"))
|
||||
"""期期:触及上破或下破目标价时平盈利腿."""
|
||||
idx = _index_px(cfg, str(plan.get("underlying") or "ETH"))
|
||||
if target is None or idx is None:
|
||||
if idx is None:
|
||||
return None
|
||||
near = abs(idx - target) / max(abs(target), 1.0) <= 0.002
|
||||
if not near:
|
||||
up = _sf(plan.get("target_price_up"))
|
||||
down = _sf(plan.get("target_price_down"))
|
||||
# 旧计划仅有单目标:两边都用它
|
||||
legacy = _sf(plan.get("target_price"))
|
||||
if up is None and legacy is not None:
|
||||
up = legacy
|
||||
if down is None and legacy is not None:
|
||||
down = legacy
|
||||
if up is None and down is None:
|
||||
return None
|
||||
|
||||
hit_side: Optional[str] = None
|
||||
# 上破:现价接近或超过上破目标
|
||||
if up is not None and idx >= up * 0.998:
|
||||
hit_side = "up"
|
||||
# 下破:现价接近或低于下破目标
|
||||
elif down is not None and idx <= down * 1.002:
|
||||
hit_side = "down"
|
||||
if not hit_side:
|
||||
return None
|
||||
if not _env_bool("HEDGE_PLAN_OO_CLOSE_WINNER_ONLY", True):
|
||||
return None
|
||||
@@ -270,16 +287,23 @@ def _tick_oo_target(
|
||||
),
|
||||
)
|
||||
return {"plan_id": plan["id"], "msg": "平盈利腿失败", "close": close_r}
|
||||
reason = "target_up_win_leg" if hit_side == "up" else "target_down_win_leg"
|
||||
conn.execute(
|
||||
"UPDATE hedge_plan_legs SET status=?, close_reason=?, closed_at=?, realized_pnl=? WHERE id=?",
|
||||
("closed", "target_win_leg", _now(), best_pnl, best["id"]),
|
||||
("closed", reason, _now(), best_pnl, best["id"]),
|
||||
)
|
||||
update_plan(conn, int(plan["id"]), close_reason="target_win_leg")
|
||||
update_plan(conn, int(plan["id"]), close_reason=reason)
|
||||
mid = dict(plan)
|
||||
mid["close_reason"] = "target_win_leg"
|
||||
mid["close_reason"] = reason
|
||||
mid["status"] = "active"
|
||||
notify_plan_end(cfg, conn, mid)
|
||||
return {"plan_id": plan["id"], "close_reason": "target_win_leg", "closed_leg": best.get("id")}
|
||||
return {
|
||||
"plan_id": plan["id"],
|
||||
"close_reason": reason,
|
||||
"hit_side": hit_side,
|
||||
"closed_leg": best.get("id"),
|
||||
"index": idx,
|
||||
}
|
||||
|
||||
|
||||
def _tick_oo_expiry(
|
||||
|
||||
@@ -48,7 +48,8 @@ def build_hedge_start_message(plan: dict[str, Any], *, legs: Optional[list[dict[
|
||||
else:
|
||||
lines.extend(
|
||||
[
|
||||
f"🎯 目标价 S*:{_fmt(plan.get('target_price'))}",
|
||||
f"🎯 上破:{_fmt(plan.get('target_price_up') or plan.get('target_price'))}"
|
||||
f"|下破:{_fmt(plan.get('target_price_down') or plan.get('target_price'))}",
|
||||
f"💎 期权保费合计:{_fmt(plan.get('premium_total'), 4)} USDC",
|
||||
]
|
||||
)
|
||||
@@ -78,6 +79,8 @@ def build_hedge_end_message(plan: dict[str, Any]) -> str:
|
||||
"perp_tp": "永续止盈(期权默认不平)",
|
||||
"perp_sl": "永续止损(期权强制平)",
|
||||
"target_win_leg": "期期已平盈利腿(中间态)",
|
||||
"target_up_win_leg": "期期上破·已平盈利腿",
|
||||
"target_down_win_leg": "期期下破·已平盈利腿",
|
||||
"oo_expiry_loss": "期期到期无盈利·总亏损",
|
||||
"oo_expiry_win": "期期到期仍盈利",
|
||||
"expiry": "到期收口",
|
||||
@@ -143,13 +146,23 @@ def notify_plan_end(cfg: dict[str, Any], conn: Any, plan: dict[str, Any]) -> boo
|
||||
if int(plan.get("wechat_end_sent") or 0):
|
||||
return False
|
||||
# 中间态 target_win_leg 不算正式结束推送(用告警)
|
||||
if (plan.get("close_reason") or "") == "target_win_leg" and (plan.get("status") or "") != "closed":
|
||||
if (plan.get("close_reason") or "") in (
|
||||
"target_win_leg",
|
||||
"target_up_win_leg",
|
||||
"target_down_win_leg",
|
||||
) and (plan.get("status") or "") != "closed":
|
||||
side = "上破" if "up" in str(plan.get("close_reason")) else (
|
||||
"下破" if "down" in str(plan.get("close_reason")) else "目标价"
|
||||
)
|
||||
notify_hedge(
|
||||
cfg,
|
||||
build_hedge_alert_message(
|
||||
title="期期已平盈利腿,亏损腿继续持有至到期",
|
||||
title=f"期期{side}已平盈利腿,亏损腿继续持有至到期",
|
||||
plan_id=plan.get("id"),
|
||||
detail=f"目标价 {_fmt(plan.get('target_price'))}",
|
||||
detail=(
|
||||
f"上破 {_fmt(plan.get('target_price_up') or plan.get('target_price'))}"
|
||||
f"|下破 {_fmt(plan.get('target_price_down') or plan.get('target_price'))}"
|
||||
),
|
||||
),
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -382,8 +382,20 @@ def validate_start_body(plan_type: str, body: dict[str, Any]) -> Optional[str]:
|
||||
b = body.get("leg_b") or {}
|
||||
if not a.get("inst_id") or not b.get("inst_id"):
|
||||
return "请选用两条期权腿"
|
||||
if body.get("target_price") in (None, ""):
|
||||
return "缺少目标价"
|
||||
up = body.get("target_price_up")
|
||||
down = body.get("target_price_down")
|
||||
legacy = body.get("target_price")
|
||||
if up in (None, "") and legacy not in (None, ""):
|
||||
up = legacy
|
||||
if down in (None, "") and legacy not in (None, ""):
|
||||
down = legacy
|
||||
if up in (None, "") or down in (None, ""):
|
||||
return "请填写上破与下破目标价"
|
||||
try:
|
||||
if float(up) <= float(down):
|
||||
return "上破目标价必须大于下破目标价"
|
||||
except (TypeError, ValueError):
|
||||
return "目标价无效"
|
||||
return None
|
||||
return "未知计划类型"
|
||||
|
||||
|
||||
@@ -269,7 +269,21 @@ def _persist_oo(cfg: dict[str, Any], result: dict[str, Any], body: dict[str, Any
|
||||
"plan_type": "options_options",
|
||||
"status": "active",
|
||||
"underlying": str(body.get("underlying") or "ETH").upper(),
|
||||
"target_price": float(body.get("target_price") or 0),
|
||||
"target_price": float(
|
||||
body.get("target_price_up")
|
||||
or body.get("target_price")
|
||||
or 0
|
||||
),
|
||||
"target_price_up": float(
|
||||
body.get("target_price_up")
|
||||
or body.get("target_price")
|
||||
or 0
|
||||
),
|
||||
"target_price_down": float(
|
||||
body.get("target_price_down")
|
||||
or body.get("target_price")
|
||||
or 0
|
||||
),
|
||||
"sizing_mode_at_open": load_position_sizing_mode(),
|
||||
"premium_total": premium,
|
||||
"opened_at": result.get("opened_at"),
|
||||
@@ -610,8 +624,20 @@ def _preview_po(body: dict[str, Any]) -> dict[str, Any]:
|
||||
|
||||
|
||||
def _preview_oo(body: dict[str, Any]) -> dict[str, Any]:
|
||||
target = float(body["target_price"])
|
||||
index_px = float(body.get("index_px") or target)
|
||||
up = body.get("target_price_up")
|
||||
down = body.get("target_price_down")
|
||||
legacy = body.get("target_price")
|
||||
if up in (None, "") and legacy not in (None, ""):
|
||||
up = legacy
|
||||
if down in (None, "") and legacy not in (None, ""):
|
||||
down = legacy
|
||||
if up in (None, "") or down in (None, ""):
|
||||
raise ValueError("请填写上破与下破目标价")
|
||||
up_f = float(up)
|
||||
down_f = float(down)
|
||||
if up_f <= down_f:
|
||||
raise ValueError("上破目标价必须大于下破目标价")
|
||||
index_px = float(body.get("index_px") or ((up_f + down_f) / 2))
|
||||
leg_a = body.get("leg_a") or {}
|
||||
leg_b = body.get("leg_b") or {}
|
||||
for name, leg in (("leg_a", leg_a), ("leg_b", leg_b)):
|
||||
@@ -626,7 +652,8 @@ def _preview_oo(body: dict[str, Any]) -> dict[str, Any]:
|
||||
if leg.get("premium_paid") is None:
|
||||
raise ValueError(f"缺少 {name} 权利金")
|
||||
return build_options_options_preview(
|
||||
target_price=target,
|
||||
target_price_up=up_f,
|
||||
target_price_down=down_f,
|
||||
index_px=index_px,
|
||||
leg_a=leg_a,
|
||||
leg_b=leg_b,
|
||||
|
||||
@@ -118,11 +118,14 @@
|
||||
<div class="form-row hp-uly-row">
|
||||
<button type="button" class="btn-secondary hp-uly-btn-oo active" data-uly="ETH">ETH</button>
|
||||
<button type="button" class="btn-secondary hp-uly-btn-oo" data-uly="BTC">BTC</button>
|
||||
<label>目标价 S* <input type="number" step="any" id="hp-target" /></label>
|
||||
</div>
|
||||
<div class="form-row hp-target-row">
|
||||
<label>上破目标 <input type="number" step="any" id="hp-target-up" placeholder="向上突破" /></label>
|
||||
<label>下破目标 <input type="number" step="any" id="hp-target-down" placeholder="向下突破" /></label>
|
||||
</div>
|
||||
<div id="hp-oo-index" class="muted hp-quote-line"></div>
|
||||
<div id="hp-oo-bal-line" class="muted hp-quote-line"></div>
|
||||
<p class="muted hp-unit-hint">单位说明:目标价=指数价(USD) · 张数=<strong>期权张</strong>(整张) · 权利金结算币=USDC</p>
|
||||
<p class="muted hp-unit-hint">震荡突破:设上下两个目标价(USD);触达任一侧重平盈利腿。张数=<strong>期权张</strong> · 权利金=USDC</p>
|
||||
<div id="hp-oo-legs" class="hp-oo-legs">
|
||||
<div class="hp-oo-leg-row" data-leg="a">
|
||||
<div class="muted" id="hp-oo-leg-a-info">腿A: 尚未选用</div>
|
||||
@@ -186,7 +189,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="hp-result-tbody-oo">
|
||||
<tr><td colspan="6" class="muted">选用两腿并填目标价后点计算</td></tr>
|
||||
<tr><td colspan="6" class="muted">选用两腿并填上破/下破目标后点计算</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -230,4 +233,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/hedge_plan.js?v=11"></script>
|
||||
<script src="/static/hedge_plan.js?v=12"></script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=6">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=89">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=90">
|
||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||
<meta name="theme-color" content="#0b0d14">
|
||||
<title>{{ exchange_display }} · 加密货币 | 交易监控复盘系统</title>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
||||
<title>{{ exchange_display }} · 加密货币 | 交易监控复盘系统</title>
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=3">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=89">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=90">
|
||||
|
||||
</head>
|
||||
<body
|
||||
|
||||
@@ -72,9 +72,25 @@ class TestHedgePlanCalc(unittest.TestCase):
|
||||
def test_oo_expiry_loss_flag(self):
|
||||
a = {"opt_type": "C", "strike": 3300, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
b = {"opt_type": "P", "strike": 3100, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
p = build_options_options_preview(target_price=3500, index_px=3200, leg_a=a, leg_b=b)
|
||||
p = build_options_options_preview(
|
||||
target_price_up=3500,
|
||||
target_price_down=3000,
|
||||
index_px=3200,
|
||||
leg_a=a,
|
||||
leg_b=b,
|
||||
)
|
||||
self.assertEqual(p["summary"]["premium_paid"], 10)
|
||||
self.assertTrue(p["summary"]["expiry_is_loss"])
|
||||
self.assertEqual(len(p["scenarios"]), 4)
|
||||
self.assertEqual(p["scenarios"][0]["id"], "target_up")
|
||||
self.assertEqual(p["scenarios"][1]["id"], "target_down")
|
||||
|
||||
def test_oo_legacy_single_target_still_works(self):
|
||||
a = {"opt_type": "C", "strike": 3300, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
b = {"opt_type": "P", "strike": 3100, "sheets": 1, "ct_mult": 0.01, "premium_paid": 5}
|
||||
p = build_options_options_preview(target_price=3500, index_px=3200, leg_a=a, leg_b=b)
|
||||
self.assertEqual(p["target_price_up"], 3500)
|
||||
self.assertEqual(p["target_price_down"], 3500)
|
||||
|
||||
def test_perp_short_pnl(self):
|
||||
self.assertEqual(
|
||||
|
||||
@@ -148,6 +148,8 @@ class TestHedgePlanOrderPath(unittest.TestCase):
|
||||
}
|
||||
body = {
|
||||
"target_price": 1900,
|
||||
"target_price_up": 1950,
|
||||
"target_price_down": 1750,
|
||||
"leg_a": {"inst_id": "A", "sheets": 1},
|
||||
"leg_b": {"inst_id": "B", "sheets": 1},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user