修复翻倍出场达标却不自动平:对冲异常不再整段跳过,并按买一价×全仓判定。
原先对冲表异常会直接 return 0 导致翻倍出场永不执行;可回收改为买一价×全部张数,买一深度不足时仍会触发分批平仓。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -211,13 +211,34 @@ def _estimate_recycle(
|
|||||||
pos: dict[str, Any],
|
pos: dict[str, Any],
|
||||||
premium_paid: float | None,
|
premium_paid: float | None,
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
|
"""翻倍出场用可回收估算.
|
||||||
|
|
||||||
|
用买一价 × 全部持仓张数(不吃深度上限):深度不够时仍应触发分批平仓;
|
||||||
|
残档/无效买一则返回 None(禁止按假买盘自动平).
|
||||||
|
"""
|
||||||
from lib.options.options_positions_lib import attach_close_preview
|
from lib.options.options_positions_lib import attach_close_preview
|
||||||
|
from lib.options.options_pricing_lib import total_premium
|
||||||
|
|
||||||
row = dict(pos)
|
row = dict(pos)
|
||||||
attach_close_preview(cfg, ex, row, premium_paid=premium_paid)
|
attach_close_preview(cfg, ex, row, premium_paid=premium_paid)
|
||||||
preview = row.get("close_preview") if isinstance(row.get("close_preview"), dict) else {}
|
preview = row.get("close_preview") if isinstance(row.get("close_preview"), dict) else {}
|
||||||
if preview.get("bid_invalid"):
|
if preview.get("bid_invalid"):
|
||||||
return None
|
return None
|
||||||
|
levels = preview.get("levels") or []
|
||||||
|
ct_mult = float(row.get("ct_mult") or 0.01)
|
||||||
|
sheets = int(
|
||||||
|
abs(
|
||||||
|
_safe_float(row.get("avail_pos") if row.get("avail_pos") is not None else row.get("pos"))
|
||||||
|
or 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if levels and sheets > 0 and ct_mult > 0:
|
||||||
|
try:
|
||||||
|
px = float(levels[0].get("px") or 0)
|
||||||
|
except (TypeError, ValueError, AttributeError):
|
||||||
|
px = 0.0
|
||||||
|
if px > 0:
|
||||||
|
return round(total_premium(px, sheets * ct_mult), 8)
|
||||||
return _safe_float(preview.get("total_received"))
|
return _safe_float(preview.get("total_received"))
|
||||||
|
|
||||||
|
|
||||||
@@ -296,7 +317,8 @@ def run_options_profit_exits(
|
|||||||
init_hedge_plan_tables(conn)
|
init_hedge_plan_tables(conn)
|
||||||
hedge_managed = active_hedge_option_inst_ids(conn)
|
hedge_managed = active_hedge_option_inst_ids(conn)
|
||||||
except Exception:
|
except Exception:
|
||||||
return 0
|
# 对冲表异常时不得整段跳过翻倍出场
|
||||||
|
hedge_managed = set()
|
||||||
|
|
||||||
rules = profit_exit_by_inst(conn)
|
rules = profit_exit_by_inst(conn)
|
||||||
triggered = 0
|
triggered = 0
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
"""翻倍出场:买一价×全仓张数判定达标(不受买一深度截断)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from lib.options.options_profit_exit_lib import _estimate_recycle, profit_exit_hit
|
||||||
|
|
||||||
|
|
||||||
|
class ProfitExitHitTests(unittest.TestCase):
|
||||||
|
def test_hit_at_0_3_mult(self):
|
||||||
|
# 0.3倍: recycle >= paid * 1.3
|
||||||
|
self.assertTrue(profit_exit_hit(premium_paid=1.0, recycle_usdc=1.3, mult=0.3))
|
||||||
|
self.assertFalse(profit_exit_hit(premium_paid=1.0, recycle_usdc=1.29, mult=0.3))
|
||||||
|
|
||||||
|
def test_estimate_uses_full_sheets_not_depth(self):
|
||||||
|
pos = {"inst_id": "ETH-USD-260927-2700-P", "pos": 100, "ct_mult": 0.01}
|
||||||
|
preview = {
|
||||||
|
"bid_invalid": False,
|
||||||
|
"levels": [{"px": 0.2, "sheets": 10}], # 深度只够 10 张
|
||||||
|
"total_received": 0.02, # 仅深度内
|
||||||
|
}
|
||||||
|
|
||||||
|
def fake_attach(cfg, ex, row, premium_paid=None):
|
||||||
|
row["close_preview"] = preview
|
||||||
|
row["ct_mult"] = 0.01
|
||||||
|
row["pos"] = 100
|
||||||
|
return row
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"lib.options.options_positions_lib.attach_close_preview",
|
||||||
|
side_effect=fake_attach,
|
||||||
|
):
|
||||||
|
recv = _estimate_recycle({}, object(), pos, 1.0)
|
||||||
|
# 应按 100 张 × 0.01 × 0.2 = 0.2,而非深度截断的 0.02
|
||||||
|
self.assertAlmostEqual(float(recv), 0.2, places=6)
|
||||||
|
self.assertTrue(profit_exit_hit(premium_paid=0.15, recycle_usdc=float(recv), mult=0.3))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user