From e1a42a901152fa7ad300aed15303f151b7c67dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=83=D1=80=D0=BD=D0=B0=D1=82=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Tue, 14 Jul 2026 23:36:04 +0300 Subject: [PATCH] fix: align pooled symbol features at training --- tests/test_trade_objective_training.py | 36 +++++++++++++++++++++++ tools/train_torch_recurrent_forecaster.py | 4 +++ 2 files changed, 40 insertions(+) diff --git a/tests/test_trade_objective_training.py b/tests/test_trade_objective_training.py index 44206a6..1e674ed 100644 --- a/tests/test_trade_objective_training.py +++ b/tests/test_trade_objective_training.py @@ -12,6 +12,7 @@ from tools.train_torch_recurrent_forecaster import ( RecurrentReturnModel, _barrier_outcome, _export_head_state, + _prepare_data, ) @@ -84,3 +85,38 @@ def test_multitask_head_export_matches_runtime_inference() -> None: actual = _torch_head_outputs(context[0].tolist(), entry, hidden_size=4) assert actual == pytest.approx(expected, abs=2e-6) + + +def test_pooled_training_populates_symbol_identity_feature() -> None: + candles = [ + _candle( + index, + open_=100.0 + index * 0.01, + high=100.2 + index * 0.01, + low=99.8 + index * 0.01, + close=100.0 + index * 0.01, + ) + for index in range(180) + ] + + prepared = _prepare_data( + symbol="BTCUSDT", + candles=candles, + feature_names=["return_1", "symbol_is_BTCUSDT", "symbol_is_ETHUSDT"], + lookback=8, + target_horizons=[3], + decision_horizon=3, + round_trip_cost=0.002, + stop_loss_percent=0.04, + take_profit_percent=0.035, + market_candles={"BTCUSDT": candles}, + trend_candles=candles, + validation_window=24, + holdout_window=32, + clip=8.0, + device=torch.device("cpu"), + ) + + assert prepared is not None + assert torch.all(prepared.train_x[:, :, 1] == 1.0) + assert torch.all(prepared.train_x[:, :, 2] == 0.0) diff --git a/tools/train_torch_recurrent_forecaster.py b/tools/train_torch_recurrent_forecaster.py index 0c21e8b..2000bfa 100644 --- a/tools/train_torch_recurrent_forecaster.py +++ b/tools/train_torch_recurrent_forecaster.py @@ -361,6 +361,7 @@ def _train_pooled_symbols( prepared_by_symbol: dict[str, PreparedData] = {} for symbol in symbols: prepared = _prepare_data( + symbol=symbol, candles=market_candles[symbol], feature_names=feature_names, lookback=lookback, @@ -654,6 +655,7 @@ def _train_symbol( for lookback in lookbacks: _progress(f"{symbol}: preparing lookback={lookback}") prepared = _prepare_data( + symbol=symbol, candles=candles, feature_names=feature_names, lookback=lookback, @@ -777,6 +779,7 @@ def _train_symbol( def _prepare_data( *, + symbol: str, candles: list[Candle], feature_names: list[str], lookback: int, @@ -796,6 +799,7 @@ def _prepare_data( feature_rows = _feature_matrix( candles, feature_names, + symbol=symbol, market_candles=market_candles, trend_candles=trend_candles, )