Use absolute coin counts in perp-options points mode.
Treat 2:4 as 2 perp + 4 option coins instead of normalizing to 1:2, and disable embed page caching so hub iframe picks up trade UI updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -294,6 +294,7 @@
|
||||
async function fetchTabHtml(tab) {
|
||||
const r = await fetch(embedPageUrl(tab), {
|
||||
credentials: "same-origin",
|
||||
cache: "no-store",
|
||||
headers: { "X-Instance-Soft-Nav": "1" },
|
||||
});
|
||||
const ct = (r.headers.get("content-type") || "").toLowerCase();
|
||||
|
||||
@@ -63,25 +63,41 @@ def _parse_base_common(
|
||||
}, None
|
||||
|
||||
|
||||
def _move_for_perp_correct(*, spot: float, target: float, premium: float, fee_rate: float) -> float:
|
||||
"""净利 = move − premium − fee(move) = target → 解 move.
|
||||
def _move_for_perp_correct(
|
||||
*,
|
||||
spot: float,
|
||||
target: float,
|
||||
premium: float,
|
||||
fee_rate: float,
|
||||
perp_coins: float = 1.0,
|
||||
) -> float:
|
||||
"""净利 = qty*move − premium − fee(move,qty) = target → 解 move.
|
||||
|
||||
fee = (2*spot + move) * fee_rate
|
||||
move*(1-fee_rate) = target + premium + 2*spot*fee_rate
|
||||
fee = (2*spot + move) * qty * fee_rate
|
||||
qty*move*(1-fee_rate) = target + premium + 2*spot*qty*fee_rate
|
||||
"""
|
||||
denom = 1.0 - float(fee_rate)
|
||||
qty = float(perp_coins)
|
||||
if qty <= 0:
|
||||
return 0.0
|
||||
denom = qty * (1.0 - float(fee_rate))
|
||||
if denom <= 0:
|
||||
return 0.0
|
||||
return (float(target) + float(premium) + 2.0 * float(spot) * float(fee_rate)) / denom
|
||||
return (float(target) + float(premium) + 2.0 * float(spot) * qty * float(fee_rate)) / denom
|
||||
|
||||
|
||||
def _case_sideways(*, spot: float, premium_total: float) -> dict[str, Any]:
|
||||
def _case_sideways(
|
||||
*,
|
||||
spot: float,
|
||||
premium_total: float,
|
||||
perp_coins: float = 1.0,
|
||||
) -> dict[str, Any]:
|
||||
"""横盘/到期无方向:永续≈0,期权权利金全亏,另计永续开平同价手续费.
|
||||
|
||||
最大亏损(正数) = 权利金总额 + 开平手续费(exit=entry)
|
||||
组合净利 = −最大亏损
|
||||
"""
|
||||
fee_flat = estimate_roundtrip_fee_usdt(spot, spot, qty=PERP_COINS, contract_size=1.0)
|
||||
qty = float(perp_coins) if float(perp_coins) > 0 else PERP_COINS
|
||||
fee_flat = estimate_roundtrip_fee_usdt(spot, spot, qty=qty, contract_size=1.0)
|
||||
prem = float(premium_total)
|
||||
max_loss = prem + float(fee_flat)
|
||||
return {
|
||||
@@ -221,12 +237,12 @@ def calc_perp_options_points(
|
||||
ratio_opt: float = 2.0,
|
||||
ct_mult: float = DEFAULT_CT_MULT,
|
||||
) -> Tuple[Optional[dict[str, Any]], Optional[str]]:
|
||||
"""按永续:期权比例 + 目标盈利,反推两套情景所需波动点数.
|
||||
"""按永续/期权币数 + 目标盈利,反推两套情景所需波动点数.
|
||||
|
||||
永续币数固定为 ratio 归一后的 1 币侧(perp_coins = PERP_COINS).
|
||||
期权币数 = PERP_COINS * (ratio_opt / ratio_perp),例 1:2 → 2 币.
|
||||
永续币数 = ratio_perp, 期权币数 = ratio_opt(按绝对币数,不再归一到 1 币).
|
||||
例 2:4 → 永续 2 币 + 期权 4 币;1:2 → 永续 1 币 + 期权 2 币.
|
||||
|
||||
A 永续方向对: move − premium − fee(move) = 目标盈利
|
||||
A 永续方向对: qty*move − premium − fee(move,qty) = 目标盈利
|
||||
B 期权方向对:
|
||||
- 期权净利达目标: opt_coins*move − premium = 目标
|
||||
- 组合净利达目标: move*(opt_coins − perp_coins) − premium = 目标
|
||||
@@ -246,7 +262,7 @@ def calc_perp_options_points(
|
||||
rp = _f(ratio_perp)
|
||||
ro = _f(ratio_opt)
|
||||
if rp is None or ro is None or rp <= 0 or ro <= 0:
|
||||
return None, "永续:期权比例须大于 0"
|
||||
return None, "永续/期权币数须大于 0"
|
||||
|
||||
s = common["spot"]
|
||||
capital = common["capital"]
|
||||
@@ -255,28 +271,35 @@ def calc_perp_options_points(
|
||||
o_lev = common["o_lev"]
|
||||
ct = common["ct"]
|
||||
prem_per_coin = common["prem_per_coin"]
|
||||
margin = common["margin"]
|
||||
fee_rate = common["fee_rate"]
|
||||
b = (base or "ETH").strip().upper()
|
||||
|
||||
opt_coins = PERP_COINS * (ro / rp)
|
||||
perp_coins = rp
|
||||
opt_coins = ro
|
||||
premium_total = opt_coins * prem_per_coin
|
||||
opt_sheets = opt_coins / ct
|
||||
margin = (s * perp_coins) / p_lev
|
||||
|
||||
move_a = _move_for_perp_correct(spot=s, target=target, premium=premium_total, fee_rate=fee_rate)
|
||||
move_a = _move_for_perp_correct(
|
||||
spot=s,
|
||||
target=target,
|
||||
premium=premium_total,
|
||||
fee_rate=fee_rate,
|
||||
perp_coins=perp_coins,
|
||||
)
|
||||
if move_a <= 0:
|
||||
return None, "无法解出永续方向对所需点数"
|
||||
|
||||
fee_a = estimate_roundtrip_fee_usdt(s, s + move_a, qty=PERP_COINS, contract_size=1.0)
|
||||
net_a = move_a * PERP_COINS - premium_total - fee_a
|
||||
fee_a = estimate_roundtrip_fee_usdt(s, s + move_a, qty=perp_coins, contract_size=1.0)
|
||||
net_a = move_a * perp_coins - premium_total - fee_a
|
||||
|
||||
# 期权净利 = 目标
|
||||
move_b_opt = (target + premium_total) / opt_coins
|
||||
opt_net_at_b_opt = opt_coins * move_b_opt - premium_total
|
||||
portfolio_at_b_opt = opt_net_at_b_opt - move_b_opt * PERP_COINS
|
||||
portfolio_at_b_opt = opt_net_at_b_opt - move_b_opt * perp_coins
|
||||
|
||||
# 组合净利 = 目标
|
||||
edge = opt_coins - PERP_COINS
|
||||
edge = opt_coins - perp_coins
|
||||
if edge <= 0:
|
||||
move_b_port = None
|
||||
port_err = "期权币数须大于永续币数,组合才能在方向对时赚到目标盈利"
|
||||
@@ -285,7 +308,7 @@ def calc_perp_options_points(
|
||||
port_err = None
|
||||
if move_b_port is not None:
|
||||
opt_net_at_b_port = opt_coins * move_b_port - premium_total
|
||||
portfolio_at_b_port = opt_net_at_b_port - move_b_port * PERP_COINS
|
||||
portfolio_at_b_port = opt_net_at_b_port - move_b_port * perp_coins
|
||||
else:
|
||||
opt_net_at_b_port = None
|
||||
portfolio_at_b_port = None
|
||||
@@ -299,7 +322,7 @@ def calc_perp_options_points(
|
||||
"ratio_perp": round(rp, 8),
|
||||
"ratio_opt": round(ro, 8),
|
||||
"ratio_label": f"{_fmt_ratio(rp)}:{_fmt_ratio(ro)}",
|
||||
"perp_coins": PERP_COINS,
|
||||
"perp_coins": round(perp_coins, 8),
|
||||
"opt_coins": round(opt_coins, 8),
|
||||
"opt_sheets": round(opt_sheets, 8),
|
||||
"perp_leverage": round(p_lev, 8),
|
||||
@@ -314,7 +337,7 @@ def calc_perp_options_points(
|
||||
"label": "永续方向对",
|
||||
"move_points": round(move_a, 8),
|
||||
"move_pct": round(move_a / s * 100.0, 8),
|
||||
"perp_pnl_u": round(move_a * PERP_COINS, 8),
|
||||
"perp_pnl_u": round(move_a * perp_coins, 8),
|
||||
"premium_u": round(premium_total, 8),
|
||||
"fee_u": round(fee_a, 8),
|
||||
"net_u": round(net_a, 8),
|
||||
@@ -336,7 +359,9 @@ def calc_perp_options_points(
|
||||
"portfolio_error": port_err,
|
||||
"premium_u": round(premium_total, 8),
|
||||
},
|
||||
"case_sideways": _case_sideways(spot=s, premium_total=premium_total),
|
||||
"case_sideways": _case_sideways(
|
||||
spot=s, premium_total=premium_total, perp_coins=perp_coins
|
||||
),
|
||||
}, None
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import os
|
||||
from typing import Callable
|
||||
from urllib.parse import parse_qsl, urlencode, urlsplit
|
||||
|
||||
from flask import Flask, Response, jsonify, redirect, request, session
|
||||
from flask import Flask, Response, jsonify, make_response, redirect, request, session
|
||||
from jinja2 import ChoiceLoader, FileSystemLoader
|
||||
|
||||
EMBED_TABS: tuple[str, ...] = (
|
||||
@@ -184,7 +184,10 @@ def register_embed_routes(
|
||||
if tab not in EMBED_TABS:
|
||||
tab = "trade"
|
||||
session["hub_embed_shell"] = True
|
||||
return render_main_page_fn(tab, embed_mode="shell")
|
||||
resp = make_response(render_main_page_fn(tab, embed_mode="shell"))
|
||||
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||||
resp.headers["Pragma"] = "no-cache"
|
||||
return resp
|
||||
|
||||
@login_required
|
||||
@app.route("/api/embed/page/<tab>")
|
||||
@@ -198,7 +201,10 @@ def register_embed_routes(
|
||||
html = render_main_page_fn(tab, embed_mode="fragment")
|
||||
if isinstance(html, Response):
|
||||
html = html.get_data(as_text=True)
|
||||
return jsonify({"ok": True, "page": tab, "html": html})
|
||||
resp = jsonify({"ok": True, "page": tab, "html": html})
|
||||
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||||
resp.headers["Pragma"] = "no-cache"
|
||||
return resp
|
||||
|
||||
|
||||
def pwa_app_name(exchange_key: str) -> str:
|
||||
|
||||
@@ -168,7 +168,7 @@ window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=16"></script>
|
||||
<script src="/static/instance_live.js?v=6"></script>
|
||||
<script src="/static/instance_embed.js?v=29"></script>
|
||||
<script src="/static/instance_embed.js?v=30"></script>
|
||||
<script src="/static/instance_mobile_nav.js?v=2"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user