fix卖回VWAP必须落库并覆盖指数占位价,禁止预算反推开仓价。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -495,9 +495,16 @@ def close_option_by_bid1(
|
||||
|
||||
row_budget = _safe_float(row["budget_usdt"])
|
||||
row_open_idx = _safe_float(row["open_index_px"])
|
||||
if (row_open_idx is None or row_open_idx <= 0) and row_budget and paid > 1e-12:
|
||||
# 无开仓指数时用预算/已付反推
|
||||
row_open_idx = float(row_budget) / paid
|
||||
# 禁止 budget/premium 反推开仓价(会把缓冲算进均价,开仓价被抬飞)
|
||||
if (row_open_idx is None or row_open_idx <= 0) and row.get("bridge_id") is not None:
|
||||
try:
|
||||
from lib.options.options_spot_bridge_lib import bridge_buy_vwap
|
||||
|
||||
bv = bridge_buy_vwap(conn, int(row["bridge_id"]))
|
||||
if bv is not None and float(bv) > 0:
|
||||
row_open_idx = float(bv)
|
||||
except Exception:
|
||||
pass
|
||||
settled = settle_closed_pnl(
|
||||
premium_paid=paid,
|
||||
premium_received=recv,
|
||||
|
||||
@@ -511,6 +511,16 @@ def open_coin_option_buy_full(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 开仓价必须在缩减 coin_bought 之前按买币 VWAP 冻住
|
||||
# (buy_usdt_spent/buy_coin_accum;禁止事后 budget/缩水权利金量反推)
|
||||
open_px_freeze = idx_open
|
||||
try:
|
||||
bv = _spot_bridge.bridge_buy_vwap(conn, bridge_id)
|
||||
if bv is not None and float(bv) > 0:
|
||||
open_px_freeze = float(bv)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_spot_bridge.update_bridge(
|
||||
conn,
|
||||
bridge_id,
|
||||
@@ -520,14 +530,6 @@ def open_coin_option_buy_full(
|
||||
message="期权持仓中",
|
||||
)
|
||||
|
||||
# 开仓价一次冻结:优先买币 VWAP(之后不再改);权利金币量 premium_paid 固定
|
||||
open_px_freeze = idx_open
|
||||
try:
|
||||
bv = _spot_bridge.bridge_buy_vwap(conn, bridge_id)
|
||||
if bv is not None and float(bv) > 0:
|
||||
open_px_freeze = float(bv)
|
||||
except Exception:
|
||||
pass
|
||||
trade_id = _insert_coin_trade(
|
||||
conn,
|
||||
inst_id=inst_id,
|
||||
|
||||
@@ -585,52 +585,48 @@ def sync_options_from_local_trades(
|
||||
|
||||
status_label = "已平"
|
||||
pnl_u = None
|
||||
# 价已冻则只用库内价;缺价才问桥 VWAP(避免刷新改均价)
|
||||
prices_frozen = (
|
||||
open_idx is not None
|
||||
and open_idx > 0
|
||||
and sell_idx is not None
|
||||
and sell_idx > 0
|
||||
# 始终问桥:买/卖回 VWAP 优先于库内指数占位价(防预算反推开仓价、平仓指数冒充卖回价)
|
||||
opx, cpx, _gate = resolve_spot_vwap_prices(
|
||||
conn,
|
||||
bridge_id=r["bridge_id"],
|
||||
open_index_px=open_idx,
|
||||
close_index_px=sell_idx,
|
||||
)
|
||||
use_bridge = None if prices_frozen else r["bridge_id"]
|
||||
pnl_u, pnl_src = resolve_true_pnl_usdt_from_trade(
|
||||
premium_paid=prem,
|
||||
premium_received=r["premium_received"],
|
||||
realized_pnl=r["realized_pnl"],
|
||||
open_index_px=open_idx if (open_idx is not None and open_idx > 0) else None,
|
||||
close_index_px=sell_idx,
|
||||
open_index_px=opx,
|
||||
close_index_px=cpx,
|
||||
open_fee=ofee,
|
||||
close_fee=cfee,
|
||||
premium_ccy=ccy,
|
||||
fee_ccy=str(r["premium_ccy"] or ccy or ""),
|
||||
conn=conn if use_bridge is not None else None,
|
||||
bridge_id=use_bridge,
|
||||
conn=conn,
|
||||
bridge_id=r["bridge_id"],
|
||||
)
|
||||
if pnl_src == "pending_sell":
|
||||
status_label = "待卖回"
|
||||
pnl_u = None
|
||||
elif pnl_u is not None and pnl_src not in ("none", "pending_sell"):
|
||||
try:
|
||||
opx, cpx = open_idx, sell_idx
|
||||
if not prices_frozen:
|
||||
opx, cpx, _g = resolve_spot_vwap_prices(
|
||||
conn,
|
||||
bridge_id=r["bridge_id"],
|
||||
open_index_px=open_idx,
|
||||
close_index_px=sell_idx,
|
||||
)
|
||||
# 真盈亏U每次刷新按固定量+价重算;均价只补缺不覆盖
|
||||
# VWAP 非空时覆盖指数占位;币量字段不改
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE options_trades
|
||||
SET realized_pnl_usdt = ?,
|
||||
open_index_px = COALESCE(open_index_px, ?),
|
||||
close_index_px = COALESCE(close_index_px, ?),
|
||||
open_index_px = COALESCE(?, open_index_px),
|
||||
close_index_px = COALESCE(?, close_index_px),
|
||||
pnl_settled = 1
|
||||
WHERE id = ?
|
||||
""",
|
||||
(pnl_u, opx, cpx, trade_id),
|
||||
)
|
||||
if opx is not None and opx > 0:
|
||||
open_idx = opx
|
||||
open_idx_for_prem = opx
|
||||
if cpx is not None and cpx > 0:
|
||||
sell_idx = cpx
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -778,6 +778,13 @@ def sell_residual_after_option_flat(
|
||||
}
|
||||
recovered = _safe_float(sell.get("usdt_recovered"))
|
||||
sold_amt = _safe_float(sell.get("coin_sold"))
|
||||
# 缺 USDT 回报时用成交价×币量补,保证 close_sell VWAP 能落库
|
||||
if (recovered is None or recovered <= 0) and sold_amt is not None and sold_amt > 0:
|
||||
fill_px = _safe_float(sell.get("fill_px"))
|
||||
if fill_px is not None and fill_px > 0:
|
||||
recovered = round(float(sold_amt) * float(fill_px), 8)
|
||||
sell = dict(sell)
|
||||
sell["usdt_recovered"] = recovered
|
||||
# 平仓后卖回(可多笔):累加 close_sell_* 供卖回 VWAP;开仓多余卖回不走这里
|
||||
add_bridge_close_sell_fill(
|
||||
c,
|
||||
@@ -809,6 +816,7 @@ def sell_residual_after_option_flat(
|
||||
"bridge_id": bid,
|
||||
"bridge_status": BRIDGE_PENDING_SELL,
|
||||
}
|
||||
sell_vwap = bridge_sell_vwap(c, bid)
|
||||
update_bridge(
|
||||
c,
|
||||
bid,
|
||||
@@ -818,13 +826,26 @@ def sell_residual_after_option_flat(
|
||||
message="期权已平,币已卖回 USDT",
|
||||
close=True,
|
||||
)
|
||||
# 卖回 VWAP 写入关联已平仓(覆盖平仓时指数占位价)
|
||||
if sell_vwap is not None and float(sell_vwap) > 0:
|
||||
try:
|
||||
c.execute(
|
||||
"""
|
||||
UPDATE options_trades
|
||||
SET close_index_px = ?
|
||||
WHERE bridge_id = ? AND status = 'closed'
|
||||
""",
|
||||
(float(sell_vwap), int(bid)),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"ok": True,
|
||||
"sell": sell,
|
||||
"bridge_id": bid,
|
||||
"bridge_status": BRIDGE_CLOSED,
|
||||
"sell_complete": True,
|
||||
"sell_vwap": bridge_sell_vwap(c, bid),
|
||||
"sell_vwap": sell_vwap,
|
||||
}
|
||||
|
||||
if get_db is not None:
|
||||
|
||||
+13
-2
@@ -421,7 +421,11 @@ def _patch_spot_bridge_lib() -> None:
|
||||
if not is_sellable_spot_coin(ex, coin, amt):
|
||||
min_sz = spot_min_sell_sz(ex, coin)
|
||||
return {"ok": False, "msg": f"{coin} 可卖数量过小(低于现货最小 {min_sz:g})"}
|
||||
sell_sz = max(0.0, float(amt) * 0.999)
|
||||
# 平仓卖回(coin_amount=None)一次卖光;限量卖才留粉尘余量
|
||||
if coin_amount is None:
|
||||
sell_sz = float(amt)
|
||||
else:
|
||||
sell_sz = max(0.0, float(amt) * 0.999)
|
||||
if sell_sz <= 0 or not is_sellable_spot_coin(ex, coin, sell_sz):
|
||||
return {"ok": False, "msg": f"{coin} 可卖数量过小"}
|
||||
pub = _sim_public_exchange(ex)
|
||||
@@ -445,13 +449,20 @@ def _patch_spot_bridge_lib() -> None:
|
||||
notify_instance_balance_changed()
|
||||
except Exception:
|
||||
pass
|
||||
recovered = result.get("usdt_recovered")
|
||||
if recovered is None and result.get("fill_px") is not None:
|
||||
try:
|
||||
recovered = round(float(sell_sz) * float(result.get("fill_px")), 8)
|
||||
except (TypeError, ValueError):
|
||||
recovered = None
|
||||
return {
|
||||
"ok": True,
|
||||
"inst_id": result.get("inst_id") or "",
|
||||
"ord_id": f"sim-spot-sell-{result.get('fill_px') or 0}",
|
||||
"coin_sold": float(sell_sz),
|
||||
"fill_px": result.get("fill_px"),
|
||||
"sim": True,
|
||||
"usdt_recovered": result.get("usdt_recovered"),
|
||||
"usdt_recovered": recovered,
|
||||
"data": {"sCode": "0", "sMsg": "sim filled"},
|
||||
}
|
||||
except Exception as e:
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""从模拟盘流水重建买/卖回 VWAP,并按真盈亏公式重算 realized_pnl_usdt.
|
||||
|
||||
用于 close_sell_* 漏记、开仓价被 budget/premium 抬飞的历史单.
|
||||
默认 dry-run;加 --apply 才写库.
|
||||
|
||||
python scripts/repair_options_spot_vwap_from_sim_ledger.py --db crypto.db
|
||||
python scripts/repair_options_spot_vwap_from_sim_ledger.py --db crypto.db --apply
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _safe_float(v: Any) -> float | None:
|
||||
if v is None or v == "":
|
||||
return None
|
||||
try:
|
||||
return float(v)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _ledger_buy_sell(
|
||||
conn: sqlite3.Connection,
|
||||
*,
|
||||
underly: str,
|
||||
inst_id: str,
|
||||
open_ts: str,
|
||||
close_ts: str | None,
|
||||
) -> tuple[float | None, float | None, float | None, float | None]:
|
||||
"""在开仓前后窗口内汇总 USDT→币 与 平仓后币→USDT."""
|
||||
u = (underly or "").upper()
|
||||
inst = str(inst_id or "").strip()
|
||||
buys = conn.execute(
|
||||
"""
|
||||
SELECT amount, ts FROM sim_ledger_entries
|
||||
WHERE ccy='USDT' AND account='trading' AND kind='convert'
|
||||
AND amount < 0 AND note LIKE ?
|
||||
AND ts >= datetime(?, '-14 hour')
|
||||
AND ts <= datetime(?, '+14 hour')
|
||||
ORDER BY id
|
||||
""",
|
||||
(f"{u}/USDT%", open_ts, open_ts),
|
||||
).fetchall()
|
||||
buy_usdt = 0.0
|
||||
buy_coin = 0.0
|
||||
for row in buys:
|
||||
coin_row = conn.execute(
|
||||
"""
|
||||
SELECT amount FROM sim_ledger_entries
|
||||
WHERE ccy=? AND account='trading' AND kind='convert' AND amount > 0
|
||||
AND ts=? AND note LIKE 'from usdt%'
|
||||
ORDER BY id LIMIT 1
|
||||
""",
|
||||
(u, row["ts"]),
|
||||
).fetchone()
|
||||
if not coin_row:
|
||||
continue
|
||||
buy_usdt += abs(float(row["amount"]))
|
||||
buy_coin += float(coin_row["amount"])
|
||||
sell_usdt = 0.0
|
||||
sell_coin = 0.0
|
||||
last_close = None
|
||||
if inst:
|
||||
last_close = conn.execute(
|
||||
"""
|
||||
SELECT ts FROM sim_ledger_entries
|
||||
WHERE kind='option_close' AND note LIKE ?
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""",
|
||||
(f"%{inst}%",),
|
||||
).fetchone()
|
||||
if last_close or close_ts:
|
||||
close_anchor = str(last_close["ts"]) if last_close else str(close_ts)
|
||||
sells = conn.execute(
|
||||
"""
|
||||
SELECT amount, ts FROM sim_ledger_entries
|
||||
WHERE ccy=? AND account='trading' AND kind='convert'
|
||||
AND amount < 0 AND note LIKE ?
|
||||
AND ts >= ?
|
||||
ORDER BY id
|
||||
""",
|
||||
(u, f"{u}/USDT%", close_anchor),
|
||||
).fetchall()
|
||||
for row in sells:
|
||||
usdt_row = conn.execute(
|
||||
"""
|
||||
SELECT amount FROM sim_ledger_entries
|
||||
WHERE ccy='USDT' AND account='trading' AND kind='convert'
|
||||
AND amount > 0 AND ts=? AND note LIKE 'from %'
|
||||
ORDER BY id LIMIT 1
|
||||
""",
|
||||
(row["ts"],),
|
||||
).fetchone()
|
||||
if not usdt_row:
|
||||
continue
|
||||
sell_coin += abs(float(row["amount"]))
|
||||
sell_usdt += float(usdt_row["amount"])
|
||||
return (
|
||||
buy_usdt if buy_usdt > 0 else None,
|
||||
buy_coin if buy_coin > 0 else None,
|
||||
sell_usdt if sell_usdt > 0 else None,
|
||||
sell_coin if sell_coin > 0 else None,
|
||||
)
|
||||
|
||||
|
||||
def repair(conn: sqlite3.Connection, *, apply: bool) -> list[dict[str, Any]]:
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from lib.options.options_pnl_settle_lib import true_pnl_usdt_premium_move
|
||||
from lib.options.options_spot_bridge_lib import ensure_bridge_table
|
||||
|
||||
ensure_bridge_table(conn)
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT id, inst_id, underlying, premium_paid, premium_received, realized_pnl,
|
||||
open_index_px, close_index_px, realized_pnl_usdt, bridge_id,
|
||||
created_at, closed_at, budget_usdt
|
||||
FROM options_trades
|
||||
WHERE status='closed'
|
||||
ORDER BY id
|
||||
"""
|
||||
).fetchall()
|
||||
report: list[dict[str, Any]] = []
|
||||
for r in rows:
|
||||
tid = int(r["id"])
|
||||
underly = str(r["underlying"] or (str(r["inst_id"] or "").split("-")[0]) or "").upper()
|
||||
open_ts = str(r["created_at"] or "")
|
||||
close_ts = str(r["closed_at"] or "") or None
|
||||
buy_u, buy_c, sell_u, sell_c = _ledger_buy_sell(
|
||||
conn,
|
||||
underly=underly,
|
||||
inst_id=str(r["inst_id"] or ""),
|
||||
open_ts=open_ts,
|
||||
close_ts=close_ts,
|
||||
)
|
||||
open_px = (buy_u / buy_c) if buy_u and buy_c and buy_c > 0 else None
|
||||
close_px = (sell_u / sell_c) if sell_u and sell_c and sell_c > 0 else None
|
||||
paid = _safe_float(r["premium_paid"])
|
||||
coin = _safe_float(r["realized_pnl"])
|
||||
old_open = _safe_float(r["open_index_px"])
|
||||
old_close = _safe_float(r["close_index_px"])
|
||||
old_u = _safe_float(r["realized_pnl_usdt"])
|
||||
new_u = true_pnl_usdt_premium_move(
|
||||
coin_pnl=coin,
|
||||
premium_paid=paid,
|
||||
open_index_px=open_px or old_open,
|
||||
sell_back_px=close_px or old_close,
|
||||
)
|
||||
item = {
|
||||
"id": tid,
|
||||
"inst_id": r["inst_id"],
|
||||
"buy_vwap": round(open_px, 4) if open_px else None,
|
||||
"sell_vwap": round(close_px, 4) if close_px else None,
|
||||
"old_open": old_open,
|
||||
"old_close": old_close,
|
||||
"old_u": old_u,
|
||||
"new_u": new_u,
|
||||
"bridge_id": r["bridge_id"],
|
||||
"sell_usdt": sell_u,
|
||||
"sell_coin": sell_c,
|
||||
"buy_usdt": buy_u,
|
||||
"buy_coin": buy_c,
|
||||
}
|
||||
report.append(item)
|
||||
if not apply:
|
||||
continue
|
||||
bid = r["bridge_id"]
|
||||
if bid is not None and buy_u and buy_c:
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE options_spot_bridge
|
||||
SET buy_usdt_spent = ?, buy_coin_accum = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(float(buy_u), float(buy_c), int(bid)),
|
||||
)
|
||||
if bid is not None and sell_u and sell_c:
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE options_spot_bridge
|
||||
SET close_sell_usdt = ?, close_sell_coin = ?,
|
||||
usdt_recovered = ?, coin_sold = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(float(sell_u), float(sell_c), float(sell_u), float(sell_c), int(bid)),
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE options_trades
|
||||
SET open_index_px = COALESCE(?, open_index_px),
|
||||
close_index_px = COALESCE(?, close_index_px),
|
||||
realized_pnl_usdt = COALESCE(?, realized_pnl_usdt),
|
||||
initial_premium_usdt = CASE
|
||||
WHEN ? IS NOT NULL AND premium_paid IS NOT NULL
|
||||
THEN round(premium_paid * ?, 4)
|
||||
ELSE initial_premium_usdt
|
||||
END,
|
||||
pnl_settled = 1
|
||||
WHERE id = ?
|
||||
""",
|
||||
(
|
||||
open_px,
|
||||
close_px,
|
||||
new_u,
|
||||
open_px,
|
||||
open_px,
|
||||
tid,
|
||||
),
|
||||
)
|
||||
if apply:
|
||||
conn.commit()
|
||||
# 复盘行跟着刷
|
||||
try:
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
|
||||
sync_options_from_local_trades(conn, None)
|
||||
conn.commit()
|
||||
except Exception as e:
|
||||
print("review_sync_warn", e)
|
||||
return report
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--db", required=True)
|
||||
ap.add_argument("--apply", action="store_true")
|
||||
args = ap.parse_args()
|
||||
path = Path(args.db)
|
||||
if not path.is_file():
|
||||
print("db missing", path)
|
||||
return 1
|
||||
conn = sqlite3.connect(str(path))
|
||||
conn.row_factory = sqlite3.Row
|
||||
report = repair(conn, apply=bool(args.apply))
|
||||
conn.close()
|
||||
import json
|
||||
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
print("apply" if args.apply else "dry-run", "rows", len(report))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -7,7 +7,7 @@ import unittest
|
||||
|
||||
class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
def test_refresh_recomputes_u_keeps_frozen_qty_px(self):
|
||||
"""刷新按公式重算U;权利金币/币盈亏/开仓卖回价不变."""
|
||||
"""刷新按公式重算U;权利金币/币盈亏不变."""
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
from lib.sim.mode_lib import MODE_SIM, TRADING_MODE_KEY
|
||||
@@ -55,6 +55,60 @@ class ReviewLocalSyncFreezeTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(float(row["realized_pnl_total"]), 700.0, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_refresh_prefers_bridge_sell_vwap_over_wrong_index(self):
|
||||
"""库内卖回价若是指数占位,刷新须用桥上卖回 VWAP 纠正."""
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
from lib.options.options_spot_bridge_lib import (
|
||||
BRIDGE_CLOSED,
|
||||
add_bridge_buy_fill,
|
||||
add_bridge_close_sell_fill,
|
||||
ensure_bridge_table,
|
||||
insert_bridge,
|
||||
update_bridge,
|
||||
)
|
||||
from lib.sim.mode_lib import MODE_SIM, TRADING_MODE_KEY
|
||||
from lib.instance.runtime_settings_lib import ensure_runtime_settings_table, runtime_set
|
||||
|
||||
conn = sqlite3.connect(":memory:")
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_options_tables(conn)
|
||||
ensure_bridge_table(conn)
|
||||
ensure_runtime_settings_table(conn)
|
||||
runtime_set(conn, TRADING_MODE_KEY, MODE_SIM)
|
||||
bid = insert_bridge(conn, underlying="BTC", status="holding", budget_usdt=990.0)
|
||||
add_bridge_buy_fill(conn, bid, usdt_spent=840.10, coin_bought=0.01)
|
||||
add_bridge_close_sell_fill(conn, bid, usdt_recovered=814.18, coin_sold=0.01)
|
||||
update_bridge(conn, bid, status=BRIDGE_CLOSED, close=True)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_trades(
|
||||
inst_id, underlying, opt_type, sheets, eth_amount,
|
||||
premium_paid, premium_received, realized_pnl, premium_ccy,
|
||||
open_fee, close_fee, status, realized_pnl_usdt,
|
||||
open_index_px, close_index_px, pnl_settled, bridge_id,
|
||||
created_at, closed_at
|
||||
) VALUES (
|
||||
'BTC-USD-261015-84000-P', 'BTC', 'P', 1, 0.01,
|
||||
0.01, 0.02, 0.01, 'BTC',
|
||||
0, 0, 'closed', 1544.0,
|
||||
88086.0, 85400.0, 1, ?,
|
||||
'2026-10-12 01:46:50', '2026-10-13 08:07:35'
|
||||
)
|
||||
""",
|
||||
(bid,),
|
||||
)
|
||||
conn.commit()
|
||||
sync_options_from_local_trades(conn, None)
|
||||
trade = conn.execute(
|
||||
"SELECT realized_pnl_usdt, open_index_px, close_index_px FROM options_trades WHERE id=1"
|
||||
).fetchone()
|
||||
self.assertAlmostEqual(float(trade["open_index_px"]), 84010.0, places=2)
|
||||
self.assertAlmostEqual(float(trade["close_index_px"]), 81418.0, places=2)
|
||||
# A = 0.01*81418 - 0.01*(84010-81418) = 814.18 - 25.92 = 788.26
|
||||
self.assertAlmostEqual(float(trade["realized_pnl_usdt"]), 788.26, places=2)
|
||||
conn.close()
|
||||
|
||||
def test_premium_usdt_move_formula(self):
|
||||
from lib.options.options_db import init_options_tables
|
||||
from lib.options.options_review_lib import sync_options_from_local_trades
|
||||
|
||||
Reference in New Issue
Block a user