fix(portfolio): runner data_dir dedicata, no resize posizioni aperte, poll da config, +test cap/cluster_rp
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import pytest
|
||||
from scripts.portfolios._defs import PORTFOLIOS
|
||||
|
||||
|
||||
def test_port06_cap_backtest_numbers_locked():
|
||||
r = PORTFOLIOS["PORT06"].backtest()
|
||||
# regression-lock dei numeri del default (cap pairs 0.33) — vedi report_families
|
||||
assert r.full["sharpe"] == pytest.approx(6.07, abs=0.15)
|
||||
assert r.oos["sharpe"] == pytest.approx(8.19, abs=0.25)
|
||||
assert r.full["dd"] == pytest.approx(4.9, abs=0.5)
|
||||
@@ -2,12 +2,24 @@ from src.portfolio.runner import rebalance_allocations
|
||||
from src.portfolio.ledger import PortfolioLedger
|
||||
|
||||
|
||||
def test_rebalance_resizes_to_total(tmp_path):
|
||||
L = PortfolioLedger("PX", total_capital=1000.0, data_dir=tmp_path)
|
||||
class FakeWorker:
|
||||
def __init__(self, cap, in_position=False):
|
||||
self.capital = cap
|
||||
self.in_position = in_position
|
||||
|
||||
class FakeWorker:
|
||||
def __init__(self, cap): self.capital = cap
|
||||
workers = {"a": FakeWorker(700.0), "b": FakeWorker(500.0)}
|
||||
|
||||
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_skips_in_position_worker(tmp_path):
|
||||
L = PortfolioLedger("PX", total_capital=1000.0, data_dir=tmp_path)
|
||||
workers = {"a": FakeWorker(700.0, in_position=True), "b": FakeWorker(500.0)}
|
||||
rebalance_allocations(L, workers, {"a": 0.5, "b": 0.5})
|
||||
assert L.total_capital == 1200.0
|
||||
assert workers["a"].capital == 700.0 # invariato: posizione aperta
|
||||
assert workers["b"].capital == 600.0 # flat: riallineato
|
||||
|
||||
@@ -39,3 +39,15 @@ def test_inverse_vol_prefers_low_vol():
|
||||
w = W.inverse_vol(["lo", "hi"], df, lookback=90)
|
||||
assert w["lo"] > w["hi"]
|
||||
assert pytest.approx(sum(w.values())) == 1.0
|
||||
|
||||
|
||||
def test_cluster_rp_equal_across_clusters():
|
||||
idx = pd.date_range("2024-01-01", periods=100, freq="D", tz="UTC")
|
||||
rng = np.random.default_rng(1)
|
||||
cols = ["MR01_BTC", "MR02_BTC", "PR_ETHBTC"]
|
||||
df = pd.DataFrame({c: rng.normal(0, 0.02, 100) for c in cols}, index=idx)
|
||||
clusters = {"MR01_BTC": "BTC-rev", "MR02_BTC": "BTC-rev", "PR_ETHBTC": "ETH-rev"}
|
||||
w = W.cluster_rp(cols, clusters, df, lookback=90)
|
||||
assert pytest.approx(sum(w.values())) == 1.0
|
||||
# due cluster equipesati: il cluster con 1 solo sleeve (ETH-rev) prende ~0.5
|
||||
assert pytest.approx(w["PR_ETHBTC"], abs=1e-9) == 0.5
|
||||
|
||||
Reference in New Issue
Block a user