test(paper): update full-exit/partial-reduction equity tests to compounding model

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-06-22 10:17:46 +02:00
parent c17dd5c96e
commit 3ab4fec0cf

View File

@@ -47,33 +47,38 @@ def test_derive_and_persist_is_idempotent_per_run_date():
def test_realized_pnl_on_full_exit():
# Compounding MTM model: the FULL held-book gain (not just a realized sliver) flows into the nav curve,
# so equity reflects it even after a full exit to cash.
repo = PaperRepo("sqlite://")
repo.migrate()
svc = PaperBookService(repo, capital=100_000.0)
# day 1: long BTC, full notional 100k at price 100 -> qty 1000
_derive(svc, "2026-06-21", 1.0, {"BTC": 100.0}, "2026-06-21")
# day 2: fully exit (weight 0) at price 110 -> realized = 1000 * (110-100) = 10_000
_derive(svc, "2026-06-22", 0.0, {"BTC": 110.0}, "2026-06-22")
view = svc.view(live=FakeLivePrice({})) # no open positions
at = dt.datetime(2026, 6, 22, tzinfo=dt.UTC)
# day 1: long BTC full 100k at 100 -> qty 1000 (sized off equity = capital)
persist_paper_book(repo, capital=100_000.0, run_date="2026-06-21", sleeve_weights={"s": 1.0},
symbol_weights_by_sleeve={"s": {"BTC": 1.0}}, prices={"BTC": 100.0}, at=at)
# day 2: fully exit (sleeve weight 0) at 110 -> prior book marked: gain 1000*(110-100)=10_000 -> equity 110k, flat
persist_paper_book(repo, capital=100_000.0, run_date="2026-06-22", sleeve_weights={"s": 0.0},
symbol_weights_by_sleeve={"s": {"BTC": 1.0}}, prices={"BTC": 110.0}, at=at)
view = PaperBookService(repo, capital=100_000.0).view(live=FakeLivePrice({})) # no open positions
assert not view.positions
assert view.equity == 110_000.0 # capital + realized 10k, even with no open book
assert view.equity == pytest.approx(110_000.0) # full book gain captured by the compounding nav curve
def test_realized_pnl_on_partial_reduction():
# Compounding MTM model: holding the full book into the up-day captures the whole gain in the nav curve;
# the reduction still records a realized sliver (now display-only, not the equity driver).
repo = PaperRepo("sqlite://")
repo.migrate()
svc = PaperBookService(repo, capital=100_000.0)
at = dt.datetime(2026, 6, 22, tzinfo=dt.UTC)
# day 1: long BTC full 100k at 100 -> qty 1000
_derive(svc, "2026-06-21", 1.0, {"BTC": 100.0}, "2026-06-21")
# day 2: reduce to half weight at price 110. target notional 50k / 110 = ~454.5 qty.
# realized on the reduced (sold) qty = (1000 - 454.5...) * (110 - 100)
_derive(svc, "2026-06-22", 0.5, {"BTC": 110.0}, "2026-06-22")
closed = 1000.0 - (0.5 * 100_000.0 / 110.0)
expected_realized = closed * (110.0 - 100.0)
assert repo.realized_pnl() == pytest.approx(expected_realized)
view = svc.view(live=FakeLivePrice({"BTC": 110.0}))
# equity = capital + realized + unrealized(0 since last==entry on the surviving lot)
assert view.equity == pytest.approx(100_000.0 + expected_realized)
persist_paper_book(repo, capital=100_000.0, run_date="2026-06-21", sleeve_weights={"s": 1.0},
symbol_weights_by_sleeve={"s": {"BTC": 1.0}}, prices={"BTC": 100.0}, at=at)
# day 2: reduce to half sleeve weight at 110 -> prior full book (qty 1000) marked: gain 1000*(110-100)=10_000
persist_paper_book(repo, capital=100_000.0, run_date="2026-06-22", sleeve_weights={"s": 0.5},
symbol_weights_by_sleeve={"s": {"BTC": 1.0}}, prices={"BTC": 110.0}, at=at)
assert repo.realized_pnl() > 0.0 # reduction still records a realized sliver (display-only)
view = PaperBookService(repo, capital=100_000.0).view(live=FakeLivePrice({"BTC": 110.0}))
# equity = compounding nav curve (110k, full book gain) + intraday (0: surviving lot entry=110, marked @110)
assert view.equity == pytest.approx(110_000.0)
def test_persist_paper_book_writes_nav():