Equity = prior equity + the PRIOR held book marked to today's close (Σ positions_before.qty·(close−entry)), and the throttled book is sized off that running equity so gains compound. Previously positions were re-derived each day at that day's close, so unrealized≡0 and equity only reflected realized slivers, discarding all held-position appreciation. Also fix positions_before to anchor on nav rows (one per rebalanced run_date) instead of position rows, so a FLAT book (full exit / kill-switch, which persists zero position rows) is honored as the prior: the day after a kill correctly sees an EMPTY book rather than the last non-empty book before it. Updated test_backfill_rerun_is_idempotent_* to assert compounding equity reproduction instead of the old realized-monotone model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
38 lines
2.1 KiB
Python
38 lines
2.1 KiB
Python
import datetime as dt
|
|
from fxhnt.adapters.persistence.paper_repo import PaperRepo
|
|
from fxhnt.application.paper_book import persist_paper_book
|
|
|
|
def _p(repo, d, px, lev=1.0):
|
|
persist_paper_book(repo, capital=100000.0, run_date=d, sleeve_weights={"s": 1.0},
|
|
symbol_weights_by_sleeve={"s": {"X": 1.0}}, prices={"X": px},
|
|
at=dt.datetime(2026, 1, 1, tzinfo=dt.UTC), leverage=lev)
|
|
|
|
def test_equity_compounds_off_prior_equity():
|
|
r = PaperRepo("sqlite://"); r.migrate()
|
|
_p(r, "2026-01-01", 100.0) # day0: no prior -> equity = capital
|
|
assert r.nav_history()[-1].equity == 100000.0
|
|
_p(r, "2026-01-02", 110.0) # prior qty 1000 *(110-100)=+10000 -> 110000
|
|
assert abs(r.nav_history()[-1].equity - 110000.0) < 1e-6
|
|
pos = r.positions_at("2026-01-02") # sized off 110000 -> qty 110000/110=1000
|
|
assert abs(pos[0].qty - 110000.0 / 110.0) < 1e-6
|
|
_p(r, "2026-01-03", 121.0) # +10% again, compounded -> 121000
|
|
assert abs(r.nav_history()[-1].equity - 121000.0) < 1e-6
|
|
|
|
def test_killed_flat_book_keeps_equity_flat():
|
|
r = PaperRepo("sqlite://"); r.migrate()
|
|
_p(r, "2026-01-01", 100.0)
|
|
_p(r, "2026-01-02", 110.0) # equity 110000
|
|
_p(r, "2026-01-03", 130.0, lev=0.0) # kill: prior(qty1000)*(130-110)=+20000 -> 130000, FLAT book
|
|
assert abs(r.nav_history()[-1].equity - 130000.0) < 1e-6
|
|
assert r.positions_at("2026-01-03") == []
|
|
_p(r, "2026-01-04", 200.0) # prior empty -> gain 0 -> equity flat 130000
|
|
assert abs(r.nav_history()[-1].equity - 130000.0) < 1e-6
|
|
|
|
def test_rerun_is_idempotent():
|
|
r = PaperRepo("sqlite://"); r.migrate()
|
|
seq = [("2026-01-01", 100.0), ("2026-01-02", 110.0), ("2026-01-03", 121.0)]
|
|
for d, px in seq: _p(r, d, px)
|
|
first = [(n.run_date, n.equity) for n in r.nav_history()]
|
|
for d, px in seq: _p(r, d, px) # re-run ascending
|
|
assert [(n.run_date, n.equity) for n in r.nav_history()] == first
|