fix卖回未完成禁止用指数提前结算,并纠正买卖VWAP回退。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -226,7 +226,6 @@ def resolve_spot_vwap_prices(
|
|||||||
try:
|
try:
|
||||||
from lib.options.options_spot_bridge_lib import (
|
from lib.options.options_spot_bridge_lib import (
|
||||||
BRIDGE_CLOSED,
|
BRIDGE_CLOSED,
|
||||||
BRIDGE_PENDING_SELL,
|
|
||||||
bridge_buy_vwap,
|
bridge_buy_vwap,
|
||||||
bridge_sell_vwap,
|
bridge_sell_vwap,
|
||||||
ensure_bridge_table,
|
ensure_bridge_table,
|
||||||
@@ -243,14 +242,13 @@ def resolve_spot_vwap_prices(
|
|||||||
st = str(
|
st = str(
|
||||||
row["status"] if row and hasattr(row, "keys") else (row[0] if row else "") or ""
|
row["status"] if row and hasattr(row, "keys") else (row[0] if row else "") or ""
|
||||||
)
|
)
|
||||||
if st == BRIDGE_PENDING_SELL:
|
# 桥未关闭(=卖回流程未结束):禁止用平仓指数冒充卖回 VWAP 提前结算
|
||||||
|
if st and st != BRIDGE_CLOSED:
|
||||||
return open_px, None, "pending_sell"
|
return open_px, None, "pending_sell"
|
||||||
sv = bridge_sell_vwap(conn, bid)
|
sv = bridge_sell_vwap(conn, bid)
|
||||||
if sv is not None and sv > 0:
|
if sv is not None and sv > 0:
|
||||||
close_px = sv
|
close_px = sv
|
||||||
elif st == BRIDGE_CLOSED and (close_px is None or close_px <= 0):
|
# 已关桥但无卖回 VWAP:允许回退 close_index_px;两者都无则 missing
|
||||||
# 已关桥但无 VWAP/冻结卖回价
|
|
||||||
return open_px, None, "missing"
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if open_px is None or open_px <= 0 or close_px is None or close_px <= 0:
|
if open_px is None or open_px <= 0 or close_px is None or close_px <= 0:
|
||||||
|
|||||||
@@ -599,6 +599,18 @@ def sync_options_from_local_trades(
|
|||||||
if pnl_src == "pending_sell":
|
if pnl_src == "pending_sell":
|
||||||
status_label = "待卖回"
|
status_label = "待卖回"
|
||||||
pnl_u = None
|
pnl_u = None
|
||||||
|
# 清掉曾用指数提前冻上的假真盈亏
|
||||||
|
try:
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
UPDATE options_trades
|
||||||
|
SET realized_pnl_usdt = NULL, pnl_settled = 0
|
||||||
|
WHERE id = ? AND COALESCE(pnl_settled, 0) = 1
|
||||||
|
""",
|
||||||
|
(trade_id,),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
elif pnl_u is not None and pnl_src not in ("none", "pending_sell"):
|
elif pnl_u is not None and pnl_src not in ("none", "pending_sell"):
|
||||||
if frozen_usdt is None or abs(float(frozen_usdt) - float(pnl_u)) > 0.05:
|
if frozen_usdt is None or abs(float(frozen_usdt) - float(pnl_u)) > 0.05:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -184,12 +184,16 @@ def add_bridge_close_sell_fill(
|
|||||||
|
|
||||||
|
|
||||||
def bridge_buy_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float | None:
|
def bridge_buy_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float | None:
|
||||||
"""开仓买币 VWAP(USDT/币);多笔加权.忽略后续把 coin_bought 改成权利金量."""
|
"""开仓买币 VWAP(USDT/币);多笔加权.
|
||||||
|
|
||||||
|
只用 buy_usdt_spent/buy_coin_accum.禁止 budget/coin_bought:
|
||||||
|
开仓成功后 coin_bought 会被改成权利金量,用 budget/缩水量会把开仓价抬飞.
|
||||||
|
"""
|
||||||
if bridge_id is None:
|
if bridge_id is None:
|
||||||
return None
|
return None
|
||||||
ensure_bridge_table(conn)
|
ensure_bridge_table(conn)
|
||||||
row = conn.execute(
|
row = conn.execute(
|
||||||
"SELECT buy_usdt_spent, buy_coin_accum, budget_usdt, coin_bought FROM options_spot_bridge WHERE id=?",
|
"SELECT buy_usdt_spent, buy_coin_accum FROM options_spot_bridge WHERE id=?",
|
||||||
(int(bridge_id),),
|
(int(bridge_id),),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
@@ -198,21 +202,19 @@ def bridge_buy_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float |
|
|||||||
coins = _safe_float(row["buy_coin_accum"] if hasattr(row, "keys") else row[1])
|
coins = _safe_float(row["buy_coin_accum"] if hasattr(row, "keys") else row[1])
|
||||||
if spent is not None and coins is not None and spent > 0 and coins > 0:
|
if spent is not None and coins is not None and spent > 0 and coins > 0:
|
||||||
return round(float(spent) / float(coins), 8)
|
return round(float(spent) / float(coins), 8)
|
||||||
# 旧桥无累加字段:用预算/买入量近似单笔
|
|
||||||
budget = _safe_float(row["budget_usdt"] if hasattr(row, "keys") else row[2])
|
|
||||||
bought = _safe_float(row["coin_bought"] if hasattr(row, "keys") else row[3])
|
|
||||||
if budget is not None and bought is not None and budget > 0 and bought > 0:
|
|
||||||
return round(float(budget) / float(bought), 8)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def bridge_sell_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float | None:
|
def bridge_sell_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float | None:
|
||||||
"""平仓后卖回 VWAP(USDT/币);仅 close_sell_* 多笔加权,不含开仓多余卖回."""
|
"""平仓后卖回 VWAP(USDT/币);仅 close_sell_* 多笔加权.
|
||||||
|
|
||||||
|
禁止回退 usdt_recovered/coin_sold:后者常混有开仓多余卖回,会污染卖回均价.
|
||||||
|
"""
|
||||||
if bridge_id is None:
|
if bridge_id is None:
|
||||||
return None
|
return None
|
||||||
ensure_bridge_table(conn)
|
ensure_bridge_table(conn)
|
||||||
row = conn.execute(
|
row = conn.execute(
|
||||||
"SELECT close_sell_usdt, close_sell_coin, usdt_recovered, coin_sold, status FROM options_spot_bridge WHERE id=?",
|
"SELECT close_sell_usdt, close_sell_coin FROM options_spot_bridge WHERE id=?",
|
||||||
(int(bridge_id),),
|
(int(bridge_id),),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
@@ -221,13 +223,6 @@ def bridge_sell_vwap(conn: sqlite3.Connection, bridge_id: int | None) -> float |
|
|||||||
c = _safe_float(row["close_sell_coin"] if hasattr(row, "keys") else row[1])
|
c = _safe_float(row["close_sell_coin"] if hasattr(row, "keys") else row[1])
|
||||||
if u is not None and c is not None and u > 0 and c > 0:
|
if u is not None and c is not None and u > 0 and c > 0:
|
||||||
return round(float(u) / float(c), 8)
|
return round(float(u) / float(c), 8)
|
||||||
# 旧桥无分拆字段且已关桥:用总收回/总卖出近似(可能含开仓多余卖回)
|
|
||||||
st = str(row["status"] if hasattr(row, "keys") else row[4] or "")
|
|
||||||
if st == BRIDGE_CLOSED:
|
|
||||||
ru = _safe_float(row["usdt_recovered"] if hasattr(row, "keys") else row[2])
|
|
||||||
sc = _safe_float(row["coin_sold"] if hasattr(row, "keys") else row[3])
|
|
||||||
if ru is not None and sc is not None and ru > 0 and sc > 0:
|
|
||||||
return round(float(ru) / float(sc), 8)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class SpotVwapBridgeTests(unittest.TestCase):
|
|||||||
def test_pending_sell_blocks_true_pnl(self):
|
def test_pending_sell_blocks_true_pnl(self):
|
||||||
from lib.options.options_pnl_settle_lib import resolve_true_pnl_usdt_from_trade
|
from lib.options.options_pnl_settle_lib import resolve_true_pnl_usdt_from_trade
|
||||||
from lib.options.options_spot_bridge_lib import (
|
from lib.options.options_spot_bridge_lib import (
|
||||||
|
BRIDGE_HOLDING,
|
||||||
BRIDGE_PENDING_SELL,
|
BRIDGE_PENDING_SELL,
|
||||||
add_bridge_buy_fill,
|
add_bridge_buy_fill,
|
||||||
add_bridge_close_sell_fill,
|
add_bridge_close_sell_fill,
|
||||||
@@ -103,6 +104,39 @@ class SpotVwapBridgeTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(src, "pending_sell")
|
self.assertEqual(src, "pending_sell")
|
||||||
self.assertIsNone(pnl)
|
self.assertIsNone(pnl)
|
||||||
|
# holding 未关桥同样不得用指数提前结算
|
||||||
|
update_bridge(conn, bid, status=BRIDGE_HOLDING)
|
||||||
|
pnl2, src2 = resolve_true_pnl_usdt_from_trade(
|
||||||
|
premium_paid=0.01,
|
||||||
|
premium_received=0.02,
|
||||||
|
realized_pnl=0.01,
|
||||||
|
open_index_px=3000.0,
|
||||||
|
close_index_px=3000.0,
|
||||||
|
premium_ccy="ETH",
|
||||||
|
conn=conn,
|
||||||
|
bridge_id=bid,
|
||||||
|
)
|
||||||
|
self.assertEqual(src2, "pending_sell")
|
||||||
|
self.assertIsNone(pnl2)
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def test_buy_vwap_ignores_shrunk_coin_bought(self):
|
||||||
|
"""开仓后 coin_bought 被改成权利金量时,不得用 budget/缩水量当买价."""
|
||||||
|
from lib.options.options_spot_bridge_lib import (
|
||||||
|
add_bridge_buy_fill,
|
||||||
|
bridge_buy_vwap,
|
||||||
|
ensure_bridge_table,
|
||||||
|
insert_bridge,
|
||||||
|
update_bridge,
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(":memory:")
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
ensure_bridge_table(conn)
|
||||||
|
bid = insert_bridge(conn, underlying="BTC", status="holding", budget_usdt=2200)
|
||||||
|
add_bridge_buy_fill(conn, bid, usdt_spent=2200.0, coin_bought=1.1)
|
||||||
|
update_bridge(conn, bid, coin_bought=0.01) # 缩成权利金
|
||||||
|
self.assertAlmostEqual(float(bridge_buy_vwap(conn, bid)), 2000.0, places=4)
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user