Count expiry settlements as martingale losses.
Even small-profit expiry closes count toward consecutive loss days; only non-expiry profitable days break the streak. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -242,3 +242,45 @@ def test_martingale_doubles_capped(tmp_path, monkeypatch) -> None:
|
||||
assert mg["doubles"] == 3
|
||||
assert abs(float(mg["effective_pct"]) - 16.0) < 1e-9
|
||||
db.close()
|
||||
|
||||
|
||||
def test_expiry_settle_counts_as_loss_even_if_profit(tmp_path, monkeypatch) -> None:
|
||||
"""到期结算小盈利也按亏损计入倍投连亏日。"""
|
||||
monkeypatch.setenv("MODE", "SIM")
|
||||
from datetime import datetime, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from app.models.db import Database
|
||||
from app.strategy.risk_sizing import consecutive_loss_days
|
||||
|
||||
db = Database(tmp_path / "mg_exp.db")
|
||||
sh = ZoneInfo("Asia/Shanghai")
|
||||
|
||||
def day_ms(ymd: str) -> int:
|
||||
dt = datetime.strptime(ymd, "%Y-%m-%d").replace(hour=16, tzinfo=sh)
|
||||
return int(dt.astimezone(timezone.utc).timestamp() * 1000)
|
||||
|
||||
# 达标盈利打断;随后两天均为到期小盈利 → 仍计连亏 2
|
||||
rows = [
|
||||
("e0", day_ms("2026-07-28"), 20.0, "fixed_usdt"),
|
||||
("e1", day_ms("2026-07-29"), 3.5, "expiry"),
|
||||
("e2", day_ms("2026-07-30"), 1.2, "expiry"),
|
||||
]
|
||||
for gid, ms, pnl, reason in rows:
|
||||
db.execute(
|
||||
"""INSERT INTO groups(
|
||||
group_id, status, realized_pnl, close_at_ms, open_at_ms, close_reason
|
||||
) VALUES(?,?,?,?,?,?)""",
|
||||
(gid, "closed", pnl, ms, ms - 3600_000, reason),
|
||||
)
|
||||
assert consecutive_loss_days(db) == 2
|
||||
|
||||
# 再来一天达标盈利 → 连亏清零
|
||||
db.execute(
|
||||
"""INSERT INTO groups(
|
||||
group_id, status, realized_pnl, close_at_ms, open_at_ms, close_reason
|
||||
) VALUES(?,?,?,?,?,?)""",
|
||||
("e3", "closed", 15.0, day_ms("2026-07-31"), day_ms("2026-07-31") - 1000, "fixed_usdt"),
|
||||
)
|
||||
assert consecutive_loss_days(db) == 0
|
||||
db.close()
|
||||
|
||||
Reference in New Issue
Block a user