diff --git a/docs/振幅统计说明.md b/docs/振幅统计说明.md index 7a28989..2c17796 100644 --- a/docs/振幅统计说明.md +++ b/docs/振幅统计说明.md @@ -3,7 +3,8 @@ 中控只读工具:按自定义整点起点、**固定北京时间 16:00 收窗**,统计 OKX 上 ETH/BTC 的历史「点数振幅」档案,辅助一天期期权判断空间。 > 开发方案见 [ETH时段振幅统计-开发方案.md](./ETH时段振幅统计-开发方案.md)。 -> **不改下单链路**;不算 IV / 权利金。 +> 永期对冲公式见 [永期对冲计算器.md](./永期对冲计算器.md)。 +> **不改下单链路**;不算 IV。 --- @@ -81,6 +82,41 @@ K 线粒度:**1H**(与整点对齐);价源优先 OKX 指数(ETH-USD / --- +## 永期对冲对照 + +与中控 [永期对冲计算器](./永期对冲计算器.md) 同口径:**永续做多 1 币 + 买期权**(默认比例 **1:2**),在历史振幅日表上做对照。 + +表单填:现价、目标盈利、永续杠杆、期权杠杆、比例(可改)。未填齐现价/目标/杠杆时不计算该块。 + +```text +单币权利金 = 现价 / 期权杠杆 +期权币数 = 1 × (期权比例 / 永续比例) +权利金总额 = 期权币数 × 单币权利金 +``` + +### ① 所需点数达标 + +复用计算器「由比例推点数」: + +| 指标 | 规则 | +|------|------| +| A 所需点数 | 永续方向对、净利=目标 | +| A 达标 | 日 `涨跌 ≥ A点数` 的天数与占比 | +| B 所需点数 | 期权方向对、**组合净利**=目标 | +| B 达标 | 日 `涨跌 ≤ −B点数` 的天数与占比 | + +### ② 按日组合盈亏 + +| 日向 | 组合净利 | +|------|----------| +| 上涨 `涨跌≥0` | `涨跌 − 权利金 − 永续开平手续费(开→收)` | +| 下跌 `涨跌<0` | `\|涨跌\|×(期权币数−1) − 权利金` | + +汇总:合计、日均、胜率、上涨日/下跌日盈亏小计、单日最大赚亏;日表 **永期盈亏** 列。 +改永期参数 / 周末筛选会**本地重算**(不重拉 K 线)。权利金按表单现价固定,不按日开盘重估。 + +--- + ## 历史 Tab - 仅 **保存到历史** 后出现(不会一算就自动入库) @@ -109,3 +145,4 @@ K 线粒度:**1H**(与整点对齐);价源优先 OKX 指数(ETH-USD / | 2026-07-23 | 买跨对照:可设双边权利金、越过占比与收盘盈亏 | | 2026-07-23 | 周末筛选/标注、止盈点(≥)、日表收益列 | | 2026-07-23 | 长周期续拉 history K 线;收益列红绿着色 | +| 2026-07-28 | 永期对冲对照:所需点数达标 + 按日组合盈亏 | diff --git a/docs/永期对冲计算器.md b/docs/永期对冲计算器.md index 2e0b393..1de6630 100644 --- a/docs/永期对冲计算器.md +++ b/docs/永期对冲计算器.md @@ -165,3 +165,5 @@ move = (目标 + 权利金) / (期权币数 − 1) ## 不做 实盘开平仓、拉 OKX 期权链卖一、把本页结果自动写入对冲计划。 + +振幅统计页可对历史日表做同口径对照,见 [振幅统计说明.md](./振幅统计说明.md)「永期对冲对照」。 diff --git a/lib/hub/amp_stats_lib.py b/lib/hub/amp_stats_lib.py index aecb68d..ed27dc0 100644 --- a/lib/hub/amp_stats_lib.py +++ b/lib/hub/amp_stats_lib.py @@ -249,10 +249,12 @@ def enrich_rows_pnl( *, straddle_premium: Optional[float] = None, take_profit: Optional[float] = None, + perp_hedge: Any = None, ) -> list[dict[str, Any]]: - """为日表附加有效波动 / 是否触达止盈 / 收益(有权利金时).""" + """为日表附加有效波动 / 是否触达止盈 / 收益(有权利金时) / 永期盈亏.""" prem = normalize_straddle_premium(straddle_premium) tp = normalize_take_profit(take_profit) + hedge = normalize_perp_hedge_params(perp_hedge) out: list[dict[str, Any]] = [] for r in rows or []: item = dict(r) @@ -272,10 +274,200 @@ def enrich_rows_pnl( item["effective_move"] = round(move, 4) item["take_profit_hit"] = hit item["profit"] = round(move - prem, 4) if prem is not None else None + if hedge is not None: + item["perp_hedge_pnl"] = perp_hedge_day_pnl( + change=float(item.get("change") or 0), + open_px=float(item.get("open") or 0), + close_px=float(item.get("close") or 0), + premium_total=float(hedge["premium_total"]), + opt_coins=float(hedge["opt_coins"]), + ) + else: + item["perp_hedge_pnl"] = None out.append(item) return out +def normalize_perp_hedge_params(raw: Any) -> Optional[dict[str, float]]: + """永期对冲对照参数.缺必填则返回 None(不做对照). + + 接受 dict 或带 spot/target_profit_u/perp_leverage/option_leverage 的对象字段. + """ + if raw is None or raw == "": + return None + if not isinstance(raw, dict): + return None + spot = _safe_float(raw.get("spot")) + target = _safe_float(raw.get("target_profit_u") if "target_profit_u" in raw else raw.get("target")) + p_lev = _safe_float(raw.get("perp_leverage")) + o_lev = _safe_float(raw.get("option_leverage")) + rp = _safe_float(raw.get("ratio_perp")) + ro = _safe_float(raw.get("ratio_opt")) + ct = _safe_float(raw.get("ct_mult")) + if spot is None or target is None or p_lev is None or o_lev is None: + return None + if spot <= 0 or target < 0 or p_lev <= 0 or o_lev <= 0: + return None + if rp is None or rp <= 0: + rp = 1.0 + if ro is None or ro <= 0: + ro = 2.0 + if ct is None or ct <= 0: + ct = 0.01 + prem_per_coin = spot / o_lev + opt_coins = 1.0 * (ro / rp) + premium_total = opt_coins * prem_per_coin + return { + "spot": spot, + "target_profit_u": target, + "perp_leverage": p_lev, + "option_leverage": o_lev, + "ratio_perp": rp, + "ratio_opt": ro, + "ct_mult": ct, + "prem_per_coin": prem_per_coin, + "opt_coins": opt_coins, + "opt_sheets": opt_coins / ct, + "premium_total": premium_total, + } + + +def perp_hedge_day_pnl( + *, + change: float, + open_px: float, + close_px: float, + premium_total: float, + opt_coins: float, +) -> float: + """单日组合净利(永续多1币 + 买期权). + + 上涨: change − 权利金 − 永续开平手续费 + 下跌: |change|×(opt_coins−1) − 权利金 + """ + from lib.trade.trade_fee_lib import estimate_roundtrip_fee_usdt + + chg = float(change or 0) + prem = float(premium_total or 0) + coins = float(opt_coins or 0) + if chg >= 0: + fee = 0.0 + if open_px and close_px and open_px > 0 and close_px > 0: + fee = estimate_roundtrip_fee_usdt(open_px, close_px, qty=1.0, contract_size=1.0) + return round(chg - prem - fee, 4) + # 下跌: 永续亏 chg(负), 期权内在 |chg|*coins + return round(abs(chg) * (coins - 1.0) - prem, 4) + + +def perp_hedge_stats( + rows: list[dict[str, Any]], + hedge: dict[str, float], +) -> dict[str, Any]: + """永期对冲:所需点数达标 + 按日组合盈亏汇总.""" + from lib.hub.hub_perp_options_calc_lib import calc_perp_options_points + + points_data, points_err = calc_perp_options_points( + base="ETH", + spot=hedge["spot"], + capital_usdt=max(hedge["spot"] / hedge["perp_leverage"] * 2, 1000.0), + target_profit_u=hedge["target_profit_u"], + perp_leverage=hedge["perp_leverage"], + option_leverage=hedge["option_leverage"], + ratio_perp=hedge["ratio_perp"], + ratio_opt=hedge["ratio_opt"], + ct_mult=hedge["ct_mult"], + ) + move_a = None + move_b = None + if points_data: + move_a = float((points_data.get("case_a") or {}).get("move_points") or 0) or None + mb = (points_data.get("case_b") or {}).get("move_points_portfolio") + move_b = float(mb) if mb is not None else None + + # 使用已 enrich 的 perp_hedge_pnl;若无则当场补算 + work: list[dict[str, Any]] = [] + for r in rows or []: + item = dict(r) + if item.get("perp_hedge_pnl") is None: + item["perp_hedge_pnl"] = perp_hedge_day_pnl( + change=float(item.get("change") or 0), + open_px=float(item.get("open") or 0), + close_px=float(item.get("close") or 0), + premium_total=float(hedge["premium_total"]), + opt_coins=float(hedge["opt_coins"]), + ) + work.append(item) + n = len(work) + empty = { + "enabled": True, + "spot": round(hedge["spot"], 4), + "target_profit_u": round(hedge["target_profit_u"], 4), + "perp_leverage": round(hedge["perp_leverage"], 4), + "option_leverage": round(hedge["option_leverage"], 4), + "ratio_perp": round(hedge["ratio_perp"], 4), + "ratio_opt": round(hedge["ratio_opt"], 4), + "ratio_label": f"{hedge['ratio_perp']:g}:{hedge['ratio_opt']:g}", + "prem_per_coin": round(hedge["prem_per_coin"], 4), + "opt_coins": round(hedge["opt_coins"], 4), + "opt_sheets": round(hedge["opt_sheets"], 4), + "premium_total": round(hedge["premium_total"], 4), + "move_a": None if move_a is None else round(move_a, 4), + "move_b": None if move_b is None else round(move_b, 4), + "points_error": points_err, + "sample_count": n, + "hit_a_days": 0, + "hit_a_ratio": None, + "hit_b_days": 0, + "hit_b_ratio": None, + "pnl_total": None, + "pnl_avg": None, + "win_days": 0, + "win_ratio": None, + "pnl_max": None, + "pnl_min": None, + "up_days": 0, + "down_days": 0, + "up_pnl_total": None, + "down_pnl_total": None, + } + if n <= 0: + return empty + + hit_a = 0 + hit_b = 0 + if move_a is not None and move_a > 0: + hit_a = sum(1 for r in work if float(r.get("change") or 0) >= move_a) + if move_b is not None and move_b > 0: + hit_b = sum(1 for r in work if float(r.get("change") or 0) <= -move_b) + + pnls = [float(r["perp_hedge_pnl"]) for r in work if r.get("perp_hedge_pnl") is not None] + win = sum(1 for p in pnls if p > 0) + up_rows = [r for r in work if float(r.get("change") or 0) >= 0] + down_rows = [r for r in work if float(r.get("change") or 0) < 0] + up_pnls = [float(r["perp_hedge_pnl"]) for r in up_rows if r.get("perp_hedge_pnl") is not None] + down_pnls = [float(r["perp_hedge_pnl"]) for r in down_rows if r.get("perp_hedge_pnl") is not None] + + empty.update( + { + "hit_a_days": hit_a, + "hit_a_ratio": round(hit_a / n, 4) if move_a else None, + "hit_b_days": hit_b, + "hit_b_ratio": round(hit_b / n, 4) if move_b else None, + "pnl_total": round(sum(pnls), 4) if pnls else None, + "pnl_avg": round(statistics.fmean(pnls), 4) if pnls else None, + "win_days": win, + "win_ratio": round(win / n, 4), + "pnl_max": round(max(pnls), 4) if pnls else None, + "pnl_min": round(min(pnls), 4) if pnls else None, + "up_days": len(up_rows), + "down_days": len(down_rows), + "up_pnl_total": round(sum(up_pnls), 4) if up_pnls else None, + "down_pnl_total": round(sum(down_pnls), 4) if down_pnls else None, + } + ) + return empty + + def straddle_long_stats( rows: list[dict[str, Any]], premium: float, @@ -343,7 +535,9 @@ def summarize_rows( *, straddle_premium: Any = None, take_profit: Any = None, + perp_hedge: Any = None, ) -> dict[str, Any]: + hedge = normalize_perp_hedge_params(perp_hedge) if not rows: out = { "sample_count": 0, @@ -358,10 +552,13 @@ def summarize_rows( "up_day_ratio": None, "down_day_ratio": None, "straddle": None, + "perp_hedge": None, } prem = normalize_straddle_premium(straddle_premium) if prem is not None: out["straddle"] = straddle_long_stats([], prem, take_profit=take_profit) + if hedge is not None: + out["perp_hedge"] = perp_hedge_stats([], hedge) return out amps = [float(r["amplitude"]) for r in rows] ups = [float(r["up_points"]) for r in rows] @@ -384,10 +581,13 @@ def summarize_rows( "up_day_ratio": round(up_days / n, 4), "down_day_ratio": round(down_days / n, 4), "straddle": None, + "perp_hedge": None, } prem = normalize_straddle_premium(straddle_premium) if prem is not None: out["straddle"] = straddle_long_stats(rows, prem, take_profit=take_profit) + if hedge is not None: + out["perp_hedge"] = perp_hedge_stats(rows, hedge) return out @@ -594,6 +794,7 @@ def compute_amp_stats( straddle_premium: Any = None, take_profit: Any = None, weekend_filter: Any = "all", + perp_hedge: Any = None, now: Optional[datetime] = None, fetch_fn: Optional[Callable[..., list[dict[str, Any]]]] = None, ) -> dict[str, Any]: @@ -604,6 +805,7 @@ def compute_amp_stats( prem = normalize_straddle_premium(straddle_premium) tp = normalize_take_profit(take_profit) we_mode = normalize_weekend_filter(weekend_filter) + hedge = normalize_perp_hedge_params(perp_hedge) sample_days = resolve_sample_days(period, custom_days) settlements = list_settlement_dates(sample_days=sample_days, now=now) if not settlements: @@ -636,6 +838,7 @@ def compute_amp_stats( straddle_premium=prem, take_profit=tp, weekend_filter=we_mode, + perp_hedge=hedge, price_source=price_source, inst_id=inst_id, missing=missing, @@ -652,6 +855,7 @@ def build_amp_result( straddle_premium: Any = None, take_profit: Any = None, weekend_filter: Any = "all", + perp_hedge: Any = None, price_source: str = "", inst_id: str = "", missing: Optional[list[str]] = None, @@ -659,9 +863,10 @@ def build_amp_result( prem = normalize_straddle_premium(straddle_premium) tp = normalize_take_profit(take_profit) we_mode = normalize_weekend_filter(weekend_filter) + hedge = normalize_perp_hedge_params(perp_hedge) filtered = filter_weekend_rows(rows_all, we_mode) - rows = enrich_rows_pnl(filtered, straddle_premium=prem, take_profit=tp) - summary = summarize_rows(rows, straddle_premium=prem, take_profit=tp) + rows = enrich_rows_pnl(filtered, straddle_premium=prem, take_profit=tp, perp_hedge=hedge) + summary = summarize_rows(rows, straddle_premium=prem, take_profit=tp, perp_hedge=hedge) if period == "custom" or str(period).startswith("custom:"): period_label = period if str(period).startswith("custom:") else f"custom:{sample_days}" else: @@ -679,6 +884,7 @@ def build_amp_result( "straddle_premium": prem, "take_profit": tp, "weekend_filter": we_mode, + "perp_hedge": hedge, "timeframe": TIMEFRAME, "price_source": price_source, "inst_id": inst_id, @@ -701,11 +907,12 @@ def reframe_amp_stats( straddle_premium: Any = None, take_profit: Any = None, weekend_filter: Any = "all", + perp_hedge: Any = None, price_source: str = "", inst_id: str = "", missing: Optional[list[str]] = None, ) -> dict[str, Any]: - """已有日表上改周末/权利金/止盈,不拉 K 线.""" + """已有日表上改周末/权利金/止盈/永期参数,不拉 K 线.""" key = normalize_symbol(symbol) return build_amp_result( rows_all=list(rows_all or []), @@ -716,6 +923,7 @@ def reframe_amp_stats( straddle_premium=straddle_premium, take_profit=take_profit, weekend_filter=weekend_filter, + perp_hedge=perp_hedge, price_source=price_source, inst_id=inst_id, missing=missing, @@ -779,6 +987,70 @@ def build_export_csv(payload: dict[str, Any]) -> str: ] ) w.writerow(["单日最大赚", st.get("pnl_max"), "单日最大亏", st.get("pnl_min")]) + ph = s.get("perp_hedge") or {} + if ph: + w.writerow([]) + w.writerow( + [ + "【永期对冲对照】", + "比例", + ph.get("ratio_label"), + "现价", + ph.get("spot"), + "目标", + ph.get("target_profit_u"), + ] + ) + w.writerow( + [ + "单币权利金", + ph.get("prem_per_coin"), + "权利金总额", + ph.get("premium_total"), + "期权币数", + ph.get("opt_coins"), + ] + ) + w.writerow( + [ + "A所需点数", + ph.get("move_a"), + "A达标天", + ph.get("hit_a_days"), + "占比", + ph.get("hit_a_ratio"), + ] + ) + w.writerow( + [ + "B所需点数(组合)", + ph.get("move_b"), + "B达标天", + ph.get("hit_b_days"), + "占比", + ph.get("hit_b_ratio"), + ] + ) + w.writerow( + [ + "组合盈亏合计", + ph.get("pnl_total"), + "日均", + ph.get("pnl_avg"), + "胜率", + ph.get("win_ratio"), + ] + ) + w.writerow( + [ + "上涨日盈亏", + ph.get("up_pnl_total"), + "下跌日盈亏", + ph.get("down_pnl_total"), + "最大赚/亏", + f"{ph.get('pnl_max')} / {ph.get('pnl_min')}", + ] + ) w.writerow([]) w.writerow(["【日表明细】"]) w.writerow( @@ -799,6 +1071,7 @@ def build_export_csv(payload: dict[str, Any]) -> str: "有效波动", "触达止盈", "收益", + "永期盈亏", ] ) for r in payload.get("rows") or []: @@ -820,6 +1093,7 @@ def build_export_csv(payload: dict[str, Any]) -> str: r.get("effective_move"), "是" if r.get("take_profit_hit") else "否", r.get("profit"), + r.get("perp_hedge_pnl"), ] ) return buf.getvalue() diff --git a/manual_trading_hub/amp_stats_routes.py b/manual_trading_hub/amp_stats_routes.py index c20f4f2..04007ed 100644 --- a/manual_trading_hub/amp_stats_routes.py +++ b/manual_trading_hub/amp_stats_routes.py @@ -20,6 +20,16 @@ from lib.hub.amp_stats_lib import ( ) +class PerpHedgeBody(BaseModel): + spot: Optional[float] = None + target_profit_u: Optional[float] = None + perp_leverage: Optional[float] = None + option_leverage: Optional[float] = None + ratio_perp: float = 1.0 + ratio_opt: float = 2.0 + ct_mult: float = 0.01 + + class ComputeBody(BaseModel): symbol: str = "eth" start_hour: int = 16 @@ -28,6 +38,7 @@ class ComputeBody(BaseModel): straddle_premium: Optional[float] = None take_profit: Optional[float] = None weekend_filter: str = "all" + perp_hedge: Optional[PerpHedgeBody] = None page: int = 1 page_size: int = 20 @@ -37,7 +48,7 @@ class SaveBody(BaseModel): class ReframeBody(BaseModel): - """已有日表上改周末/权利金/止盈(不拉 K 线).""" + """已有日表上改周末/权利金/止盈/永期参数(不拉 K 线).""" rows_all: list[dict[str, Any]] = Field(default_factory=list) symbol: str = "eth" @@ -47,12 +58,19 @@ class ReframeBody(BaseModel): straddle_premium: Optional[float] = None take_profit: Optional[float] = None weekend_filter: str = "all" + perp_hedge: Optional[PerpHedgeBody] = None price_source: str = "" inst_id: str = "" page: int = 1 page_size: int = 20 +def _hedge_dict(body_hedge: Optional[PerpHedgeBody]) -> Optional[dict[str, Any]]: + if body_hedge is None: + return None + return body_hedge.model_dump() + + def create_amp_stats_router() -> APIRouter: router = APIRouter(prefix="/api/amp-stats", tags=["amp-stats"]) @@ -85,6 +103,7 @@ def create_amp_stats_router() -> APIRouter: "timeframe": "1H", "metric_note": "振幅与距离均为点数:振幅=最高-最低=(开→高)+(开→低)", "straddle_note": "买跨:越过权利金用>;止盈≥触达用止盈点否则|涨跌|;收益=有效波动-权利金", + "perp_hedge_note": "永期对冲:永续多1币+买期权;比例默认1:2;达标与组合盈亏见文档", } @router.post("/compute") @@ -98,6 +117,7 @@ def create_amp_stats_router() -> APIRouter: straddle_premium=body.straddle_premium, take_profit=body.take_profit, weekend_filter=body.weekend_filter, + perp_hedge=_hedge_dict(body.perp_hedge), ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @@ -125,6 +145,7 @@ def create_amp_stats_router() -> APIRouter: straddle_premium=body.straddle_premium, take_profit=body.take_profit, weekend_filter=body.weekend_filter, + perp_hedge=_hedge_dict(body.perp_hedge), price_source=body.price_source, inst_id=body.inst_id, ) @@ -168,12 +189,30 @@ def create_amp_stats_router() -> APIRouter: straddle_premium: Optional[float] = Query(default=None), take_profit: Optional[float] = Query(default=None), weekend_filter: str = Query(default="all"), + hedge_spot: Optional[float] = Query(default=None), + hedge_target: Optional[float] = Query(default=None), + hedge_perp_lev: Optional[float] = Query(default=None), + hedge_opt_lev: Optional[float] = Query(default=None), + hedge_ratio_perp: float = Query(default=1.0), + hedge_ratio_opt: float = Query(default=2.0), + hedge_ct_mult: float = Query(default=0.01), ): + hedge_q = { + "spot": hedge_spot, + "target_profit_u": hedge_target, + "perp_leverage": hedge_perp_lev, + "option_leverage": hedge_opt_lev, + "ratio_perp": hedge_ratio_perp, + "ratio_opt": hedge_ratio_opt, + "ct_mult": hedge_ct_mult, + } if (history_id or "").strip(): item = get_history(history_id.strip()) if not item: raise HTTPException(status_code=404, detail="历史不存在") rows_all = item.get("rows_all") or item.get("rows") or [] + item_hedge = item.get("perp_hedge") if isinstance(item.get("perp_hedge"), dict) else None + use_hedge = hedge_q if hedge_spot is not None else item_hedge try: payload = reframe_amp_stats( rows_all=rows_all, @@ -186,6 +225,7 @@ def create_amp_stats_router() -> APIRouter: else item.get("straddle_premium"), take_profit=take_profit if take_profit is not None else item.get("take_profit"), weekend_filter=weekend_filter or item.get("weekend_filter") or "all", + perp_hedge=use_hedge, price_source=str(item.get("price_source") or ""), inst_id=str(item.get("inst_id") or ""), missing=item.get("missing_days") or [], @@ -206,6 +246,7 @@ def create_amp_stats_router() -> APIRouter: straddle_premium=straddle_premium, take_profit=take_profit, weekend_filter=weekend_filter, + perp_hedge=hedge_q, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc diff --git a/manual_trading_hub/static/amp_stats.js b/manual_trading_hub/static/amp_stats.js index e071ba6..4ba6f79 100644 --- a/manual_trading_hub/static/amp_stats.js +++ b/manual_trading_hub/static/amp_stats.js @@ -59,6 +59,31 @@ return el("amp-weekend-filter")?.value || "all"; } + function readNum(id) { + const raw = (el(id)?.value || "").trim(); + if (!raw) return null; + const n = Number(raw); + return Number.isFinite(n) ? n : null; + } + + function readPerpHedge() { + const spot = readNum("amp-hedge-spot"); + const target = readNum("amp-hedge-target"); + const perpLev = readNum("amp-hedge-perp-lev"); + const optLev = readNum("amp-hedge-opt-lev"); + if (spot == null || target == null || perpLev == null || optLev == null) return null; + if (spot <= 0 || target < 0 || perpLev <= 0 || optLev <= 0) return null; + return { + spot, + target_profit_u: target, + perp_leverage: perpLev, + option_leverage: optLev, + ratio_perp: readNum("amp-hedge-ratio-perp") || 1, + ratio_opt: readNum("amp-hedge-ratio-opt") || 2, + ct_mult: 0.01, + }; + } + function setStatus(msg) { const s = el("amp-status"); if (s) s.textContent = msg || ""; @@ -108,6 +133,7 @@ if (!s.sample_count) { box.innerHTML = '
暂无汇总
'; renderStraddle(null); + renderPerpHedge(null); return; } box.innerHTML = @@ -122,6 +148,7 @@ `填写「永期·现价 / 目标 / 杠杆」后计算;对照所需点数达标天数与组合盈亏(永续多1币+买期权)
'; + return; + } + const err = + ph.points_error + ? `口径:开→高=最高−开盘;开→低=开盘−最低;振幅=最高−最低.买跨收益=有效波动−权利金;止盈≥触达则有效波动=止盈点,否则用|涨跌|.周末按结算日标注/筛选.
+口径:开→高=最高−开盘;开→低=开盘−最低;振幅=最高−最低.买跨收益=有效波动−权利金.永期对冲=永续多1币+买期权(默认1:2),对照所需点数达标与组合盈亏.周末按结算日标注/筛选.
| 结算日 | 窗起点 | 开 | 高 | 低 | 收 | -开→高 | 开→低 | 振幅 | 涨跌 | 收益 | +开→高 | 开→低 | 振幅 | 涨跌 | 收益 | 永期盈亏 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 点击「计算」加载 | ||||||||||||||||
| 点击「计算」加载 | ||||||||||||||||