Files
quant_engine/tests/test_strategy_optimizer.py
2026-10-04 11:10:01 +08:00

180 lines
6.2 KiB
Python

"""Bounded optimizer runs actual core strategies and rejects ambiguous ranking."""
import numpy as np
import pandas as pd
import pytest
from quant_engine import strategy_optimizer as optimizer
from quant_engine.strategy_contracts import STRATEGIES, strategy_parameters
from quant_engine.strategy_research import BenchmarkInput, run_strategy_research
from quant_engine import strategy_contracts
def feed(values=None):
values = np.asarray(
values if values is not None else 10 + 2 * np.sin(np.arange(80) / 2), dtype=float
)
return pd.DataFrame(
dict.fromkeys(("open", "high", "low", "close"), values),
index=pd.date_range("2026-01-01", periods=len(values), freq="B"),
)
@pytest.mark.parametrize("name", STRATEGIES)
def test_each_default_strategy_uses_the_real_ledger(name):
defaults = strategy_parameters(name)
key = next(iter(defaults))
result = optimizer.optimize_strategy_research(
name,
feed(),
asset="SYNTHETIC",
param_grid={key: [defaults[key]]},
objective="total_return",
initial_cash=1000,
commission=0.01,
stamp_duty=0.002,
)
direct = run_strategy_research(
name, feed(), asset="SYNTHETIC", initial_cash=1000, commission=0.01, stamp_duty=0.002
)
assert len(result.trials) == 1
assert result.trials[0].result.ledger.positions == direct.ledger.positions
assert result.trials[0].result.ledger.daily_executions == direct.ledger.daily_executions
assert result.trials[0].score == direct.metrics["total_return"]
assert result.decision_eligible is False
def test_real_negative_zero_scores_and_explicit_costs_sort_without_defaults():
result = optimizer.optimize_strategy_research(
"BuyAndHold",
feed([10, 10, 9, 8]),
asset="SYNTHETIC",
param_grid={"buy_pct": [0.5, 0, 0.25]},
objective="total_return",
initial_cash=1000,
commission=0.01,
stamp_duty=0.002,
)
assert [trial.parameters["buy_pct"] for trial in result.trials] == [0, 0.25, 0.5]
assert [trial.score for trial in result.trials] == pytest.approx([0, -0.0525, -0.105])
assert [trial.result.ledger.total_costs for trial in result.trials] == pytest.approx(
[0, 2.5, 5]
)
def test_stable_ties_retain_canonical_axis_and_candidate_order():
result = optimizer.optimize_strategy_research(
"MACross",
feed([10] * 40),
asset="SYNTHETIC",
param_grid={"atr_period": [2, 0], "fast": [5, 4]},
objective="total_return",
commission=0,
stamp_duty=0,
)
assert [
(trial.parameters["fast"], trial.parameters["atr_period"]) for trial in result.trials
] == [(5, 2), (5, 0), (4, 2), (4, 0)]
def test_hundred_combinations_are_unique_actual_results():
result = optimizer.optimize_strategy_research(
"MACross",
feed(),
asset="SYNTHETIC",
param_grid={"fast": list(range(1, 11)), "slow": list(range(11, 21))},
objective="total_return",
commission=0,
stamp_duty=0,
)
assert len(result.trials) == 100
assert len({tuple(trial.parameters.items()) for trial in result.trials}) == 100
assert all(len(trial.result.ledger.positions) == 80 for trial in result.trials)
@pytest.mark.parametrize(
"grid,objective",
[
({"fast": []}, "total_return"),
({"fast": [5, 40]}, "total_return"),
({"fast": [True]}, "total_return"),
({"fast": [5]}, "unknown"),
({"fast": [5, 5]}, "total_return"),
(
{"fast": list(range(1, 11)), "slow": list(range(11, 21)), "atr_period": [0, 1]},
"total_return",
),
],
)
def test_invalid_or_partly_invalid_grid_never_starts_a_strategy(monkeypatch, grid, objective):
calls = []
monkeypatch.setattr(optimizer, "run_strategy_research", lambda *a, **kw: calls.append(1))
with pytest.raises(
ValueError, match=r"grid|Grid|candidate|number|less than|objective|Duplicate"
):
optimizer.optimize_strategy_research(
"MACross", feed(), asset="SYNTHETIC", param_grid=grid, objective=objective
)
assert calls == []
def test_history_failure_for_one_candidate_prevents_all_runs(monkeypatch):
calls = []
monkeypatch.setattr(optimizer, "run_strategy_research", lambda *a, **kw: calls.append(1))
with pytest.raises(ValueError, match=r"history"):
optimizer.optimize_strategy_research(
"MACross",
feed([10] * 40),
asset="SYNTHETIC",
param_grid={"slow": [30, 100]},
objective="total_return",
)
assert calls == []
def test_requested_benchmark_error_prevents_every_run(monkeypatch):
calls = []
monkeypatch.setattr(optimizer, "run_strategy_research", lambda *a, **kw: calls.append(1))
with pytest.raises(ValueError, match=r"benchmark source"):
optimizer.optimize_strategy_research(
"BuyAndHold",
feed(),
asset="SYNTHETIC",
param_grid={"buy_pct": [0.5, 1]},
objective="total_return",
benchmark=BenchmarkInput("source_error"),
)
assert calls == []
def test_undefined_sharpe_fails_the_ranking_instead_of_winning_as_zero():
with pytest.raises(ValueError, match=r"objective.*unavailable"):
optimizer.optimize_strategy_research(
"BuyAndHold",
feed([10] * 4),
asset="SYNTHETIC",
param_grid={"buy_pct": [0, 0.5]},
objective="sharpe_ratio",
commission=0,
stamp_duty=0,
)
def test_large_grid_fails_before_creating_cartesian_product(monkeypatch):
calls = []
monkeypatch.setattr(strategy_contracts, "product", lambda *a, **kw: calls.append("product"))
monkeypatch.setattr(optimizer, "run_strategy_research", lambda *a, **kw: calls.append("run"))
with pytest.raises(ValueError, match=r"limit"):
optimizer.optimize_strategy_research(
"MACross",
feed(),
asset="SYNTHETIC",
param_grid={
"fast": list(range(1, 11)),
"slow": list(range(11, 21)),
"atr_period": list(range(10)),
"atr_mult": list(range(1, 11)),
},
)
assert calls == []