Use Torch as the only forecast model
This commit is contained in:
+66
-50
@@ -34,11 +34,53 @@ def _candles_from_returns(returns: list[float]) -> list[Candle]:
|
||||
return candles
|
||||
|
||||
|
||||
def test_time_series_forecaster_selects_positive_predictive_model(make_settings, tmp_path) -> None:
|
||||
def _write_torch_gru_artifact(
|
||||
path,
|
||||
*,
|
||||
head_bias: float,
|
||||
validation_mae_percent: float = 0.02,
|
||||
baseline_mae_percent: float = 0.08,
|
||||
skill: float = 0.2,
|
||||
) -> None:
|
||||
hidden_size = 2
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 2,
|
||||
"type": "pytorch_recurrent_forecaster",
|
||||
"symbols": {
|
||||
"BTCUSDT": {
|
||||
"model": "torch_gru",
|
||||
"architecture": "gru",
|
||||
"lookback": 8,
|
||||
"hidden_size": hidden_size,
|
||||
"num_layers": 1,
|
||||
"mean": 0.0,
|
||||
"scale": 0.001,
|
||||
"clip": 8.0,
|
||||
"validation_mae_percent": validation_mae_percent,
|
||||
"baseline_mae_percent": baseline_mae_percent,
|
||||
"skill": skill,
|
||||
"state_dict": {
|
||||
"weight_ih_l0": [[0.0] for _ in range(3 * hidden_size)],
|
||||
"weight_hh_l0": [[0.0, 0.0] for _ in range(3 * hidden_size)],
|
||||
"bias_ih_l0": [0.0 for _ in range(3 * hidden_size)],
|
||||
"bias_hh_l0": [0.0 for _ in range(3 * hidden_size)],
|
||||
},
|
||||
"head_weight": [0.0, 0.0],
|
||||
"head_bias": head_bias,
|
||||
},
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def test_time_series_forecaster_requires_torch_artifact(make_settings, tmp_path) -> None:
|
||||
settings = make_settings(
|
||||
tmp_path,
|
||||
time_series_min_candles=80,
|
||||
time_series_validation_window=24,
|
||||
time_series_forecast_horizon=3,
|
||||
)
|
||||
returns = []
|
||||
@@ -47,32 +89,32 @@ def test_time_series_forecaster_selects_positive_predictive_model(make_settings,
|
||||
value = 0.00025 + value * 0.55
|
||||
returns.append(value)
|
||||
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns))
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns), symbol="BTCUSDT")
|
||||
|
||||
assert forecast.usable is True
|
||||
assert forecast.model != "naive"
|
||||
assert forecast.expected_return_percent > 0
|
||||
assert forecast.probability_up > 0.5
|
||||
assert forecast.usable is False
|
||||
assert forecast.model == "none"
|
||||
assert forecast.candidates == []
|
||||
assert "PyTorch" in forecast.reason
|
||||
|
||||
|
||||
def test_time_series_forecaster_blocks_negative_edge(make_settings, tmp_path) -> None:
|
||||
def test_time_series_forecaster_blocks_negative_torch_edge(make_settings, tmp_path) -> None:
|
||||
artifact_path = tmp_path / "lstm_forecaster.json"
|
||||
_write_torch_gru_artifact(artifact_path, head_bias=-0.8, validation_mae_percent=0.01)
|
||||
settings = make_settings(
|
||||
tmp_path,
|
||||
time_series_min_candles=80,
|
||||
time_series_validation_window=24,
|
||||
time_series_forecast_horizon=3,
|
||||
time_series_min_edge_percent=0.03,
|
||||
time_series_lstm_model_path=artifact_path,
|
||||
)
|
||||
returns = []
|
||||
value = -0.0003
|
||||
for _ in range(140):
|
||||
value = -0.00025 + value * 0.55
|
||||
returns.append(value)
|
||||
returns = [0.00015 if index % 4 else -0.00005 for index in range(140)]
|
||||
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns))
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns), symbol="BTCUSDT")
|
||||
|
||||
assert forecast.usable is True
|
||||
assert forecast.model == "torch_gru"
|
||||
assert forecast.expected_return_percent < 0
|
||||
assert forecast.probability_up < 0.45
|
||||
assert forecast.block_entry is True
|
||||
|
||||
|
||||
@@ -93,7 +135,6 @@ def test_time_series_forecaster_ignores_legacy_lstm_artifact(make_settings, tmp_
|
||||
settings = make_settings(
|
||||
tmp_path,
|
||||
time_series_min_candles=80,
|
||||
time_series_validation_window=20,
|
||||
time_series_lstm_enabled=True,
|
||||
time_series_lstm_model_path=artifact_path,
|
||||
)
|
||||
@@ -101,46 +142,18 @@ def test_time_series_forecaster_ignores_legacy_lstm_artifact(make_settings, tmp_
|
||||
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns), symbol="BTCUSDT")
|
||||
|
||||
assert forecast.usable is True
|
||||
assert all(candidate["model"] != "lstm" for candidate in forecast.candidates)
|
||||
assert forecast.usable is False
|
||||
assert forecast.model == "none"
|
||||
assert forecast.candidates == []
|
||||
assert "PyTorch" in forecast.reason
|
||||
|
||||
|
||||
def test_time_series_forecaster_reads_torch_gru_artifact(make_settings, tmp_path) -> None:
|
||||
artifact_path = tmp_path / "lstm_forecaster.json"
|
||||
hidden_size = 2
|
||||
artifact_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"version": 2,
|
||||
"type": "pytorch_recurrent_forecaster",
|
||||
"symbols": {
|
||||
"BTCUSDT": {
|
||||
"model": "torch_gru",
|
||||
"architecture": "gru",
|
||||
"lookback": 8,
|
||||
"hidden_size": hidden_size,
|
||||
"num_layers": 1,
|
||||
"mean": 0.0,
|
||||
"scale": 0.001,
|
||||
"clip": 8.0,
|
||||
"state_dict": {
|
||||
"weight_ih_l0": [[0.0] for _ in range(3 * hidden_size)],
|
||||
"weight_hh_l0": [[0.0, 0.0] for _ in range(3 * hidden_size)],
|
||||
"bias_ih_l0": [0.0 for _ in range(3 * hidden_size)],
|
||||
"bias_hh_l0": [0.0 for _ in range(3 * hidden_size)],
|
||||
},
|
||||
"head_weight": [0.0, 0.0],
|
||||
"head_bias": 0.2,
|
||||
},
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
_write_torch_gru_artifact(artifact_path, head_bias=0.2)
|
||||
settings = make_settings(
|
||||
tmp_path,
|
||||
time_series_min_candles=80,
|
||||
time_series_validation_window=20,
|
||||
time_series_lstm_enabled=True,
|
||||
time_series_lstm_model_path=artifact_path,
|
||||
)
|
||||
@@ -149,4 +162,7 @@ def test_time_series_forecaster_reads_torch_gru_artifact(make_settings, tmp_path
|
||||
forecast = TimeSeriesForecaster(settings).forecast(_candles_from_returns(returns), symbol="BTCUSDT")
|
||||
|
||||
assert forecast.usable is True
|
||||
assert any(candidate["model"] == "torch_gru" for candidate in forecast.candidates)
|
||||
assert forecast.model == "torch_gru"
|
||||
assert forecast.candidates == [{"model": "torch_gru", "mae_percent": 0.02}]
|
||||
assert forecast.expected_return_percent > 0
|
||||
assert forecast.probability_up > 0.5
|
||||
|
||||
Reference in New Issue
Block a user