Repository navigation
Expand file tree
/
Copy pathstrategy_selftest.py
More file actions
155 lines (119 loc) · 4.6 KB
/
Copy pathstrategy_selftest.py
File metadata and controls
155 lines (119 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""Strategy-level checks for agent.py.
No network, no private engine, no third-party packages. These are not the
official builderr evals; they catch contract, cap, and regime bugs before
submission.
Run:
python strategy_selftest.py
"""
from __future__ import annotations
import time
from datetime import date, timedelta
import agent
UNIVERSE = (
"SPY", "QQQ", "DIA", "IWM",
"XLK", "XLF", "XLE", "XLV", "XLI", "XLY", "XLP", "XLU", "XLRE", "XLC", "SMH",
"AAPL", "MSFT", "GOOGL", "AMZN", "META", "NVDA", "TSLA",
"QLD", "SSO",
)
def bars(start: float, returns: list[float]) -> list[dict]:
out = []
px = start
d = date(2024, 1, 1)
for r in returns:
px *= 1.0 + r
out.append({
"ts": d.isoformat(),
"open": px,
"high": px * 1.01,
"low": px * 0.99,
"close": px,
"volume": 1_000_000,
})
d += timedelta(days=1)
return out
def market(kind: str) -> dict[str, list[dict]]:
if kind == "risk_off":
base = [-0.003] * 90
defensive = [0.0005] * 90
return {t: bars(100.0, defensive if t in {"XLP", "XLU", "XLV", "XLE"} else base) for t in UNIVERSE}
if kind == "high_vol":
calm_up = [0.002] * 90
qqq_chop = ([0.035, -0.03] * 45)
data = {t: bars(100.0, calm_up) for t in UNIVERSE}
data["QQQ"] = bars(100.0, qqq_chop)
return data
# Low-vol risk-on, with differentiated momentum.
data = {t: bars(100.0, [0.001] * 90) for t in UNIVERSE}
for t in ("SMH", "NVDA", "XLK"):
data[t] = bars(100.0, [0.004] * 90)
for t in ("QQQ", "AAPL", "META"):
data[t] = bars(100.0, [0.0025] * 90)
data["SPY"] = bars(100.0, [0.0018] * 90)
data["QLD"] = bars(100.0, [0.0048] * 90)
data["SSO"] = bars(100.0, [0.0034] * 90)
return data
def reset_agent_state() -> None:
agent._last_rebalance_bar_date = None
agent._last_targets = {}
def beta_gross(weights: dict[str, float]) -> float:
return sum(w * agent.BETA_MULTIPLE.get(t, 1.0) for t, w in weights.items())
def test_empty_data_returns_no_orders() -> None:
reset_agent_state()
assert agent.decide({}, {"cash": 100_000, "positions": [], "last_prices": {}}, 100_000) == []
def test_insufficient_history_returns_no_targets() -> None:
short_market = {t: bars(100.0, [0.001] * 40) for t in UNIVERSE}
assert agent.target_weights(short_market) == {}
def test_risk_off_uses_defensive_book() -> None:
weights = agent.target_weights(market("risk_off"))
assert set(weights).issubset({"XLP", "XLU", "XLV", "XLE"})
assert weights
def test_risk_on_selects_positive_momentum() -> None:
weights = agent.target_weights(market("risk_on"))
assert {"SMH", "NVDA", "XLK"} & set(weights)
assert len(weights) >= 4
def test_high_vol_disables_leverage() -> None:
weights = agent.target_weights(market("high_vol"))
assert "QLD" not in weights
assert "SSO" not in weights
def test_caps_hold() -> None:
for kind in ("risk_off", "high_vol", "risk_on"):
weights = agent.target_weights(market(kind))
assert all(w < 0.240001 for w in weights.values()), (kind, weights)
assert beta_gross(weights) <= 1.350001, (kind, weights, beta_gross(weights))
def test_orders_are_bounded_and_fast() -> None:
reset_agent_state()
m = market("risk_on")
latest = {t: b[-1]["close"] for t, b in m.items()}
portfolio = {"cash": 100_000.0, "positions": [], "last_prices": latest}
start = time.perf_counter()
orders = agent.decide(m, portfolio, 100_000.0)
elapsed = time.perf_counter() - start
assert elapsed < 0.05, elapsed
assert 0 < len(orders) < 50, orders
assert all(o["side"] in {"buy", "sell"} and o["quantity"] > 0 for o in orders)
assert agent.decide(m, portfolio, 100_000.0) == []
def test_tiny_stale_position_is_not_sold() -> None:
orders = agent.orders_to_rebalance(
targets={"SPY": 0.20},
positions={"XYZ": {"quantity": 0.5, "avg_cost": 100.0}},
total_equity=100_000.0,
prices={"XYZ": 100.0, "SPY": 500.0},
cash_available=0.0,
)
assert orders == []
def run() -> None:
tests = [
test_empty_data_returns_no_orders,
test_insufficient_history_returns_no_targets,
test_risk_off_uses_defensive_book,
test_risk_on_selects_positive_momentum,
test_high_vol_disables_leverage,
test_caps_hold,
test_orders_are_bounded_and_fast,
test_tiny_stale_position_is_not_sold,
]
for test in tests:
test()
print(f"✓ {len(tests)} strategy checks passed.")
if __name__ == "__main__":
run()