Files
PythagorasGoal/tests/portfolio/test_runner_rebalance.py
T
Adriano Dal Pastro 432c1ad790 fix(ledger): ribilancio conserva l'equity (no doppio conteggio in-position)
Bug: i flat si dividevano l'INTERO total includendo il capitale dei worker in
posizione, che lo trattenevano in piu' -> equity gonfiata di Sigma(capital-alloc)
sugli in-pos. Caso MR02_BTC 15m seedato (181.19) e in posizione al ribilancio
00:01: +4.77 fantasma. allocate(weights, reserved={sid:cap}): gli in-pos
trattengono il loro, i flat si dividono total-Sigma(reserved) per peso
rinormalizzato -> Sigma alloc == total sempre. Parita' runner intatta
(5.8e-08). Test conservazione (ledger + runner). Suite verde.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 10:09:36 +00:00

34 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from src.portfolio.runner import rebalance_allocations
from src.portfolio.ledger import PortfolioLedger
class FakeWorker:
def __init__(self, cap, in_position=False):
self.capital = cap
self.in_position = in_position
def test_rebalance_resizes_flat_workers(tmp_path):
L = PortfolioLedger("PX", total_capital=1000.0, data_dir=tmp_path)
workers = {"a": FakeWorker(700.0), "b": FakeWorker(500.0)} # equity 1200, both flat
rebalance_allocations(L, workers, {"a": 0.5, "b": 0.5})
assert L.total_capital == 1200.0
assert workers["a"].capital == 600.0 and workers["b"].capital == 600.0
def test_rebalance_in_position_conserves_equity(tmp_path):
# FIX 2026-06-13: il worker in posizione trattiene il suo capitale (deployato);
# i flat si dividono il RESTO per peso rinormalizzato -> Σcapital CONSERVATO.
# Prima (bug): i flat prendevano peso×total includendo il capitale dell'in-pos
# -> somma gonfiata (qui sarebbe stata 1300, +100 fantasma).
L = PortfolioLedger("PX", total_capital=1000.0, data_dir=tmp_path)
workers = {"a": FakeWorker(700.0, in_position=True),
"b": FakeWorker(300.0), "c": FakeWorker(200.0)} # equity 1200
rebalance_allocations(L, workers, {"a": 0.5, "b": 0.25, "c": 0.25})
assert L.total_capital == 1200.0
assert workers["a"].capital == 700.0 # invariato: posizione aperta (reserved)
# b e c si dividono 1200-700=500 per peso 0.25/0.25 rinormalizzato -> 250 ciascuno
assert workers["b"].capital == 250.0 and workers["c"].capital == 250.0
total = sum(w.capital for w in workers.values())
assert total == 1200.0 # equity CONSERVATA dal ribilancio