ifieryarrows commited on
Commit
00044fd
·
verified ·
1 Parent(s): e411cee

Sync from GitHub (tests passed)

Browse files
deep_learning/config.py CHANGED
@@ -136,10 +136,12 @@ class ASROConfig:
136
 
137
  @dataclass(frozen=True)
138
  class WeeklyLossConfig:
139
- lambda_weekly_quantile: float = 0.55
140
- lambda_t1_quantile: float = 0.15
141
- lambda_dispersion: float = 0.20
142
- lambda_directional: float = 0.10
 
 
143
 
144
 
145
  @dataclass(frozen=True)
 
136
 
137
  @dataclass(frozen=True)
138
  class WeeklyLossConfig:
139
+ lambda_weekly_quantile: float = 0.70
140
+ lambda_t1_quantile: float = 0.20
141
+ lambda_dispersion: float = 0.35
142
+ lambda_magnitude: float = 0.50
143
+ lambda_naive: float = 0.50
144
+ lambda_directional: float = 0.00
145
 
146
 
147
  @dataclass(frozen=True)
deep_learning/models/monotonic_quantiles.py CHANGED
@@ -4,11 +4,14 @@ import torch
4
  import torch.nn.functional as F
5
 
6
 
 
 
 
7
  def enforce_monotonic_quantiles(
8
  y_pred: torch.Tensor,
9
  median_idx: int = 3,
10
  min_gap: float = 1e-5,
11
- gap_scale: float = 0.01,
12
  init_bias: float = -3.0,
13
  ) -> torch.Tensor:
14
  """
 
4
  import torch.nn.functional as F
5
 
6
 
7
+ DEFAULT_MONOTONIC_GAP_SCALE = 0.02
8
+
9
+
10
  def enforce_monotonic_quantiles(
11
  y_pred: torch.Tensor,
12
  median_idx: int = 3,
13
  min_gap: float = 1e-5,
14
+ gap_scale: float = DEFAULT_MONOTONIC_GAP_SCALE,
15
  init_bias: float = -3.0,
16
  ) -> torch.Tensor:
17
  """
deep_learning/models/tft_copper.py CHANGED
@@ -21,6 +21,7 @@ import numpy as np
21
  from deep_learning.contract import RETURN_SPACE, log_to_simple_return
22
  from deep_learning.config import TFTASROConfig, get_tft_config
23
  from deep_learning.models.monotonic_quantiles import (
 
24
  enforce_monotonic_quantiles,
25
  validate_monotonicity,
26
  )
@@ -136,10 +137,12 @@ try:
136
  def __init__(
137
  self,
138
  quantiles: list,
139
- lambda_weekly_quantile: float = 0.55,
140
- lambda_t1_quantile: float = 0.15,
141
- lambda_dispersion: float = 0.20,
142
- lambda_directional: float = 0.10,
 
 
143
  sharpe_eps: float = 1e-8,
144
  debug_mode: bool = False,
145
  ):
@@ -148,6 +151,8 @@ try:
148
  self.lambda_t1_quantile = lambda_t1_quantile
149
  self.lambda_dispersion = lambda_dispersion
150
  self.lambda_directional = lambda_directional
 
 
151
  self.sharpe_eps = sharpe_eps
152
  self.debug_mode = debug_mode
153
  self.median_idx = len(quantiles) // 2
@@ -158,6 +163,8 @@ try:
158
  "weekly_q": 0.0,
159
  "t1_q": 0.0,
160
  "dispersion": 0.0,
 
 
161
  "directional": 0.0,
162
  "total": 0.0,
163
  }
@@ -168,12 +175,16 @@ try:
168
  weekly_q_loss: torch.Tensor,
169
  t1_q_loss: torch.Tensor,
170
  dispersion_loss: torch.Tensor,
 
 
171
  directional_loss: torch.Tensor,
172
  total_loss: torch.Tensor,
173
  ) -> None:
174
  self._component_sums["weekly_q"] += float(weekly_q_loss.detach().mean().cpu())
175
  self._component_sums["t1_q"] += float(t1_q_loss.detach().mean().cpu())
176
  self._component_sums["dispersion"] += float(dispersion_loss.detach().mean().cpu())
 
 
177
  self._component_sums["directional"] += float(directional_loss.detach().mean().cpu())
178
  self._component_sums["total"] += float(total_loss.detach().mean().cpu())
179
  self._component_batches += 1
@@ -186,6 +197,8 @@ try:
186
  "weekly_q_loss_mean": 0.0,
187
  "t1_q_loss_mean": 0.0,
188
  "dispersion_loss_mean": 0.0,
 
 
189
  "directional_loss_mean": 0.0,
190
  "total_loss_mean": 0.0,
191
  "dominant_component": None,
@@ -195,6 +208,8 @@ try:
195
  "weekly_q": self._component_sums["weekly_q"],
196
  "t1_q": self._component_sums["t1_q"],
197
  "dispersion": self._component_sums["dispersion"],
 
 
198
  "directional": self._component_sums["directional"],
199
  }
200
  return {
@@ -202,6 +217,8 @@ try:
202
  "weekly_q_loss_mean": self._component_sums["weekly_q"] / n_batches,
203
  "t1_q_loss_mean": self._component_sums["t1_q"] / n_batches,
204
  "dispersion_loss_mean": self._component_sums["dispersion"] / n_batches,
 
 
205
  "directional_loss_mean": self._component_sums["directional"] / n_batches,
206
  "total_loss_mean": self._component_sums["total"] / n_batches,
207
  "dominant_component": max(components, key=components.get),
@@ -225,7 +242,7 @@ try:
225
  y_pred,
226
  median_idx=self.median_idx,
227
  min_gap=1e-5,
228
- gap_scale=0.01,
229
  init_bias=-3.0,
230
  )
231
  if self.debug_mode:
@@ -258,7 +275,11 @@ try:
258
  pred_abs_med = pred_weekly_median.abs().median() + eps
259
  actual_abs_med = actual_weekly.abs().median() + eps
260
  magnitude_loss = torch.abs(torch.log(pred_abs_med / actual_abs_med))
261
- combined_calibration_loss = 0.5 * dispersion_loss + 0.5 * magnitude_loss
 
 
 
 
262
 
263
  pred_direction = torch.tanh(median_path * 10.0)
264
  actual_direction = torch.sign(y_actual)
@@ -272,18 +293,25 @@ try:
272
 
273
  weekly_q_loss = _to_scalar(weekly_q_loss)
274
  t1_q_loss = _to_scalar(t1_q_loss)
275
- combined_calibration_loss = _to_scalar(combined_calibration_loss)
 
276
  directional_loss = _to_scalar(directional_loss)
 
277
  total_loss = (
278
  self.lambda_weekly_quantile * _to_scalar(weekly_q_loss)
279
  + self.lambda_t1_quantile * _to_scalar(t1_q_loss)
280
- + self.lambda_dispersion * _to_scalar(combined_calibration_loss)
 
 
281
  + self.lambda_directional * _to_scalar(directional_loss)
282
  )
 
283
  self._record_components(
284
  weekly_q_loss,
285
  t1_q_loss,
286
- combined_calibration_loss,
 
 
287
  directional_loss,
288
  total_loss,
289
  )
@@ -324,14 +352,20 @@ def create_tft_model(
324
  lambda_weekly_quantile=cfg.weekly_loss.lambda_weekly_quantile,
325
  lambda_t1_quantile=cfg.weekly_loss.lambda_t1_quantile,
326
  lambda_dispersion=cfg.weekly_loss.lambda_dispersion,
 
 
327
  lambda_directional=cfg.weekly_loss.lambda_directional,
328
  )
329
  logger.info(
330
- "Using weekly ASRO loss | weekly_q=%.2f t1_q=%.2f dispersion=%.2f dir=%.2f monotonic_transform=true",
 
331
  cfg.weekly_loss.lambda_weekly_quantile,
332
  cfg.weekly_loss.lambda_t1_quantile,
333
  cfg.weekly_loss.lambda_dispersion,
 
 
334
  cfg.weekly_loss.lambda_directional,
 
335
  )
336
  elif use_asro and ASROPFLoss is not None:
337
  loss = ASROPFLoss(
@@ -513,7 +547,7 @@ def _format_prediction_legacy_simple_return(
513
  torch.as_tensor(raw_pred, dtype=torch.float64),
514
  median_idx=median_idx,
515
  min_gap=1e-5,
516
- gap_scale=0.01,
517
  init_bias=-3.0,
518
  )
519
  pred = ordered_tensor.detach().cpu().numpy()
@@ -674,7 +708,7 @@ def format_prediction(
674
  torch.as_tensor(raw_pred, dtype=torch.float64),
675
  median_idx=median_idx,
676
  min_gap=1e-5,
677
- gap_scale=0.01,
678
  init_bias=-3.0,
679
  )
680
  pred = ordered_tensor.detach().cpu().numpy()
 
21
  from deep_learning.contract import RETURN_SPACE, log_to_simple_return
22
  from deep_learning.config import TFTASROConfig, get_tft_config
23
  from deep_learning.models.monotonic_quantiles import (
24
+ DEFAULT_MONOTONIC_GAP_SCALE,
25
  enforce_monotonic_quantiles,
26
  validate_monotonicity,
27
  )
 
137
  def __init__(
138
  self,
139
  quantiles: list,
140
+ lambda_weekly_quantile: float = 0.70,
141
+ lambda_t1_quantile: float = 0.20,
142
+ lambda_dispersion: float = 0.35,
143
+ lambda_directional: float = 0.00,
144
+ lambda_magnitude: float = 0.50,
145
+ lambda_naive: float = 0.50,
146
  sharpe_eps: float = 1e-8,
147
  debug_mode: bool = False,
148
  ):
 
151
  self.lambda_t1_quantile = lambda_t1_quantile
152
  self.lambda_dispersion = lambda_dispersion
153
  self.lambda_directional = lambda_directional
154
+ self.lambda_magnitude = lambda_magnitude
155
+ self.lambda_naive = lambda_naive
156
  self.sharpe_eps = sharpe_eps
157
  self.debug_mode = debug_mode
158
  self.median_idx = len(quantiles) // 2
 
163
  "weekly_q": 0.0,
164
  "t1_q": 0.0,
165
  "dispersion": 0.0,
166
+ "magnitude": 0.0,
167
+ "naive": 0.0,
168
  "directional": 0.0,
169
  "total": 0.0,
170
  }
 
175
  weekly_q_loss: torch.Tensor,
176
  t1_q_loss: torch.Tensor,
177
  dispersion_loss: torch.Tensor,
178
+ magnitude_loss: torch.Tensor,
179
+ naive_relative_loss: torch.Tensor,
180
  directional_loss: torch.Tensor,
181
  total_loss: torch.Tensor,
182
  ) -> None:
183
  self._component_sums["weekly_q"] += float(weekly_q_loss.detach().mean().cpu())
184
  self._component_sums["t1_q"] += float(t1_q_loss.detach().mean().cpu())
185
  self._component_sums["dispersion"] += float(dispersion_loss.detach().mean().cpu())
186
+ self._component_sums["magnitude"] += float(magnitude_loss.detach().mean().cpu())
187
+ self._component_sums["naive"] += float(naive_relative_loss.detach().mean().cpu())
188
  self._component_sums["directional"] += float(directional_loss.detach().mean().cpu())
189
  self._component_sums["total"] += float(total_loss.detach().mean().cpu())
190
  self._component_batches += 1
 
197
  "weekly_q_loss_mean": 0.0,
198
  "t1_q_loss_mean": 0.0,
199
  "dispersion_loss_mean": 0.0,
200
+ "magnitude_loss_mean": 0.0,
201
+ "naive_loss_mean": 0.0,
202
  "directional_loss_mean": 0.0,
203
  "total_loss_mean": 0.0,
204
  "dominant_component": None,
 
208
  "weekly_q": self._component_sums["weekly_q"],
209
  "t1_q": self._component_sums["t1_q"],
210
  "dispersion": self._component_sums["dispersion"],
211
+ "magnitude": self._component_sums["magnitude"],
212
+ "naive": self._component_sums["naive"],
213
  "directional": self._component_sums["directional"],
214
  }
215
  return {
 
217
  "weekly_q_loss_mean": self._component_sums["weekly_q"] / n_batches,
218
  "t1_q_loss_mean": self._component_sums["t1_q"] / n_batches,
219
  "dispersion_loss_mean": self._component_sums["dispersion"] / n_batches,
220
+ "magnitude_loss_mean": self._component_sums["magnitude"] / n_batches,
221
+ "naive_loss_mean": self._component_sums["naive"] / n_batches,
222
  "directional_loss_mean": self._component_sums["directional"] / n_batches,
223
  "total_loss_mean": self._component_sums["total"] / n_batches,
224
  "dominant_component": max(components, key=components.get),
 
242
  y_pred,
243
  median_idx=self.median_idx,
244
  min_gap=1e-5,
245
+ gap_scale=DEFAULT_MONOTONIC_GAP_SCALE,
246
  init_bias=-3.0,
247
  )
248
  if self.debug_mode:
 
275
  pred_abs_med = pred_weekly_median.abs().median() + eps
276
  actual_abs_med = actual_weekly.abs().median() + eps
277
  magnitude_loss = torch.abs(torch.log(pred_abs_med / actual_abs_med))
278
+
279
+ # Naive-zero baseline loss:
280
+ model_mae = torch.mean(torch.abs(pred_weekly_median - actual_weekly))
281
+ zero_mae = torch.mean(torch.abs(actual_weekly)) + eps
282
+ naive_relative_loss = torch.relu((model_mae / zero_mae) - 1.0)
283
 
284
  pred_direction = torch.tanh(median_path * 10.0)
285
  actual_direction = torch.sign(y_actual)
 
293
 
294
  weekly_q_loss = _to_scalar(weekly_q_loss)
295
  t1_q_loss = _to_scalar(t1_q_loss)
296
+ magnitude_loss = _to_scalar(magnitude_loss)
297
+ naive_relative_loss = _to_scalar(naive_relative_loss)
298
  directional_loss = _to_scalar(directional_loss)
299
+
300
  total_loss = (
301
  self.lambda_weekly_quantile * _to_scalar(weekly_q_loss)
302
  + self.lambda_t1_quantile * _to_scalar(t1_q_loss)
303
+ + self.lambda_dispersion * _to_scalar(dispersion_loss)
304
+ + self.lambda_magnitude * _to_scalar(magnitude_loss)
305
+ + self.lambda_naive * _to_scalar(naive_relative_loss)
306
  + self.lambda_directional * _to_scalar(directional_loss)
307
  )
308
+
309
  self._record_components(
310
  weekly_q_loss,
311
  t1_q_loss,
312
+ dispersion_loss,
313
+ magnitude_loss,
314
+ naive_relative_loss,
315
  directional_loss,
316
  total_loss,
317
  )
 
352
  lambda_weekly_quantile=cfg.weekly_loss.lambda_weekly_quantile,
353
  lambda_t1_quantile=cfg.weekly_loss.lambda_t1_quantile,
354
  lambda_dispersion=cfg.weekly_loss.lambda_dispersion,
355
+ lambda_magnitude=cfg.weekly_loss.lambda_magnitude,
356
+ lambda_naive=cfg.weekly_loss.lambda_naive,
357
  lambda_directional=cfg.weekly_loss.lambda_directional,
358
  )
359
  logger.info(
360
+ "Using weekly ASRO loss | weekly_q=%.2f t1_q=%.2f dispersion=%.2f "
361
+ "magnitude=%.2f naive=%.2f dir=%.2f monotonic_transform=true gap_scale=%.3f",
362
  cfg.weekly_loss.lambda_weekly_quantile,
363
  cfg.weekly_loss.lambda_t1_quantile,
364
  cfg.weekly_loss.lambda_dispersion,
365
+ cfg.weekly_loss.lambda_magnitude,
366
+ cfg.weekly_loss.lambda_naive,
367
  cfg.weekly_loss.lambda_directional,
368
+ DEFAULT_MONOTONIC_GAP_SCALE,
369
  )
370
  elif use_asro and ASROPFLoss is not None:
371
  loss = ASROPFLoss(
 
547
  torch.as_tensor(raw_pred, dtype=torch.float64),
548
  median_idx=median_idx,
549
  min_gap=1e-5,
550
+ gap_scale=DEFAULT_MONOTONIC_GAP_SCALE,
551
  init_bias=-3.0,
552
  )
553
  pred = ordered_tensor.detach().cpu().numpy()
 
708
  torch.as_tensor(raw_pred, dtype=torch.float64),
709
  median_idx=median_idx,
710
  min_gap=1e-5,
711
+ gap_scale=DEFAULT_MONOTONIC_GAP_SCALE,
712
  init_bias=-3.0,
713
  )
714
  pred = ordered_tensor.detach().cpu().numpy()
deep_learning/training/callbacks.py CHANGED
@@ -101,11 +101,14 @@ class WeeklyLossComponentLogger(pl.Callback):
101
  epoch = getattr(trainer, "current_epoch", 0)
102
  logger.info(
103
  "Weekly loss components | epoch=%s weekly_q=%.6f t1_q=%.6f "
104
- "dispersion=%.6f directional=%.6f total=%.6f dominant=%s",
 
105
  epoch,
106
  stats["weekly_q_loss_mean"],
107
  stats["t1_q_loss_mean"],
108
  stats["dispersion_loss_mean"],
 
 
109
  stats["directional_loss_mean"],
110
  stats["total_loss_mean"],
111
  stats["dominant_component"],
@@ -115,7 +118,11 @@ class WeeklyLossComponentLogger(pl.Callback):
115
  "Weekly dispersion loss is dominating weekly quantile loss; "
116
  "lambda_dispersion may need to be reduced."
117
  )
118
- if stats["directional_loss_mean"] < 0.05 * max(stats["total_loss_mean"], 1e-12):
 
 
 
 
119
  logger.warning(
120
  "Weekly directional loss is below 5%% of total loss; "
121
  "lambda_directional may need to increase."
 
101
  epoch = getattr(trainer, "current_epoch", 0)
102
  logger.info(
103
  "Weekly loss components | epoch=%s weekly_q=%.6f t1_q=%.6f "
104
+ "dispersion=%.6f magnitude=%.6f naive=%.6f directional=%.6f "
105
+ "total=%.6f dominant=%s",
106
  epoch,
107
  stats["weekly_q_loss_mean"],
108
  stats["t1_q_loss_mean"],
109
  stats["dispersion_loss_mean"],
110
+ stats.get("magnitude_loss_mean", 0.0),
111
+ stats.get("naive_loss_mean", 0.0),
112
  stats["directional_loss_mean"],
113
  stats["total_loss_mean"],
114
  stats["dominant_component"],
 
118
  "Weekly dispersion loss is dominating weekly quantile loss; "
119
  "lambda_dispersion may need to be reduced."
120
  )
121
+ lambda_directional = float(getattr(loss, "lambda_directional", 0.0))
122
+ directional_is_tiny = (
123
+ stats["directional_loss_mean"] < 0.05 * max(stats["total_loss_mean"], 1e-12)
124
+ )
125
+ if lambda_directional > 0.0 and directional_is_tiny:
126
  logger.warning(
127
  "Weekly directional loss is below 5%% of total loss; "
128
  "lambda_directional may need to increase."
deep_learning/training/hyperopt.py CHANGED
@@ -66,10 +66,12 @@ KNOWN_GOOD_TRIAL_PARAMS = {
66
  "lambda_vol": 0.30,
67
  "lambda_quantile": 0.25,
68
  "lambda_madl": 0.40,
69
- "lambda_weekly_quantile": 0.55,
70
- "lambda_t1_quantile": 0.15,
71
- "lambda_dispersion": 0.20,
72
- "lambda_directional": 0.10,
 
 
73
  "batch_size": 32,
74
  }
75
 
@@ -287,10 +289,12 @@ def create_trial_config(trial, base_cfg: TFTASROConfig) -> TFTASROConfig:
287
  )
288
 
289
  weekly_loss_cfg = WeeklyLossConfig(
290
- lambda_weekly_quantile=trial.suggest_float("lambda_weekly_quantile", 0.45, 0.65, step=0.05),
291
- lambda_t1_quantile=trial.suggest_float("lambda_t1_quantile", 0.05, 0.20, step=0.05),
292
- lambda_dispersion=trial.suggest_float("lambda_dispersion", 0.15, 0.35, step=0.05),
293
- lambda_directional=trial.suggest_float("lambda_directional", 0.05, 0.12, step=0.01),
 
 
294
  )
295
 
296
  training_cfg = TrainingConfig(
 
66
  "lambda_vol": 0.30,
67
  "lambda_quantile": 0.25,
68
  "lambda_madl": 0.40,
69
+ "lambda_weekly_quantile": 0.70,
70
+ "lambda_t1_quantile": 0.20,
71
+ "lambda_dispersion": 0.35,
72
+ "lambda_magnitude": 0.50,
73
+ "lambda_naive": 0.50,
74
+ "lambda_directional": 0.00,
75
  "batch_size": 32,
76
  }
77
 
 
289
  )
290
 
291
  weekly_loss_cfg = WeeklyLossConfig(
292
+ lambda_weekly_quantile=trial.suggest_float("lambda_weekly_quantile", 0.60, 0.80, step=0.05),
293
+ lambda_t1_quantile=trial.suggest_float("lambda_t1_quantile", 0.10, 0.25, step=0.05),
294
+ lambda_dispersion=trial.suggest_float("lambda_dispersion", 0.25, 0.45, step=0.05),
295
+ lambda_magnitude=trial.suggest_float("lambda_magnitude", 0.25, 0.75, step=0.05),
296
+ lambda_naive=trial.suggest_float("lambda_naive", 0.25, 0.75, step=0.05),
297
+ lambda_directional=trial.suggest_float("lambda_directional", 0.00, 0.08, step=0.02),
298
  )
299
 
300
  training_cfg = TrainingConfig(
deep_learning/training/metrics.py CHANGED
@@ -15,7 +15,10 @@ import numpy as np
15
  import pandas as pd
16
  import torch
17
 
18
- from deep_learning.models.monotonic_quantiles import enforce_monotonic_quantiles
 
 
 
19
 
20
 
21
  def select_prediction_horizon(values: np.ndarray, horizon_idx: int = 0) -> np.ndarray:
@@ -71,7 +74,7 @@ def monotonic_quantiles_np(
71
  tensor,
72
  median_idx=median_idx,
73
  min_gap=1e-5,
74
- gap_scale=0.01,
75
  init_bias=-3.0,
76
  )
77
  return ordered.detach().cpu().numpy()
 
15
  import pandas as pd
16
  import torch
17
 
18
+ from deep_learning.models.monotonic_quantiles import (
19
+ DEFAULT_MONOTONIC_GAP_SCALE,
20
+ enforce_monotonic_quantiles,
21
+ )
22
 
23
 
24
  def select_prediction_horizon(values: np.ndarray, horizon_idx: int = 0) -> np.ndarray:
 
74
  tensor,
75
  median_idx=median_idx,
76
  min_gap=1e-5,
77
+ gap_scale=DEFAULT_MONOTONIC_GAP_SCALE,
78
  init_bias=-3.0,
79
  )
80
  return ordered.detach().cpu().numpy()
deep_learning/training/trainer.py CHANGED
@@ -57,10 +57,12 @@ KNOWN_GOOD_CONFIG = {
57
  "lambda_vol": 0.30,
58
  "lambda_quantile": 0.25,
59
  "lambda_madl": 0.40,
60
- "lambda_weekly_quantile": 0.55,
61
- "lambda_t1_quantile": 0.15,
62
- "lambda_dispersion": 0.20,
63
- "lambda_directional": 0.10,
 
 
64
  "batch_size": 32,
65
  }
66
 
@@ -278,10 +280,13 @@ def train_tft_model(
278
  cfg.training.early_stopping_patience,
279
  )
280
  logger.info(
281
- "Weekly loss | weekly_q=%.2f t1_q=%.2f dispersion=%.2f directional=%.2f monotonic_transform=true",
 
282
  cfg.weekly_loss.lambda_weekly_quantile,
283
  cfg.weekly_loss.lambda_t1_quantile,
284
  cfg.weekly_loss.lambda_dispersion,
 
 
285
  cfg.weekly_loss.lambda_directional,
286
  )
287
  else:
@@ -440,8 +445,10 @@ def train_tft_model(
440
  "lambda_crossing": cfg.asro.lambda_crossing,
441
  "lambda_weekly_quantile": cfg.weekly_loss.lambda_weekly_quantile,
442
  "lambda_t1_quantile": cfg.weekly_loss.lambda_t1_quantile,
443
- "lambda_directional": cfg.weekly_loss.lambda_directional,
444
  "lambda_dispersion": cfg.weekly_loss.lambda_dispersion,
 
 
 
445
  "monotonic_quantile_transform": True,
446
  "max_encoder_length": cfg.model.max_encoder_length,
447
  "max_prediction_length": cfg.model.max_prediction_length,
@@ -657,6 +664,10 @@ def _apply_optuna_results(cfg: TFTASROConfig) -> TFTASROConfig:
657
  params["lambda_directional"] = min(float(params["lambda_directional"]), 0.12)
658
  if "lambda_dispersion" in params:
659
  params["lambda_dispersion"] = max(float(params["lambda_dispersion"]), 0.20)
 
 
 
 
660
 
661
  logger.info(
662
  "Loaded Optuna best params (trial #%d, weekly_objective=%.4f): %s",
@@ -692,7 +703,7 @@ def _overlay_training_config(cfg: TFTASROConfig, params: dict) -> TFTASROConfig:
692
  weekly_loss_overrides = {
693
  k: params[k] for k in (
694
  "lambda_weekly_quantile", "lambda_t1_quantile", "lambda_directional",
695
- "lambda_dispersion",
696
  ) if k in params
697
  }
698
 
 
57
  "lambda_vol": 0.30,
58
  "lambda_quantile": 0.25,
59
  "lambda_madl": 0.40,
60
+ "lambda_weekly_quantile": 0.70,
61
+ "lambda_t1_quantile": 0.20,
62
+ "lambda_dispersion": 0.35,
63
+ "lambda_magnitude": 0.50,
64
+ "lambda_naive": 0.50,
65
+ "lambda_directional": 0.00,
66
  "batch_size": 32,
67
  }
68
 
 
280
  cfg.training.early_stopping_patience,
281
  )
282
  logger.info(
283
+ "Weekly loss | weekly_q=%.2f t1_q=%.2f dispersion=%.2f "
284
+ "magnitude=%.2f naive=%.2f directional=%.2f monotonic_transform=true",
285
  cfg.weekly_loss.lambda_weekly_quantile,
286
  cfg.weekly_loss.lambda_t1_quantile,
287
  cfg.weekly_loss.lambda_dispersion,
288
+ cfg.weekly_loss.lambda_magnitude,
289
+ cfg.weekly_loss.lambda_naive,
290
  cfg.weekly_loss.lambda_directional,
291
  )
292
  else:
 
445
  "lambda_crossing": cfg.asro.lambda_crossing,
446
  "lambda_weekly_quantile": cfg.weekly_loss.lambda_weekly_quantile,
447
  "lambda_t1_quantile": cfg.weekly_loss.lambda_t1_quantile,
 
448
  "lambda_dispersion": cfg.weekly_loss.lambda_dispersion,
449
+ "lambda_magnitude": cfg.weekly_loss.lambda_magnitude,
450
+ "lambda_naive": cfg.weekly_loss.lambda_naive,
451
+ "lambda_directional": cfg.weekly_loss.lambda_directional,
452
  "monotonic_quantile_transform": True,
453
  "max_encoder_length": cfg.model.max_encoder_length,
454
  "max_prediction_length": cfg.model.max_prediction_length,
 
664
  params["lambda_directional"] = min(float(params["lambda_directional"]), 0.12)
665
  if "lambda_dispersion" in params:
666
  params["lambda_dispersion"] = max(float(params["lambda_dispersion"]), 0.20)
667
+ if "lambda_magnitude" in params:
668
+ params["lambda_magnitude"] = min(max(float(params["lambda_magnitude"]), 0.0), 0.80)
669
+ if "lambda_naive" in params:
670
+ params["lambda_naive"] = min(max(float(params["lambda_naive"]), 0.0), 0.80)
671
 
672
  logger.info(
673
  "Loaded Optuna best params (trial #%d, weekly_objective=%.4f): %s",
 
703
  weekly_loss_overrides = {
704
  k: params[k] for k in (
705
  "lambda_weekly_quantile", "lambda_t1_quantile", "lambda_directional",
706
+ "lambda_dispersion", "lambda_magnitude", "lambda_naive",
707
  ) if k in params
708
  }
709