fix模拟盘盈亏:收回×卖回价 − 开仓U权利金 − 手续费,历史按U展示。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2833,12 +2833,15 @@
|
|||||||
list.forEach(function (h) {
|
list.forEach(function (h) {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
const premCcy = posPremiumCcy(h);
|
const premCcy = posPremiumCcy(h);
|
||||||
const unit = premCcy === "USDC" ? "U" : premCcy;
|
const premUnit = premCcy === "USDC" || premCcy === "USDT" ? "U" : premCcy;
|
||||||
|
// 模拟盘盈亏已折 U 时用 pnl_display_ccy,勿再标 BTC
|
||||||
|
const pnlCcy = String(h.pnl_display_ccy || premCcy).toUpperCase();
|
||||||
|
const pnlUnit = pnlCcy === "USDC" || pnlCcy === "USDT" ? "U" : pnlCcy;
|
||||||
const premCore = fmtDisplay(
|
const premCore = fmtDisplay(
|
||||||
h.premium_paid_fmt,
|
h.premium_paid_fmt,
|
||||||
h.premium_paid != null ? fmtPremiumAmt(h.premium_paid, premCcy) : null
|
h.premium_paid != null ? fmtPremiumAmt(h.premium_paid, premCcy) : null
|
||||||
);
|
);
|
||||||
const premTxt = premCore === "—" ? "—" : premCore + " " + unit;
|
const premTxt = premCore === "—" ? "—" : premCore + " " + premUnit;
|
||||||
const isOpen = h.status === "open";
|
const isOpen = h.status === "open";
|
||||||
const pnl = isOpen ? null : h.realized_pnl;
|
const pnl = isOpen ? null : h.realized_pnl;
|
||||||
let pnlTxt = "—";
|
let pnlTxt = "—";
|
||||||
@@ -2846,9 +2849,9 @@
|
|||||||
const n = Number(pnl);
|
const n = Number(pnl);
|
||||||
const absCore = fmtDisplay(
|
const absCore = fmtDisplay(
|
||||||
null,
|
null,
|
||||||
fmtPremiumAmt(Math.abs(n), premCcy)
|
pnlUnit === "U" ? fmt(Math.abs(n), 2) : fmtPremiumAmt(Math.abs(n), pnlCcy)
|
||||||
);
|
);
|
||||||
pnlTxt = (n > 0 ? "+" : n < 0 ? "-" : "") + absCore + " " + unit;
|
pnlTxt = (n > 0 ? "+" : n < 0 ? "-" : "") + absCore + " " + pnlUnit;
|
||||||
}
|
}
|
||||||
const pnlCls = pnl > 0 ? "pos-pnl-profit" : pnl < 0 ? "pos-pnl-loss" : "";
|
const pnlCls = pnl > 0 ? "pos-pnl-profit" : pnl < 0 ? "pos-pnl-loss" : "";
|
||||||
const timeTxt = (h.closed_at || h.created_at || "—").replace("T", " ").slice(0, 19);
|
const timeTxt = (h.closed_at || h.created_at || "—").replace("T", " ").slice(0, 19);
|
||||||
|
|||||||
@@ -1467,6 +1467,7 @@ def format_option_history_row(
|
|||||||
realized = _safe_float(raw.get("realizedPnl"))
|
realized = _safe_float(raw.get("realizedPnl"))
|
||||||
if realized is None:
|
if realized is None:
|
||||||
realized = _safe_float(raw.get("pnl"))
|
realized = _safe_float(raw.get("pnl"))
|
||||||
|
pnl_usdt = _safe_float(raw.get("_sim_realized_pnl_usdt"))
|
||||||
pnl_ratio = _safe_float(raw.get("pnlRatio"))
|
pnl_ratio = _safe_float(raw.get("pnlRatio"))
|
||||||
close_type = str(raw.get("type") or "").strip()
|
close_type = str(raw.get("type") or "").strip()
|
||||||
utime = _safe_float(raw.get("uTime"))
|
utime = _safe_float(raw.get("uTime"))
|
||||||
@@ -1477,6 +1478,13 @@ def format_option_history_row(
|
|||||||
premium_ccy = str(raw.get("_sim_premium_ccy") or "").strip().upper() or premium_ccy_for_mode(
|
premium_ccy = str(raw.get("_sim_premium_ccy") or "").strip().upper() or premium_ccy_for_mode(
|
||||||
row_mode, uly or "ETH"
|
row_mode, uly or "ETH"
|
||||||
)
|
)
|
||||||
|
# 模拟盘已冻结 U 盈亏时,盈亏列按 U 展示(权利金仍为币)
|
||||||
|
pnl_display_ccy = str(raw.get("_sim_pnl_display_ccy") or "").strip().upper()
|
||||||
|
if pnl_usdt is not None:
|
||||||
|
realized = float(pnl_usdt)
|
||||||
|
pnl_display_ccy = "USDT"
|
||||||
|
elif not pnl_display_ccy:
|
||||||
|
pnl_display_ccy = premium_ccy
|
||||||
if close_type in ("3", "4"):
|
if close_type in ("3", "4"):
|
||||||
status_label = "强平"
|
status_label = "强平"
|
||||||
else:
|
else:
|
||||||
@@ -1505,9 +1513,13 @@ def format_option_history_row(
|
|||||||
"premium_paid": premium_paid,
|
"premium_paid": premium_paid,
|
||||||
"premium_paid_fmt": format_premium_amount(premium_paid, ccy=premium_ccy),
|
"premium_paid_fmt": format_premium_amount(premium_paid, ccy=premium_ccy),
|
||||||
"premium_ccy": premium_ccy,
|
"premium_ccy": premium_ccy,
|
||||||
|
"pnl_display_ccy": pnl_display_ccy,
|
||||||
"margin_mode": row_mode,
|
"margin_mode": row_mode,
|
||||||
"realized_pnl": realized,
|
"realized_pnl": realized,
|
||||||
"realized_pnl_fmt": format_premium_amount(realized, ccy=premium_ccy),
|
"realized_pnl_usdt": pnl_usdt if pnl_usdt is not None else (
|
||||||
|
float(realized) if pnl_display_ccy == "USDT" and realized is not None else None
|
||||||
|
),
|
||||||
|
"realized_pnl_fmt": format_premium_amount(realized, ccy=pnl_display_ccy),
|
||||||
"pnl_ratio_pct": round(pnl_ratio * 100, 2) if pnl_ratio is not None else None,
|
"pnl_ratio_pct": round(pnl_ratio * 100, 2) if pnl_ratio is not None else None,
|
||||||
"status": "closed",
|
"status": "closed",
|
||||||
"status_label": status_label,
|
"status_label": status_label,
|
||||||
|
|||||||
@@ -174,6 +174,62 @@ def fee_to_usdt(
|
|||||||
return round(amt, 4)
|
return round(amt, 4)
|
||||||
|
|
||||||
|
|
||||||
|
def settle_sim_coin_pnl_usdt(
|
||||||
|
*,
|
||||||
|
premium_paid: Any,
|
||||||
|
premium_received: Any,
|
||||||
|
open_fee: Any = 0,
|
||||||
|
close_fee: Any = 0,
|
||||||
|
open_index_px: float | None,
|
||||||
|
sell_back_px: float | None,
|
||||||
|
fee_ccy: str | None = None,
|
||||||
|
premium_ccy: str = "ETH",
|
||||||
|
) -> dict[str, float | None]:
|
||||||
|
"""模拟盘币本位盈亏(U).
|
||||||
|
|
||||||
|
盈利: (权利金btc+盈利btc)×卖回价 − 初始U权利金 − 手续费
|
||||||
|
= 收回币×卖回价 − 已付币×开仓指数 − 手续费U
|
||||||
|
亏损: 剩余权利金U − 手续费U − 初始U权利金(同一式,结果为负)
|
||||||
|
|
||||||
|
不用买币预算当成本(预算含缓冲).
|
||||||
|
"""
|
||||||
|
del fee_ccy # 手续费按权利金币种折 U
|
||||||
|
paid = float(_safe_float(premium_paid) or 0.0)
|
||||||
|
recv = float(_safe_float(premium_received) or 0.0)
|
||||||
|
ofee = abs(float(_safe_float(open_fee) or 0.0))
|
||||||
|
cfee = abs(float(_safe_float(close_fee) or 0.0))
|
||||||
|
open_idx = _safe_float(open_index_px)
|
||||||
|
sell_px = _safe_float(sell_back_px)
|
||||||
|
net_coin = round(recv - paid - ofee - cfee, 8)
|
||||||
|
if open_idx is None or open_idx <= 0 or sell_px is None or sell_px <= 0:
|
||||||
|
return {
|
||||||
|
"realized_pnl": net_coin,
|
||||||
|
"close_index_px": float(sell_px) if sell_px is not None and sell_px > 0 else None,
|
||||||
|
"open_index_px": float(open_idx) if open_idx is not None and open_idx > 0 else None,
|
||||||
|
"realized_pnl_usdt": None,
|
||||||
|
"total_fee": round(ofee + cfee, 8),
|
||||||
|
"initial_premium_usdt": None,
|
||||||
|
"proceeds_usdt": None,
|
||||||
|
}
|
||||||
|
# 权利金btc+盈利btc = 平仓收回币
|
||||||
|
proceeds_u = float(recv) * float(sell_px)
|
||||||
|
initial_u = float(paid) * float(open_idx)
|
||||||
|
# 开仓费按开仓指数、平仓费按卖回价折 U
|
||||||
|
fee_u = ofee * float(open_idx) + cfee * float(sell_px)
|
||||||
|
pnl_u = round(proceeds_u - initial_u - fee_u, 4)
|
||||||
|
return {
|
||||||
|
"realized_pnl": net_coin,
|
||||||
|
"close_index_px": float(sell_px),
|
||||||
|
"open_index_px": float(open_idx),
|
||||||
|
"realized_pnl_usdt": pnl_u,
|
||||||
|
"total_fee": round(ofee + cfee, 8),
|
||||||
|
"initial_premium_usdt": round(initial_u, 4),
|
||||||
|
"proceeds_usdt": round(proceeds_u, 4),
|
||||||
|
"fee_usdt": round(fee_u, 4),
|
||||||
|
"premium_ccy": str(premium_ccy or "").strip().upper() or "ETH",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def settle_closed_pnl(
|
def settle_closed_pnl(
|
||||||
*,
|
*,
|
||||||
premium_paid: Any,
|
premium_paid: Any,
|
||||||
@@ -189,13 +245,16 @@ def settle_closed_pnl(
|
|||||||
"""
|
"""
|
||||||
由「毛权利金」结算.
|
由「毛权利金」结算.
|
||||||
币数量净盈亏 = 收回 − 已付 − 开/平手续费(只扣一次).
|
币数量净盈亏 = 收回 − 已付 − 开/平手续费(只扣一次).
|
||||||
冻结 U(币本位) = 净盈亏 × 平仓指数.
|
|
||||||
|
|
||||||
注意:不要用 budget_usdt 当开仓成本——它含现货买入缓冲,多余币会卖回,
|
币本位 U:
|
||||||
拿全额预算当成本会把亏损算大. open_index/budget 参数保留兼容,不参与 U 计算.
|
收回×卖回价(index_px) − 已付×开仓指数 − 手续费U
|
||||||
|
(开仓指数缺失时回退为净币×平仓指数)
|
||||||
|
USDC: 净额原样为 U.
|
||||||
|
|
||||||
|
注意:不要用 budget_usdt 当开仓成本——它含现货买入缓冲.
|
||||||
不要对 OKX realizedPnl 再调此函数扣费.
|
不要对 OKX realizedPnl 再调此函数扣费.
|
||||||
"""
|
"""
|
||||||
del open_index_px, budget_usdt, fee_ccy # 兼容旧调用,不参与结算
|
del budget_usdt # 买币预算不是权利金成本
|
||||||
paid = float(_safe_float(premium_paid) or 0.0)
|
paid = float(_safe_float(premium_paid) or 0.0)
|
||||||
recv = float(_safe_float(premium_received) or 0.0)
|
recv = float(_safe_float(premium_received) or 0.0)
|
||||||
ofee = float(_safe_float(open_fee) or 0.0)
|
ofee = float(_safe_float(open_fee) or 0.0)
|
||||||
@@ -205,6 +264,29 @@ def settle_closed_pnl(
|
|||||||
if cfee < 0:
|
if cfee < 0:
|
||||||
cfee = abs(cfee)
|
cfee = abs(cfee)
|
||||||
net = round(recv - paid - ofee - cfee, 8)
|
net = round(recv - paid - ofee - cfee, 8)
|
||||||
|
pc = str(premium_ccy or "").strip().upper()
|
||||||
|
if pc in ("ETH", "BTC"):
|
||||||
|
open_idx = _safe_float(open_index_px)
|
||||||
|
sell_px = _safe_float(index_px)
|
||||||
|
if open_idx is not None and open_idx > 0 and sell_px is not None and sell_px > 0:
|
||||||
|
out = settle_sim_coin_pnl_usdt(
|
||||||
|
premium_paid=paid,
|
||||||
|
premium_received=recv,
|
||||||
|
open_fee=ofee,
|
||||||
|
close_fee=cfee,
|
||||||
|
open_index_px=open_idx,
|
||||||
|
sell_back_px=sell_px,
|
||||||
|
fee_ccy=fee_ccy,
|
||||||
|
premium_ccy=pc,
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
# 无开仓指数:回退净币×平仓价
|
||||||
|
return freeze_pnl_to_usdt(
|
||||||
|
net,
|
||||||
|
premium_ccy=pc,
|
||||||
|
index_px=index_px,
|
||||||
|
total_fee=ofee + cfee,
|
||||||
|
)
|
||||||
out = freeze_pnl_to_usdt(
|
out = freeze_pnl_to_usdt(
|
||||||
net,
|
net,
|
||||||
premium_ccy=premium_ccy,
|
premium_ccy=premium_ccy,
|
||||||
|
|||||||
@@ -512,11 +512,10 @@ def _sim_net_usdt_converts_for_trade(
|
|||||||
|
|
||||||
|
|
||||||
def sync_sim_cash_realized_pnl_usdt(conn, ex: Any = None) -> int:
|
def sync_sim_cash_realized_pnl_usdt(conn, ex: Any = None) -> int:
|
||||||
"""模拟盘:按权利金净盈亏冻结 U(收回−已付−费)×平仓指数.
|
"""模拟盘:收回×卖回价 − 初始U权利金 − 手续费U.
|
||||||
|
|
||||||
禁止再用「卖回 USDT − 买币预算」:买币预算含权利金本金与缓冲,卖回差额会把
|
盈利=(权利金币+盈利币)×卖回价−开仓U权利金−费;
|
||||||
权利金本金也算进盈利,导致合计盈亏虚高(约翻倍).
|
亏损=剩余权利金U−费−开仓U(同一式).不用买币预算差额.
|
||||||
函数名保留兼容旧调用.
|
|
||||||
"""
|
"""
|
||||||
init_options_tables(conn)
|
init_options_tables(conn)
|
||||||
from lib.options.options_pnl_settle_lib import resolve_close_index_px, settle_closed_pnl
|
from lib.options.options_pnl_settle_lib import resolve_close_index_px, settle_closed_pnl
|
||||||
@@ -551,45 +550,40 @@ def sync_sim_cash_realized_pnl_usdt(conn, ex: Any = None) -> int:
|
|||||||
recv = float(total_premium(cq, eth))
|
recv = float(total_premium(cq, eth))
|
||||||
except Exception:
|
except Exception:
|
||||||
recv = None
|
recv = None
|
||||||
if paid is None or recv is None:
|
sell_px = _safe_float(_row_get(row, "close_index_px"))
|
||||||
# 无毛权利金时回退:已有币数量净盈亏 × 冻结指数
|
open_idx = _safe_float(_row_get(row, "open_index_px"))
|
||||||
net = _safe_float(_row_get(row, "realized_pnl"))
|
if sell_px is None or sell_px <= 0:
|
||||||
if net is None:
|
sell_px = resolve_close_index_px(
|
||||||
continue
|
ex,
|
||||||
idx = _safe_float(_row_get(row, "close_index_px")) or _safe_float(
|
inst_id=str(_row_get(row, "inst_id") or ""),
|
||||||
_row_get(row, "open_index_px")
|
underlying=underly,
|
||||||
|
hint=open_idx,
|
||||||
)
|
)
|
||||||
if idx is None or idx <= 0:
|
if open_idx is None or open_idx <= 0:
|
||||||
idx = resolve_close_index_px(
|
# 无开仓指数时用卖回价兜底(等价旧净币×价)
|
||||||
ex,
|
open_idx = sell_px
|
||||||
inst_id=str(_row_get(row, "inst_id") or ""),
|
if paid is None or recv is None:
|
||||||
underlying=underly,
|
net = _safe_float(_row_get(row, "realized_pnl"))
|
||||||
)
|
if net is None or sell_px is None or sell_px <= 0:
|
||||||
if idx is None or idx <= 0:
|
|
||||||
continue
|
continue
|
||||||
usdt = round(float(net) * float(idx), 4)
|
usdt = round(float(net) * float(sell_px), 4)
|
||||||
close_idx = float(idx)
|
close_idx = float(sell_px)
|
||||||
new_pnl = float(net)
|
new_pnl = float(net)
|
||||||
|
open_write = open_idx
|
||||||
else:
|
else:
|
||||||
idx = _safe_float(_row_get(row, "close_index_px"))
|
|
||||||
if idx is None or idx <= 0:
|
|
||||||
idx = resolve_close_index_px(
|
|
||||||
ex,
|
|
||||||
inst_id=str(_row_get(row, "inst_id") or ""),
|
|
||||||
underlying=underly,
|
|
||||||
hint=_row_get(row, "open_index_px"),
|
|
||||||
)
|
|
||||||
settled = settle_closed_pnl(
|
settled = settle_closed_pnl(
|
||||||
premium_paid=paid,
|
premium_paid=paid,
|
||||||
premium_received=recv,
|
premium_received=recv,
|
||||||
open_fee=_row_get(row, "open_fee"),
|
open_fee=_row_get(row, "open_fee"),
|
||||||
close_fee=_row_get(row, "close_fee"),
|
close_fee=_row_get(row, "close_fee"),
|
||||||
premium_ccy=ccy or underly or "ETH",
|
premium_ccy=ccy or underly or "ETH",
|
||||||
index_px=idx,
|
index_px=sell_px,
|
||||||
|
open_index_px=open_idx,
|
||||||
)
|
)
|
||||||
usdt = settled.get("realized_pnl_usdt")
|
usdt = settled.get("realized_pnl_usdt")
|
||||||
close_idx = settled.get("close_index_px")
|
close_idx = settled.get("close_index_px") or sell_px
|
||||||
new_pnl = settled.get("realized_pnl")
|
new_pnl = settled.get("realized_pnl")
|
||||||
|
open_write = settled.get("open_index_px") or open_idx
|
||||||
if usdt is None:
|
if usdt is None:
|
||||||
continue
|
continue
|
||||||
old = _safe_float(_row_get(row, "realized_pnl_usdt"))
|
old = _safe_float(_row_get(row, "realized_pnl_usdt"))
|
||||||
@@ -600,10 +594,11 @@ def sync_sim_cash_realized_pnl_usdt(conn, ex: Any = None) -> int:
|
|||||||
UPDATE options_trades
|
UPDATE options_trades
|
||||||
SET realized_pnl_usdt = ?,
|
SET realized_pnl_usdt = ?,
|
||||||
close_index_px = COALESCE(close_index_px, ?),
|
close_index_px = COALESCE(close_index_px, ?),
|
||||||
|
open_index_px = COALESCE(open_index_px, ?),
|
||||||
realized_pnl = COALESCE(?, realized_pnl)
|
realized_pnl = COALESCE(?, realized_pnl)
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""",
|
""",
|
||||||
(float(usdt), close_idx, new_pnl, int(row["id"])),
|
(float(usdt), close_idx, open_write, new_pnl, int(row["id"])),
|
||||||
)
|
)
|
||||||
updated += 1
|
updated += 1
|
||||||
return updated
|
return updated
|
||||||
|
|||||||
+17
-5
@@ -1220,10 +1220,17 @@ class SimBroker:
|
|||||||
conn = self.get_db()
|
conn = self.get_db()
|
||||||
try:
|
try:
|
||||||
init_options_tables(conn)
|
init_options_tables(conn)
|
||||||
|
try:
|
||||||
|
from lib.options.options_stats_lib import sync_sim_cash_realized_pnl_usdt
|
||||||
|
|
||||||
|
sync_sim_cash_realized_pnl_usdt(conn, exchange)
|
||||||
|
conn.commit()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
sql = """
|
sql = """
|
||||||
SELECT id, inst_id, underlying, sheets, open_quote, close_quote,
|
SELECT id, inst_id, underlying, sheets, open_quote, close_quote,
|
||||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
premium_paid, premium_received, realized_pnl, realized_pnl_usdt,
|
||||||
created_at, closed_at
|
premium_ccy, created_at, closed_at
|
||||||
FROM options_trades
|
FROM options_trades
|
||||||
WHERE status = 'closed'
|
WHERE status = 'closed'
|
||||||
"""
|
"""
|
||||||
@@ -1250,6 +1257,7 @@ class SimBroker:
|
|||||||
paid = float(r["premium_paid"] or 0) if r["premium_paid"] is not None else None
|
paid = float(r["premium_paid"] or 0) if r["premium_paid"] is not None else None
|
||||||
recv = float(r["premium_received"] or 0) if r["premium_received"] is not None else None
|
recv = float(r["premium_received"] or 0) if r["premium_received"] is not None else None
|
||||||
pnl = float(r["realized_pnl"]) if r["realized_pnl"] is not None else None
|
pnl = float(r["realized_pnl"]) if r["realized_pnl"] is not None else None
|
||||||
|
pnl_usdt = float(r["realized_pnl_usdt"]) if r["realized_pnl_usdt"] is not None else None
|
||||||
if pnl is None and paid is not None and recv is not None:
|
if pnl is None and paid is not None and recv is not None:
|
||||||
pnl = round(recv - paid, 8)
|
pnl = round(recv - paid, 8)
|
||||||
if pnl is None:
|
if pnl is None:
|
||||||
@@ -1283,16 +1291,20 @@ class SimBroker:
|
|||||||
"closeAvgPx": str(close_px) if close_px is not None else "",
|
"closeAvgPx": str(close_px) if close_px is not None else "",
|
||||||
"closeTotalPos": str(sheets),
|
"closeTotalPos": str(sheets),
|
||||||
"openMaxPos": str(sheets),
|
"openMaxPos": str(sheets),
|
||||||
"realizedPnl": str(round(pnl, 8)),
|
# 展示用 U 盈亏;币数量仍放 raw 供对账
|
||||||
"pnl": str(round(pnl, 8)),
|
"realizedPnl": str(round(pnl_usdt, 4)) if pnl_usdt is not None else str(round(pnl, 8)),
|
||||||
|
"pnl": str(round(pnl_usdt, 4)) if pnl_usdt is not None else str(round(pnl, 8)),
|
||||||
"pnlRatio": str(round(pnl_ratio, 8)) if pnl_ratio is not None else "",
|
"pnlRatio": str(round(pnl_ratio, 8)) if pnl_ratio is not None else "",
|
||||||
"cTime": str(open_ms or ""),
|
"cTime": str(open_ms or ""),
|
||||||
"uTime": str(close_ms or ""),
|
"uTime": str(close_ms or ""),
|
||||||
"type": "2",
|
"type": "2",
|
||||||
"optType": opt_type or "",
|
"optType": opt_type or "",
|
||||||
"stk": str(strike) if strike is not None else "",
|
"stk": str(strike) if strike is not None else "",
|
||||||
"_sim_premium_ccy": ccy,
|
|
||||||
"_sim_premium_paid": paid,
|
"_sim_premium_paid": paid,
|
||||||
|
"_sim_premium_ccy": ccy,
|
||||||
|
"_sim_realized_pnl_coin": pnl,
|
||||||
|
"_sim_realized_pnl_usdt": pnl_usdt,
|
||||||
|
"_sim_pnl_display_ccy": "USDT" if pnl_usdt is not None else ccy,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class FeeConvertTests(unittest.TestCase):
|
|||||||
|
|
||||||
class SettleClosedPnlTests(unittest.TestCase):
|
class SettleClosedPnlTests(unittest.TestCase):
|
||||||
def test_sim_net_times_close_index(self):
|
def test_sim_net_times_close_index(self):
|
||||||
|
# 无开仓指数时回退净币×平仓价
|
||||||
s = settle_closed_pnl(
|
s = settle_closed_pnl(
|
||||||
premium_paid=0.01,
|
premium_paid=0.01,
|
||||||
premium_received=0.02,
|
premium_received=0.02,
|
||||||
@@ -37,6 +38,35 @@ class SettleClosedPnlTests(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(float(s["realized_pnl"]), 0.009, places=8)
|
self.assertAlmostEqual(float(s["realized_pnl"]), 0.009, places=8)
|
||||||
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), 27.0, places=4)
|
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), 27.0, places=4)
|
||||||
|
|
||||||
|
def test_sim_proceeds_minus_open_premium_usdt(self):
|
||||||
|
"""盈利=(收回币)×卖回价 − 已付币×开仓指数 − 手续费U."""
|
||||||
|
s = settle_closed_pnl(
|
||||||
|
premium_paid=0.011005,
|
||||||
|
premium_received=0.02270864,
|
||||||
|
open_fee=0,
|
||||||
|
close_fee=0,
|
||||||
|
premium_ccy="BTC",
|
||||||
|
index_px=81000, # 卖回价
|
||||||
|
open_index_px=86000, # 初始 U 权利金用开仓指数
|
||||||
|
)
|
||||||
|
# 0.02270864*81000 - 0.011005*86000
|
||||||
|
expect = 0.02270864 * 81000 - 0.011005 * 86000
|
||||||
|
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), expect, places=2)
|
||||||
|
|
||||||
|
def test_sim_loss_remaining_minus_fees(self):
|
||||||
|
"""亏损:剩余权利金U − 手续费U − 初始U(同一式)."""
|
||||||
|
s = settle_closed_pnl(
|
||||||
|
premium_paid=0.01,
|
||||||
|
premium_received=0.004,
|
||||||
|
open_fee=0.0001,
|
||||||
|
close_fee=0.0001,
|
||||||
|
premium_ccy="ETH",
|
||||||
|
index_px=2500,
|
||||||
|
open_index_px=3000,
|
||||||
|
)
|
||||||
|
# 0.004*2500 - 0.01*3000 - (0.0001*3000+0.0001*2500) = 10 - 30 - 0.55 = -20.55
|
||||||
|
self.assertAlmostEqual(float(s["realized_pnl_usdt"]), -20.55, places=2)
|
||||||
|
|
||||||
def test_budget_ignored_for_usdt(self):
|
def test_budget_ignored_for_usdt(self):
|
||||||
# budget 不得抬高开仓成本
|
# budget 不得抬高开仓成本
|
||||||
s = settle_closed_pnl(
|
s = settle_closed_pnl(
|
||||||
|
|||||||
Reference in New Issue
Block a user