Fix amp-stats perp PnL to exit at daily profit target.
Hit A/B via open-to-high/low; day PnL equals target when touched, otherwise settle at close. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+13
-9
@@ -111,23 +111,26 @@ K 线粒度:**1H**(与整点对齐);价源优先 OKX 指数(ETH-USD /
|
||||
|
||||
### ① 所需点数达标
|
||||
|
||||
复用计算器「由比例推点数」:
|
||||
复用计算器「由比例推点数」(按**当日开盘**推 A/B;汇总展示用样本开盘中位):
|
||||
|
||||
| 指标 | 规则 |
|
||||
|------|------|
|
||||
| A 所需点数 | 永续方向对、净利=目标 |
|
||||
| A 达标 | 日 `涨跌 ≥ A点数` 的天数与占比 |
|
||||
| A 达标 | 日 `开→高 ≥ A点数` 的天数与占比 |
|
||||
| B 所需点数 | 期权方向对、**组合净利**=目标 |
|
||||
| B 达标 | 日 `涨跌 ≤ −B点数` 的天数与占比 |
|
||||
| B 达标 | 日 `开→低 ≥ B点数` 的天数与占比 |
|
||||
|
||||
### ② 按日组合盈亏
|
||||
### ② 按日组合盈亏(目标出场)
|
||||
|
||||
| 日向 | 组合净利 |
|
||||
|------|----------|
|
||||
| 上涨 `涨跌≥0` | `涨跌 − 当日权利金 − 永续开平手续费(开→收)` |
|
||||
| 下跌 `涨跌<0` | `\|涨跌\|×(期权币数−1) − 当日权利金` |
|
||||
日盈利目标(如 **15U**)用于出场:
|
||||
|
||||
汇总:合计、日均、胜率、上涨日/下跌日盈亏小计、单日最大赚亏;日表 **永期盈亏** 列。
|
||||
| 情形 | 日盈亏 |
|
||||
|------|--------|
|
||||
| 开→高触达 A,或 开→低触达 B | **= 目标盈利**(出场) |
|
||||
| 两边都触达 | 仍按目标盈利(OHLC 未知先后) |
|
||||
| 均未触达 | 收盘结算:上涨 `涨跌−权利金−手续费`;下跌 `\|涨跌\|×(期权币数−1)−权利金` |
|
||||
|
||||
汇总:目标出场天数 / 收盘结算天数、合计、日均、胜率、上涨日/下跌日盈亏小计、单日最大赚亏;日表 **永期盈亏** 列。
|
||||
改永期参数 / 周末筛选会**本地重算**(不重拉 K 线)。
|
||||
|
||||
---
|
||||
@@ -162,3 +165,4 @@ K 线粒度:**1H**(与整点对齐);价源优先 OKX 指数(ETH-USD /
|
||||
| 2026-07-23 | 长周期续拉 history K 线;收益列红绿着色 |
|
||||
| 2026-07-28 | 永期对冲对照:所需点数达标 + 按日组合盈亏 |
|
||||
| 2026-07-28 | 永期入场改按日开盘;买跨/永期对照模式二选一 |
|
||||
| 2026-07-28 | 永期日盈亏按目标盈利出场(开→高/低触达),未触达才收盘结算 |
|
||||
|
||||
+169
-46
@@ -275,15 +275,16 @@ def enrich_rows_pnl(
|
||||
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),
|
||||
option_leverage=float(hedge["option_leverage"]),
|
||||
opt_coins=float(hedge["opt_coins"]),
|
||||
)
|
||||
day = perp_hedge_day_result(item, hedge)
|
||||
item["perp_hedge_pnl"] = day["pnl"]
|
||||
item["perp_hedge_exit"] = day["exit"]
|
||||
item["perp_hedge_hit_a"] = day["hit_a"]
|
||||
item["perp_hedge_hit_b"] = day["hit_b"]
|
||||
else:
|
||||
item["perp_hedge_pnl"] = None
|
||||
item["perp_hedge_exit"] = None
|
||||
item["perp_hedge_hit_a"] = False
|
||||
item["perp_hedge_hit_b"] = False
|
||||
out.append(item)
|
||||
return out
|
||||
|
||||
@@ -336,7 +337,37 @@ def perp_hedge_day_premium(*, open_px: float, option_leverage: float, opt_coins:
|
||||
return coins * (o / lev)
|
||||
|
||||
|
||||
def perp_hedge_day_pnl(
|
||||
def perp_hedge_required_moves(
|
||||
*,
|
||||
open_px: float,
|
||||
hedge: dict[str, float],
|
||||
) -> tuple[Optional[float], Optional[float], Optional[str]]:
|
||||
"""按当日开盘推 A/B 达目标盈利所需点数."""
|
||||
from lib.hub.hub_perp_options_calc_lib import calc_perp_options_points
|
||||
|
||||
spot = float(open_px or 0)
|
||||
if spot <= 0:
|
||||
return None, None, "开盘价无效"
|
||||
points_data, points_err = calc_perp_options_points(
|
||||
base="ETH",
|
||||
spot=spot,
|
||||
capital_usdt=max(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"],
|
||||
)
|
||||
if not points_data:
|
||||
return None, None, points_err
|
||||
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
|
||||
return move_a, move_b, points_err
|
||||
|
||||
|
||||
def perp_hedge_day_pnl_eod(
|
||||
*,
|
||||
change: float,
|
||||
open_px: float,
|
||||
@@ -344,7 +375,7 @@ def perp_hedge_day_pnl(
|
||||
option_leverage: float,
|
||||
opt_coins: float,
|
||||
) -> float:
|
||||
"""单日组合净利(永续多1币 + 买期权);入场/权利金按当日开盘.
|
||||
"""未触达目标时按收盘结算的组合净利.
|
||||
|
||||
上涨: change − 权利金 − 永续开平手续费
|
||||
下跌: |change|×(opt_coins−1) − 权利金
|
||||
@@ -361,20 +392,114 @@ def perp_hedge_day_pnl(
|
||||
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_day_result(row: dict[str, Any], hedge: dict[str, float]) -> dict[str, Any]:
|
||||
"""单日永期结果:触达目标点数则按目标盈利出场,否则收盘结算.
|
||||
|
||||
A: 开→高 ≥ move_a → 出场净利 = 目标盈利
|
||||
B: 开→低 ≥ move_b → 出场净利 = 目标盈利
|
||||
两边都触达时仍按目标盈利(路径未知,任一边出场均约为目标).
|
||||
"""
|
||||
open_px = float(row.get("open") or 0)
|
||||
close_px = float(row.get("close") or 0)
|
||||
change = float(row.get("change") or 0)
|
||||
up_pts = float(row.get("up_points") or 0)
|
||||
down_pts = float(row.get("down_points") or 0)
|
||||
target = float(hedge["target_profit_u"])
|
||||
move_a, move_b, _err = perp_hedge_required_moves(open_px=open_px, hedge=hedge)
|
||||
hit_a = bool(move_a is not None and move_a > 0 and up_pts >= move_a)
|
||||
hit_b = bool(move_b is not None and move_b > 0 and down_pts >= move_b)
|
||||
if hit_a or hit_b:
|
||||
if hit_a and hit_b:
|
||||
exit_tag = "target_both"
|
||||
elif hit_a:
|
||||
exit_tag = "target_a"
|
||||
else:
|
||||
exit_tag = "target_b"
|
||||
return {
|
||||
"pnl": round(target, 4),
|
||||
"exit": exit_tag,
|
||||
"hit_a": hit_a,
|
||||
"hit_b": hit_b,
|
||||
"move_a": move_a,
|
||||
"move_b": move_b,
|
||||
}
|
||||
return {
|
||||
"pnl": perp_hedge_day_pnl_eod(
|
||||
change=change,
|
||||
open_px=open_px,
|
||||
close_px=close_px,
|
||||
option_leverage=float(hedge["option_leverage"]),
|
||||
opt_coins=float(hedge["opt_coins"]),
|
||||
),
|
||||
"exit": "eod",
|
||||
"hit_a": False,
|
||||
"hit_b": False,
|
||||
"move_a": move_a,
|
||||
"move_b": move_b,
|
||||
}
|
||||
|
||||
|
||||
# 兼容旧名:默认按「目标出场」完整日结果取 pnl
|
||||
def perp_hedge_day_pnl(
|
||||
*,
|
||||
change: float,
|
||||
open_px: float,
|
||||
close_px: float,
|
||||
option_leverage: float,
|
||||
opt_coins: float,
|
||||
up_points: Optional[float] = None,
|
||||
down_points: Optional[float] = None,
|
||||
target_profit_u: Optional[float] = None,
|
||||
perp_leverage: float = 10.0,
|
||||
ratio_perp: float = 1.0,
|
||||
ratio_opt: float = 2.0,
|
||||
ct_mult: float = 0.01,
|
||||
) -> float:
|
||||
"""单日盈亏.若给了目标与开→高/低,触达则按目标出场;否则收盘结算."""
|
||||
if target_profit_u is None or up_points is None or down_points is None:
|
||||
return perp_hedge_day_pnl_eod(
|
||||
change=change,
|
||||
open_px=open_px,
|
||||
close_px=close_px,
|
||||
option_leverage=option_leverage,
|
||||
opt_coins=opt_coins,
|
||||
)
|
||||
hedge = {
|
||||
"target_profit_u": float(target_profit_u),
|
||||
"perp_leverage": float(perp_leverage),
|
||||
"option_leverage": float(option_leverage),
|
||||
"ratio_perp": float(ratio_perp),
|
||||
"ratio_opt": float(ratio_opt),
|
||||
"ct_mult": float(ct_mult),
|
||||
"opt_coins": float(opt_coins),
|
||||
"opt_sheets": float(opt_coins) / float(ct_mult),
|
||||
}
|
||||
return float(
|
||||
perp_hedge_day_result(
|
||||
{
|
||||
"open": open_px,
|
||||
"close": close_px,
|
||||
"change": change,
|
||||
"up_points": up_points,
|
||||
"down_points": down_points,
|
||||
},
|
||||
hedge,
|
||||
)["pnl"]
|
||||
)
|
||||
|
||||
|
||||
def perp_hedge_stats(
|
||||
rows: list[dict[str, Any]],
|
||||
hedge: dict[str, float],
|
||||
) -> dict[str, Any]:
|
||||
"""永期对冲:所需点数达标 + 按日组合盈亏汇总.
|
||||
|
||||
日盈亏权利金按当日开盘;推所需点数用样本开盘中位数作入场参照.
|
||||
达标看开→高/开→低是否触达当日入场推得的 A/B 点数;
|
||||
触达则日盈亏=目标盈利,否则收盘结算.汇总展示点数用样本开盘中位.
|
||||
"""
|
||||
from lib.hub.hub_perp_options_calc_lib import calc_perp_options_points
|
||||
|
||||
opens = [float(r.get("open") or 0) for r in (rows or []) if float(r.get("open") or 0) > 0]
|
||||
spot_ref = statistics.median(opens) if opens else None
|
||||
prem_ref = (
|
||||
@@ -392,40 +517,24 @@ def perp_hedge_stats(
|
||||
move_b = None
|
||||
points_err = None
|
||||
if spot_ref is not None and spot_ref > 0:
|
||||
points_data, points_err = calc_perp_options_points(
|
||||
base="ETH",
|
||||
spot=spot_ref,
|
||||
capital_usdt=max(spot_ref / 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"],
|
||||
)
|
||||
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
|
||||
move_a, move_b, points_err = perp_hedge_required_moves(open_px=spot_ref, hedge=hedge)
|
||||
else:
|
||||
points_err = "样本无有效开盘价,无法推所需点数"
|
||||
|
||||
# 按日开盘重算盈亏(不沿用固定权利金)
|
||||
work: list[dict[str, Any]] = []
|
||||
for r in rows or []:
|
||||
item = dict(r)
|
||||
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),
|
||||
option_leverage=float(hedge["option_leverage"]),
|
||||
opt_coins=float(hedge["opt_coins"]),
|
||||
)
|
||||
day = perp_hedge_day_result(item, hedge)
|
||||
item["perp_hedge_pnl"] = day["pnl"]
|
||||
item["perp_hedge_exit"] = day["exit"]
|
||||
item["perp_hedge_hit_a"] = day["hit_a"]
|
||||
item["perp_hedge_hit_b"] = day["hit_b"]
|
||||
work.append(item)
|
||||
n = len(work)
|
||||
empty = {
|
||||
"enabled": True,
|
||||
"entry": "open",
|
||||
"exit": "target_or_eod",
|
||||
"spot": None if spot_ref is None else round(spot_ref, 4),
|
||||
"target_profit_u": round(hedge["target_profit_u"], 4),
|
||||
"perp_leverage": round(hedge["perp_leverage"], 4),
|
||||
@@ -445,6 +554,9 @@ def perp_hedge_stats(
|
||||
"hit_a_ratio": None,
|
||||
"hit_b_days": 0,
|
||||
"hit_b_ratio": None,
|
||||
"target_exit_days": 0,
|
||||
"target_exit_ratio": None,
|
||||
"eod_days": 0,
|
||||
"pnl_total": None,
|
||||
"pnl_avg": None,
|
||||
"win_days": 0,
|
||||
@@ -459,12 +571,10 @@ def perp_hedge_stats(
|
||||
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)
|
||||
hit_a = sum(1 for r in work if r.get("perp_hedge_hit_a"))
|
||||
hit_b = sum(1 for r in work if r.get("perp_hedge_hit_b"))
|
||||
target_exits = sum(1 for r in work if str(r.get("perp_hedge_exit") or "").startswith("target"))
|
||||
eod_days = sum(1 for r in work if r.get("perp_hedge_exit") == "eod")
|
||||
|
||||
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)
|
||||
@@ -476,9 +586,12 @@ def perp_hedge_stats(
|
||||
empty.update(
|
||||
{
|
||||
"hit_a_days": hit_a,
|
||||
"hit_a_ratio": round(hit_a / n, 4) if move_a else None,
|
||||
"hit_a_ratio": round(hit_a / n, 4),
|
||||
"hit_b_days": hit_b,
|
||||
"hit_b_ratio": round(hit_b / n, 4) if move_b else None,
|
||||
"hit_b_ratio": round(hit_b / n, 4),
|
||||
"target_exit_days": target_exits,
|
||||
"target_exit_ratio": round(target_exits / n, 4),
|
||||
"eod_days": eod_days,
|
||||
"pnl_total": round(sum(pnls), 4) if pnls else None,
|
||||
"pnl_avg": round(statistics.fmean(pnls), 4) if pnls else None,
|
||||
"win_days": win,
|
||||
@@ -1043,7 +1156,7 @@ def build_export_csv(payload: dict[str, Any]) -> str:
|
||||
[
|
||||
"A所需点数",
|
||||
ph.get("move_a"),
|
||||
"A达标天",
|
||||
"A达标天(开→高)",
|
||||
ph.get("hit_a_days"),
|
||||
"占比",
|
||||
ph.get("hit_a_ratio"),
|
||||
@@ -1053,12 +1166,22 @@ def build_export_csv(payload: dict[str, Any]) -> str:
|
||||
[
|
||||
"B所需点数(组合)",
|
||||
ph.get("move_b"),
|
||||
"B达标天",
|
||||
"B达标天(开→低)",
|
||||
ph.get("hit_b_days"),
|
||||
"占比",
|
||||
ph.get("hit_b_ratio"),
|
||||
]
|
||||
)
|
||||
w.writerow(
|
||||
[
|
||||
"目标出场天",
|
||||
ph.get("target_exit_days"),
|
||||
"收盘结算天",
|
||||
ph.get("eod_days"),
|
||||
"目标盈利",
|
||||
ph.get("target_profit_u"),
|
||||
]
|
||||
)
|
||||
w.writerow(
|
||||
[
|
||||
"组合盈亏合计",
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
if (!box) return;
|
||||
if (!ph) {
|
||||
box.innerHTML =
|
||||
'<p class="amp-empty">填写「目标 / 杠杆」后计算;入场按日开盘;对照所需点数达标与组合盈亏(永续多1币+买期权)</p>';
|
||||
'<p class="amp-empty">填写「目标 / 杠杆」后计算;入场按日开盘;触达目标点数按目标盈利出场,否则收盘结算</p>';
|
||||
return;
|
||||
}
|
||||
const err =
|
||||
@@ -219,13 +219,14 @@
|
||||
: "";
|
||||
box.innerHTML =
|
||||
`<div class="amp-sum-grid">` +
|
||||
`<div><span class="amp-sum-k">入场</span><span class="amp-sum-v">按日开盘 · 推点数中位 ${esc(ph.spot)}</span></div>` +
|
||||
`<div><span class="amp-sum-k">入场 / 出场</span><span class="amp-sum-v">开盘 · 目标 ${esc(ph.target_profit_u)}U 或收盘</span></div>` +
|
||||
`<div><span class="amp-sum-k">比例 / 期权仓</span><span class="amp-sum-v">${esc(ph.ratio_label)} · ${esc(ph.opt_coins)} 币</span></div>` +
|
||||
`<div><span class="amp-sum-k">单币/总权利金(中位)</span><span class="amp-sum-v">${esc(ph.prem_per_coin)} / ${esc(ph.premium_total)}</span></div>` +
|
||||
`<div><span class="amp-sum-k">A所需点数(永续对)</span><span class="amp-sum-v">${esc(ph.move_a)}</span></div>` +
|
||||
`<div><span class="amp-sum-k">A达标</span><span class="amp-sum-v">${esc(ph.hit_a_days)} 天 · ${esc(pct(ph.hit_a_ratio))}</span></div>` +
|
||||
`<div><span class="amp-sum-k">A达标(开→高)</span><span class="amp-sum-v">${esc(ph.hit_a_days)} 天 · ${esc(pct(ph.hit_a_ratio))}</span></div>` +
|
||||
`<div><span class="amp-sum-k">B所需点数(组合)</span><span class="amp-sum-v">${esc(ph.move_b)}</span></div>` +
|
||||
`<div><span class="amp-sum-k">B达标</span><span class="amp-sum-v">${esc(ph.hit_b_days)} 天 · ${esc(pct(ph.hit_b_ratio))}</span></div>` +
|
||||
`<div><span class="amp-sum-k">B达标(开→低)</span><span class="amp-sum-v">${esc(ph.hit_b_days)} 天 · ${esc(pct(ph.hit_b_ratio))}</span></div>` +
|
||||
`<div><span class="amp-sum-k">目标出场 / 收盘结算</span><span class="amp-sum-v">${esc(ph.target_exit_days)} / ${esc(ph.eod_days)} 天</span></div>` +
|
||||
`<div><span class="amp-sum-k">组合盈亏合计</span><span class="amp-sum-v ${pnlClass(ph.pnl_total)}">${esc(ph.pnl_total)}</span></div>` +
|
||||
`<div><span class="amp-sum-k">日均 / 胜率</span><span class="amp-sum-v ${pnlClass(ph.pnl_avg)}">${esc(ph.pnl_avg)} · ${esc(pct(ph.win_ratio))}</span></div>` +
|
||||
`<div><span class="amp-sum-k">上涨日盈亏</span><span class="amp-sum-v ${pnlClass(ph.up_pnl_total)}">${esc(ph.up_pnl_total)} <small>(${esc(ph.up_days)}天)</small></span></div>` +
|
||||
|
||||
@@ -1306,7 +1306,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p id="amp-status" class="toolbar-meta amp-status"></p>
|
||||
<p class="amp-hint">口径:开→高=最高−开盘;开→低=开盘−最低;振幅=最高−最低.对照模式二选一:买跨收益=有效波动−权利金;永期对冲=永续多1币+买期权(默认1:2),入场/权利金按日开盘.周末按结算日标注/筛选.</p>
|
||||
<p class="amp-hint">口径:开→高=最高−开盘;开→低=开盘−最低;振幅=最高−最低.对照模式二选一:买跨收益=有效波动−权利金;永期对冲=永续多1币+买期权(默认1:2),入场按日开盘,触达目标点数按目标盈利出场否则收盘结算.周末按结算日标注/筛选.</p>
|
||||
<h3 class="amp-block-title">汇总</h3>
|
||||
<div id="amp-summary" class="amp-summary"></div>
|
||||
<div id="amp-overlay-straddle-block">
|
||||
|
||||
+34
-16
@@ -93,10 +93,10 @@ class AmpStatsLibTests(unittest.TestCase):
|
||||
self.assertIsNone(s["perp_hedge"])
|
||||
|
||||
def test_perp_hedge_hit_and_pnl(self):
|
||||
from lib.hub.amp_stats_lib import perp_hedge_day_pnl
|
||||
from lib.hub.amp_stats_lib import perp_hedge_day_pnl, perp_hedge_day_pnl_eod
|
||||
|
||||
# 开盘=1800 optLev=100 → prem/coin=18; 1:2 → premium=36
|
||||
# A move ≈ 52.83; B portfolio move = 51
|
||||
# 开盘=1800 optLev=100 → prem=36; A≈52.83; B=51
|
||||
# 触达目标出场 → 日盈亏=15, 不再按收盘涨跌算满仓
|
||||
rows = [
|
||||
{"open": 1800, "close": 1860, "change": 60, "up_points": 60, "down_points": 0, "amplitude": 60, "settlement_day": "2026-07-01"},
|
||||
{"open": 1800, "close": 1740, "change": -60, "up_points": 0, "down_points": 60, "amplitude": 60, "settlement_day": "2026-07-02"},
|
||||
@@ -114,30 +114,48 @@ class AmpStatsLibTests(unittest.TestCase):
|
||||
ph = s["perp_hedge"]
|
||||
self.assertIsNotNone(ph)
|
||||
self.assertEqual(ph["entry"], "open")
|
||||
self.assertEqual(ph["exit"], "target_or_eod")
|
||||
self.assertEqual(ph["spot"], 1800.0)
|
||||
self.assertEqual(ph["opt_coins"], 2.0)
|
||||
self.assertEqual(ph["premium_total"], 36.0)
|
||||
self.assertAlmostEqual(ph["move_b"], 51.0, places=4)
|
||||
self.assertEqual(ph["hit_a_days"], 1) # only +60
|
||||
self.assertEqual(ph["hit_b_days"], 1) # only -60
|
||||
self.assertEqual(ph["hit_a_days"], 1) # up 60 >= A
|
||||
self.assertEqual(ph["hit_b_days"], 1) # down 60 >= B
|
||||
self.assertEqual(ph["target_exit_days"], 2)
|
||||
self.assertEqual(ph["eod_days"], 1)
|
||||
# 触达目标 → 15U
|
||||
self.assertAlmostEqual(ph["pnl_max"], 15.0, places=4)
|
||||
up_pnl = perp_hedge_day_pnl(
|
||||
change=60, open_px=1800, close_px=1860, option_leverage=100, opt_coins=2
|
||||
change=60,
|
||||
open_px=1800,
|
||||
close_px=1860,
|
||||
option_leverage=100,
|
||||
opt_coins=2,
|
||||
up_points=60,
|
||||
down_points=0,
|
||||
target_profit_u=15,
|
||||
)
|
||||
down_pnl = perp_hedge_day_pnl(
|
||||
change=-60, open_px=1800, close_px=1740, option_leverage=100, opt_coins=2
|
||||
change=-60,
|
||||
open_px=1800,
|
||||
close_px=1740,
|
||||
option_leverage=100,
|
||||
opt_coins=2,
|
||||
up_points=0,
|
||||
down_points=60,
|
||||
target_profit_u=15,
|
||||
)
|
||||
self.assertAlmostEqual(down_pnl, 60 * (2 - 1) - 36, places=4) # 24
|
||||
self.assertAlmostEqual(ph["down_pnl_total"], down_pnl, places=4)
|
||||
self.assertGreater(up_pnl, 0)
|
||||
self.assertAlmostEqual(up_pnl, 15.0, places=4)
|
||||
self.assertAlmostEqual(down_pnl, 15.0, places=4)
|
||||
self.assertAlmostEqual(ph["down_pnl_total"], 15.0, places=4)
|
||||
# 未触达:收盘结算
|
||||
eod = perp_hedge_day_pnl_eod(
|
||||
change=20, open_px=1800, close_px=1820, option_leverage=100, opt_coins=2
|
||||
)
|
||||
self.assertLess(eod, 0)
|
||||
self.assertEqual(ph["up_days"], 2)
|
||||
self.assertEqual(ph["down_days"], 1)
|
||||
|
||||
# 不同开盘 → 不同权利金
|
||||
hi_open_pnl = perp_hedge_day_pnl(
|
||||
change=-60, open_px=2000, close_px=1940, option_leverage=100, opt_coins=2
|
||||
)
|
||||
self.assertAlmostEqual(hi_open_pnl, 60 - 40, places=4) # prem=40
|
||||
|
||||
csv_text = build_export_csv(
|
||||
{
|
||||
"exchange": "okx",
|
||||
|
||||
Reference in New Issue
Block a user