feat: train forecasts on trade outcomes

This commit is contained in:
Курнат Андрей
2026-07-14 07:49:32 +03:00
parent 668e606ee2
commit 7186acb9a1
18 changed files with 867 additions and 94 deletions
+94
View File
@@ -2,6 +2,8 @@ from __future__ import annotations
import json
import pytest
from crypto_spot_bot.models import Candle
from crypto_spot_bot.time_series import TimeSeriesForecaster
@@ -191,6 +193,77 @@ def _write_probabilistic_torch_gru_artifact(path) -> None:
)
def _write_barrier_multitask_gru_artifact(path) -> None:
hidden_size = 2
head_hidden_size = 2
input_size = 2
output_size = 5
path.write_text(
json.dumps(
{
"version": 6,
"type": "pytorch_recurrent_forecaster",
"target_horizon": 3,
"target_horizons": [3],
"direct_horizon": True,
"target_transform": "barrier_net_return",
"event_target": "take_profit_before_stop_loss",
"round_trip_cost": 0.0026,
"output_layout": ["mean", "q10", "q50", "q90", "logit_tp_first"],
"feature_names": ["return_1", "range_percent"],
"symbols": {
"BTCUSDT": {
"model": "torch_gru",
"architecture": "gru",
"lookback": 8,
"target_horizon": 3,
"target_horizons": [3],
"direct_horizon": True,
"target_transform": "barrier_net_return",
"event_target": "take_profit_before_stop_loss",
"target_stop_loss_percent": 0.04,
"target_take_profit_percent": 0.035,
"round_trip_cost": 0.0026,
"output_layout": ["mean", "q10", "q50", "q90", "logit_tp_first"],
"input_size": input_size,
"output_size": output_size,
"feature_names": ["return_1", "range_percent"],
"feature_means": [0.0, 0.0],
"feature_scales": [0.001, 0.001],
"target_means": [0.0],
"target_scales": [1.0],
"target_mean": 0.0,
"target_scale": 1.0,
"hidden_size": hidden_size,
"num_layers": 1,
"clip": 8.0,
"validation_mae_by_horizon": {"3": 0.01},
"baseline_mae_by_horizon": {"3": 0.02},
"validation_mae_percent": 1.0,
"baseline_mae_percent": 2.0,
"skill": 0.2,
"multitask_head": True,
"head_hidden_size": head_hidden_size,
"state_dict": {
"weight_ih_l0": [[0.0, 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_hidden_weight": [[0.0, 0.0], [0.0, 0.0]],
"head_hidden_bias": [0.0, 0.0],
"return_head_weight": [[0.0, 0.0] for _ in range(4)],
"return_head_bias": [0.01, -0.01, 0.005, 0.02],
"event_head_weight": [[0.0, 0.0]],
"event_head_bias": [1.38629436112],
}
},
}
),
encoding="utf-8",
)
def test_time_series_forecaster_requires_torch_artifact(make_settings, tmp_path) -> None:
settings = make_settings(
tmp_path,
@@ -428,3 +501,24 @@ def test_time_series_forecaster_reads_probabilistic_multi_horizon_artifact(make_
assert forecast.feature_snapshot[0]["label"] == "Доходность 1ч"
assert forecast.feature_snapshot[0]["raw_display"].endswith("%")
assert "диапазон" in forecast.feature_snapshot[0]["interpretation"]
def test_time_series_forecaster_reads_barrier_multitask_artifact(make_settings, tmp_path) -> None:
artifact_path = tmp_path / "lstm_forecaster.json"
_write_barrier_multitask_gru_artifact(artifact_path)
settings = make_settings(
tmp_path,
time_series_lstm_model_path=artifact_path,
time_series_min_candles=80,
time_series_forecast_horizon=3,
)
forecast = TimeSeriesForecaster(settings).forecast(
_candles_from_returns([0.0002] * 140), symbol="BTCUSDT"
)
assert forecast.usable is True
assert forecast.target_transform == "barrier_net_return"
assert forecast.expected_return_percent == pytest.approx(1.005, abs=0.01)
assert forecast.probability_take_profit_first == pytest.approx(0.8, abs=0.001)
assert "P(TP before SL)" in forecast.reason