Views
No views yet
| Metric | Value |
|---|---|
| crps_total | 0.927381 |
| Total experiments | 404 |
| Successful | 384 (95%) |
| Max generation | 32 |
| # | Name | Metric | Gen |
|---|---|---|---|
| 273 | exp_per_asset_model_specialization_xau_simplification | 0.927381 | 28 |
| 277 | exp_final_production_submission_absolute_closure | 0.927381 | 29 |
| 283 | exp_threshold_optimization_p70_p80_test | 0.927381 | 29 |
| 295 | exp_8859 | 0.927381 | 29 |
| 296 | exp_final_production_deployment | 0.927381 | 29 |
1# Experiment: exp_per_asset_model_specialization_xau_simplification
2"""
3PER-ASSET MODEL SPECIALIZATION: XAU Simplification Test
4
5The current best model (metric=0.928795) uses a UNIFIED 2-regime AR(1) + hybrid
6jump specification for ALL assets. However, XAU (gold) has fundamentally different
7microstructure than crypto assets:
8- Lighter tails (near-Gaussian vs heavy-tailed crypto)
9- Lower volatility (~0.0002 vs ~0.0004-0.0005 for crypto)
10- Different trading dynamics (traditional asset vs 24/7 crypto)
11
12This experiment tests per-asset model specialization:
13- XAU: Pure 2-regime AR(1) WITHOUT jumps (simpler, less estimation noise)
14- BTC/ETH/SOL: Full 2-regime AR(1) + hybrid jumps (captures heavy tails)
15
16RATIONALE:
17- XAU's jump parameters are estimated from sparse events (98.5% threshold)
18- With λ≈0.005, we get ~30 jumps per 30-day window - high estimation variance
19- Gold's price dynamics may not require explicit jump modeling
20- Simpler model for XAU reduces overfitting while preserving key AR(1) structure
21
22HYPOTHESIS: XAU without jumps will perform equivalently or better because
23the jump component adds estimation noise without predictive benefit for
24near-Gaussian gold returns. Crypto assets retain jumps for tail coverage.
25"""
26
27import math
28import time
29import sys
30
31import numpy as np
32
33from prepare import (
34 load_prepared_data,
35 get_available_features,
36 print_single_challenge_scores,
37 gbm_paths,
38 run_walk_forward_eval,
39 print_walk_forward_summary,
40 ASSETS_HFT,
41 NUM_SIMULATIONS,
42 FORECAST_STEPS_HFT,
43 TIME_INCREMENT_HFT,
44 TIME_BUDGET,
45 CRPS_INTERVALS_HFT,
46 N_WALK_FORWARD_SEGMENTS,
47 MIN_EVAL_SEGMENTS,
48 N_SEEDS_PER_SEGMENT,
49)
50
51# ── Configuration ────────────────────────────────────────────────────────
52
53LOOKBACK_DAYS_HFT = 30
54TRAIN_FRACTION = 0.85
55INPUT_LEN_HFT = 60
56HORIZON_STEPS_HFT = [1, 2, 5, 15, 30, 60]
57TIME_SPLIT_HFT = 0.9
58
59# Universal threshold for regime classification
60REGIME_THRESHOLD_PCT = 75
61
62# Per-asset RV window calibration
63PER_ASSET_RV_WINDOW = {
64 'BTC': 5,
65 'ETH': 5,
66 'XAU': 3,
67 'SOL': 10,
68}
69
70# Universal Huber c
71UNIVERSAL_HUBER_C = 1.345
72
73# 3-TIER JUMP THRESHOLD CALIBRATION (crypto assets only)
74PER_ASSET_JUMP_PERCENTILE = {
75 'BTC': 99.0,
76 'ETH': 99.0,
77 'XAU': 98.5, # Not used - XAU has no jumps
78 'SOL': 99.5,
79}
80
81# Minimum jumps threshold per asset
82PER_ASSET_MIN_JUMPS = {
83 'BTC': 5,
84 'ETH': 5,
85 'XAU': 3,
86 'SOL': 7,
87}
88
89# Universal Poisson jump intensity
90UNIVERSAL_LAMBDA = 0.01
91
92# Annualization factor for 1-minute data
93ANNUALIZATION_FACTOR = 525960
94
95# HYBRID TAIL PARAMETERS (crypto assets only)
96PARETO_ALPHA_DOWN = 1.3
97UNIVERSAL_GAUSSIAN_SCALE_UP = 0.0010
98UNIVERSAL_P_UP = 0.5
99UNIVERSAL_PHI = -0.05
100
101# Model specialization flags
102ASSET_MODEL_TYPE = {
103 'BTC': 'full', # 2-regime AR(1) + hybrid jumps
104 'ETH': 'full', # 2-regime AR(1) + hybrid jumps
105 'XAU': 'no_jumps', # 2-regime AR(1) only (no jumps)
106 'SOL': 'full', # 2-regime AR(1) + hybrid jumps
107}
108
109# Bounds for numerical stability
110MIN_PARETO_ALPHA = 1.1
111MAX_PARETO_ALPHA = 5.0
112
113
114# ── Core Model Functions ─────────────────────────────────────────────────
115
116def fit_robust_ar1_for_sigma_only(returns, huber_c=1.345, max_iter=50, tol=1e-6):
117 """
118 Fit AR(1) using Huber M-estimator, but only return sigma (not phi).
119 Phi will be set universally.
120 """
121 if len(returns) < 10:
122 return np.std(returns) if len(returns) > 1 else 0.001
123
124 phi = UNIVERSAL_PHI
125
126 r_t = returns[1:]
127 r_tminus1 = returns[:-1]
128
129 valid = np.isfinite(r_t) & np.isfinite(r_tminus1)
130 if not np.any(valid):
131 return np.std(returns) if len(returns) > 1 else 0.001
132
133 r_t = r_t[valid]
134 r_tminus1 = r_tminus1[valid]
135 n = len(r_t)
136
137 if n < 5:
138 return np.std(returns) if len(returns) > 1 else 0.001
139
140 residuals = r_t - phi * r_tminus1
141
142 c = huber_c
143 for _ in range(max_iter):
144 mad = np.median(np.abs(residuals - np.median(residuals)))
145 sigma_scale = mad / 0.6745 if mad > 1e-12 else 1.0
146
147 standardized = residuals / max(sigma_scale, 1e-12)
148 abs_r = np.abs(standardized)
149 weights = np.ones_like(residuals)
150 mask = abs_r > c
151 weights[mask] = c / abs_r[mask]
152
153 weighted_residuals = weights * residuals
154 residuals = r_t - phi * r_tminus1
155
156 mad_final = np.median(np.abs(residuals - np.median(residuals)))
157 sigma = mad_final / 0.6745
158
159 if sigma < 1e-8 or not np.isfinite(sigma):
160 sigma = np.std(residuals)
161
162 return sigma
163
164
165def compute_realized_volatility(returns, window):
166 """
167 Compute realized volatility using simple close-to-close returns.
168 """
169 n = len(returns)
170 if n < window:
171 return np.full(n, np.std(returns) * np.sqrt(ANNUALIZATION_FACTOR) if n > 1 else 0.001)
172
173 rv_history = np.zeros(n)
174 for i in range(n):
175 start_idx = max(0, i - window)
176 window_returns = returns[start_idx:i+1]
177 if len(window_returns) > 1:
178 rv_history[i] = np.std(window_returns) * np.sqrt(ANNUALIZATION_FACTOR)
179 else:
180 rv_history[i] = rv_history[i-1] if i > 0 else 0.001
181
182 return rv_history
183
184
185def estimate_jump_parameters_universal(returns, asset):
186 """
187 Estimate jump parameters with universal directional probability.
188 For XAU, returns zero jumps (model specialization).
189 """
190 model_type = ASSET_MODEL_TYPE.get(asset, 'full')
191
192 # XAU uses no-jump model
193 if model_type == 'no_jumps':
194 return 0.0, UNIVERSAL_P_UP, UNIVERSAL_GAUSSIAN_SCALE_UP, 0.001, 0.001
195
196 if len(returns) < 100:
197 return 0.0, UNIVERSAL_P_UP, UNIVERSAL_GAUSSIAN_SCALE_UP, 0.001, 0.001
198
199 jump_percentile = PER_ASSET_JUMP_PERCENTILE.get(asset, 99.0)
200 min_jumps = PER_ASSET_MIN_JUMPS.get(asset, 5)
201
202 abs_returns = np.abs(returns)
203 threshold = np.percentile(abs_returns, jump_percentile)
204
205 jump_mask = abs_returns > threshold
206 n_jumps = np.sum(jump_mask)
207
208 if n_jumps < min_jumps:
209 return 0.0, UNIVERSAL_P_UP, UNIVERSAL_GAUSSIAN_SCALE_UP, PARETO_ALPHA_DOWN, threshold
210
211 lambda_poisson = UNIVERSAL_LAMBDA
212 p_up = UNIVERSAL_P_UP
213 gaussian_sigma_up = UNIVERSAL_GAUSSIAN_SCALE_UP
214 pareto_scale_down = threshold
215
216 return lambda_poisson, p_up, gaussian_sigma_up, pareto_scale_down, threshold
217
218
219def fit_model(returns, asset):
220 """
221 Fit 2-regime AR(1) with per-asset model specialization.
222 """
223 rv_window = PER_ASSET_RV_WINDOW.get(asset, 5)
224 model_type = ASSET_MODEL_TYPE.get(asset, 'full')
225
226 if len(returns) < 100:
227 sigma = fit_robust_ar1_for_sigma_only(returns, huber_c=UNIVERSAL_HUBER_C)
228 threshold = np.percentile(np.abs(returns), 99.0) if len(returns) > 10 else 0.001
229 return {
230 'phi': UNIVERSAL_PHI,
231 'sigma_calm': sigma,
232 'sigma_volatile': sigma,
233 'vol_threshold': np.inf,
234 'regime': 'calm',
235 'use_regime': False,
236 'lambda_poisson': 0.0,
237 'p_up': UNIVERSAL_P_UP,
238 'gaussian_sigma_up': UNIVERSAL_GAUSSIAN_SCALE_UP,
239 'pareto_scale_down': threshold,
240 'jump_threshold': threshold,
241 'rv_window': rv_window,
242 'model_type': model_type,
243 'jump_percentile': PER_ASSET_JUMP_PERCENTILE.get(asset, 99.0),
244 }
245
246 phi = UNIVERSAL_PHI
247 sigma_overall = fit_robust_ar1_for_sigma_only(returns, huber_c=UNIVERSAL_HUBER_C)
248
249 # Estimate jump parameters (zero for XAU)
250 lambda_poisson, p_up, gaussian_sigma_up, pareto_scale_down, jump_threshold = estimate_jump_parameters_universal(returns, asset)
251
252 # Compute RV history for regime classification
253 rv_history = compute_realized_volatility(returns, rv_window)
254
255 valid_rv = rv_history[np.isfinite(rv_history)]
256 if len(valid_rv) == 0:
257 valid_rv = np.array([sigma_overall])
258
259 vol_threshold = np.percentile(valid_rv, REGIME_THRESHOLD_PCT)
260
261 calm_mask = rv_history < vol_threshold
262 volatile_mask = ~calm_mask
263
264 # Regime-specific sigma estimation using universal phi
265 returns_lag = returns[:-1]
266 returns_curr = returns[1:]
267
268 if np.sum(calm_mask[:-1]) > 10:
269 calm_idx = np.where(calm_mask[:-1])[0]
270 residuals_calm = returns_curr[calm_idx] - phi * returns_lag[calm_idx]
271 mad_calm = np.median(np.abs(residuals_calm - np.median(residuals_calm)))
272 sigma_calm = mad_calm / 0.6745
273 else:
274 sigma_calm = sigma_overall
275
276 if np.sum(volatile_mask[:-1]) > 10:
277 volatile_idx = np.where(volatile_mask[:-1])[0]
278 residuals_volatile = returns_curr[volatile_idx] - phi * returns_lag[volatile_idx]
279 mad_volatile = np.median(np.abs(residuals_volatile - np.median(residuals_volatile)))
280 sigma_volatile = mad_volatile / 0.6745
281 else:
282 sigma_volatile = sigma_overall * 1.5
283
284 if sigma_volatile <= sigma_calm:
285 sigma_volatile = sigma_calm * 1.3
286
287 current_rv = rv_history[-1] if len(rv_history) > 0 and np.isfinite(rv_history[-1]) else sigma_overall
288 current_regime = 'volatile' if current_rv > vol_threshold else 'calm'
289
290 return {
291 'phi': phi,
292 'sigma_calm': sigma_calm,
293 'sigma_volatile': sigma_volatile,
294 'vol_threshold': vol_threshold,
295 'regime': current_regime,
296 'use_regime': True,
297 'lambda_poisson': lambda_poisson,
298 'p_up': p_up,
299 'gaussian_sigma_up': gaussian_sigma_up,
300 'pareto_scale_down': pareto_scale_down,
301 'jump_threshold': jump_threshold,
302 'rv_window': rv_window,
303 'model_type': model_type,
304 'jump_percentile': PER_ASSET_JUMP_PERCENTILE.get(asset, 99.0),
305 }
306
307
308def train_model(data_hft, assets):
309 """Train 2-regime AR(1) with per-asset model specialization."""
310 print("=" * 60)
311 print("PER-ASSET MODEL SPECIALIZATION: XAU Simplification Test")
312 print("=" * 60)
313 print("Testing different model families per asset:")
314 for asset in assets:
315 model_type = ASSET_MODEL_TYPE.get(asset, 'full')
316 if model_type == 'full':
317 print(f" {asset}: 2-regime AR(1) + hybrid jumps")
318 else:
319 print(f" {asset}: 2-regime AR(1) NO JUMPS (simplified)")
320 print("-" * 60)
321 print("Universal parameters:")
322 print(f" phi={UNIVERSAL_PHI:.4f}, p_up={UNIVERSAL_P_UP:.2f}, scale={UNIVERSAL_GAUSSIAN_SCALE_UP:.4f}")
323 print("-" * 60)
324
325 model_params = {}
326
327 for asset in assets:
328 if asset not in data_hft:
329 continue
330
331 df = data_hft[asset]
332 prices = df['close'].values
333 log_prices = np.log(prices)
334 returns = np.diff(log_prices)
335 returns = returns[np.isfinite(returns)]
336
337 if len(returns) < 10:
338 threshold = 0.001
339 model_type = ASSET_MODEL_TYPE.get(asset, 'full')
340 model_params[asset] = {
341 'phi': UNIVERSAL_PHI, 'sigma_calm': 0.001, 'sigma_volatile': 0.001,
342 'vol_threshold': np.inf, 'regime': 'calm', 'use_regime': False,
343 'lambda_poisson': 0.0, 'p_up': UNIVERSAL_P_UP,
344 'gaussian_sigma_up': UNIVERSAL_GAUSSIAN_SCALE_UP,
345 'pareto_scale_down': threshold,
346 'jump_threshold': threshold, 'rv_window': PER_ASSET_RV_WINDOW.get(asset, 5),
347 'model_type': model_type,
348 'jump_percentile': PER_ASSET_JUMP_PERCENTILE.get(asset, 99.0),
349 }
350 continue
351
352 params = fit_model(returns, asset)
353 params['last_return'] = returns[-1] if len(returns) > 0 else 0.0
354 model_params[asset] = params
355
356 reg_str = f"[{params['regime'].upper()}]"
357 model_type = params['model_type']
358 if model_type == 'full':
359 jump_str = f" λ={params['lambda_poisson']:.4f}"
360 else:
361 jump_str = " NO-JUMPS"
362 print(f" {asset}: phi={params['phi']:.4f}, "
363 f"σ_calm={params['sigma_calm']:.6f}, σ_vol={params['sigma_volatile']:.6f}, "
364 f"p↑={params['p_up']:.2f}{jump_str} {reg_str}")
365
366 return {'model_params': model_params}
367
368
369def generate_pareto_jumps(num_samples, alpha, scale):
370 """
371 Generate Pareto-distributed random variables.
372 """
373 u = np.random.random(num_samples)
374 u = np.clip(u, 1e-10, 1.0)
375 jumps = scale * (u ** (-1.0 / alpha))
376 max_jump = scale * 100
377 jumps = np.clip(jumps, scale, max_jump)
378 return jumps
379
380
381def generate_gaussian_jumps(num_samples, sigma):
382 """
383 Generate Gaussian-distributed random variables (truncated to positive).
384 """
385 jumps = np.random.normal(0.0, sigma, num_samples)
386 jumps = np.maximum(jumps, 0.001)
387 max_jump = sigma * 10
388 jumps = np.clip(jumps, 0.001, max_jump)
389 return jumps
390
391
392def generate_paths(
393 current_price: float,
394 historical_prices: np.ndarray,
395 forecast_steps: int,
396 time_increment: int,
397 num_simulations: int,
398 phi: float,
399 sigma_calm: float,
400 sigma_volatile: float,
401 vol_threshold: float,
402 current_regime: str,
403 use_regime: bool,
404 lambda_poisson: float,
405 p_up: float,
406 gaussian_sigma_up: float,
407 pareto_scale_down: float,
408 jump_threshold: float,
409 rv_window: int = 5,
410 model_type: str = 'full',
411):
412 """
413 Generate price paths using 2-regime AR(1) with per-asset specialization.
414 """
415 if not use_regime:
416 sigma_eff = sigma_calm
417 else:
418 log_prices = np.log(historical_prices)
419 returns = np.diff(log_prices)
420 recent_returns = returns[-rv_window:] if len(returns) >= rv_window else returns
421
422 current_rv = np.std(recent_returns) * np.sqrt(ANNUALIZATION_FACTOR) if len(recent_returns) > 1 else sigma_calm
423 sigma_eff = sigma_volatile if current_rv > vol_threshold else sigma_calm
424
425 sigma_eff = np.clip(sigma_eff, 1e-6, 0.5)
426
427 current_log_price = np.log(current_price)
428 log_paths = np.zeros((num_simulations, forecast_steps))
429 log_paths[:, 0] = current_log_price
430
431 if len(historical_prices) >= 2:
432 last_return = np.log(historical_prices[-1]) - np.log(historical_prices[-2])
433 else:
434 last_return = 0.0
435
436 current_returns = np.full(num_simulations, last_return)
437
438 eps_normal = np.random.normal(0.0, 1.0, (num_simulations, forecast_steps))
439
440 # Jump arrivals - only for 'full' model type
441 if model_type == 'full' and lambda_poisson > 0:
442 jump_prob = 1.0 - np.exp(-lambda_poisson)
443 jump_occurs = np.random.random((num_simulations, forecast_steps)) < jump_prob
444 else:
445 jump_occurs = np.zeros((num_simulations, forecast_steps), dtype=bool)
446
447 for t in range(1, forecast_steps):
448 continuous_innov = phi * current_returns + sigma_eff * eps_normal[:, t]
449
450 jump_innov = np.zeros(num_simulations)
451 jumping_paths = jump_occurs[:, t]
452 n_jumping = np.sum(jumping_paths)
453
454 if n_jumping > 0:
455 up_mask = np.random.random(n_jumping) < p_up
456 n_up = np.sum(up_mask)
457 n_down = n_jumping - n_up
458
459 up_jumps = generate_gaussian_jumps(n_up, gaussian_sigma_up)
460 down_jumps = -generate_pareto_jumps(n_down, PARETO_ALPHA_DOWN, pareto_scale_down)
461
462 jump_values = np.concatenate([up_jumps, down_jumps])
463 jump_innov[jumping_paths] = jump_values
464
465 new_return = continuous_innov + jump_innov
466 log_paths[:, t] = log_paths[:, t-1] + new_return
467 current_returns = new_return
468
469 paths = np.exp(log_paths)
470 paths[:, 0] = current_price
471
472 return paths
473
474
475def generate_predictions(
476 current_price: float,
477 historical_prices: np.ndarray,
478 forecast_steps: int,
479 time_increment: int,
480 num_simulations: int = 1000,
481 model=None,
482 features: np.ndarray = None,
483 horizon_steps=None,
484) -> np.ndarray:
485 """
486 Generate predictions using per-asset model specialization.
487 """
488 if model is None:
489 return gbm_paths(
490 current_price=current_price,
491 historical_prices=historical_prices,
492 num_steps=forecast_steps,
493 num_simulations=num_simulations,
494 time_increment=time_increment,
495 )
496
497 model_params = model.get('model_params', {})
498 asset_params = model_params.get(model.get('current_asset', ''), {})
499
500 return generate_paths(
501 current_price=current_price,
502 historical_prices=historical_prices,
503 forecast_steps=forecast_steps,
504 time_increment=time_increment,
505 num_simulations=num_simulations,
506 phi=asset_params.get('phi', UNIVERSAL_PHI),
507 sigma_calm=asset_params.get('sigma_calm', 0.001),
508 sigma_volatile=asset_params.get('sigma_volatile', 0.001),
509 vol_threshold=asset_params.get('vol_threshold', np.inf),
510 current_regime=asset_params.get('regime', 'calm'),
511 use_regime=asset_params.get('use_regime', False),
512 lambda_poisson=asset_params.get('lambda_poisson', 0.0),
513 p_up=asset_params.get('p_up', UNIVERSAL_P_UP),
514 gaussian_sigma_up=asset_params.get('gaussian_sigma_up', UNIVERSAL_GAUSSIAN_SCALE_UP),
515 pareto_scale_down=asset_params.get('pareto_scale_down', 0.001),
516 jump_threshold=asset_params.get('jump_threshold', 0.001),
517 rv_window=asset_params.get('rv_window', 5),
518 model_type=asset_params.get('model_type', 'full'),
519 )
520
521
522# ── Main ─────────────────────────────────────────────────────────────────
523
524def main():
525 start_time = time.time()
526 peak_vram = 0.0
527
528 print("=" * 60)
529 print("SYNTH 1H HIGH FREQUENCY - Per-Asset Model Specialization")
530 print("=" * 60, flush=True)
531 print("Testing XAU simplification (no jumps) vs crypto full model")
532 print(" XAU: 2-regime AR(1) without jumps (simplified)")
533 print(" BTC/ETH/SOL: 2-regime AR(1) + hybrid jumps (full)")
534 print(f" Universal: phi={UNIVERSAL_PHI:.4f}, p_up={UNIVERSAL_P_UP:.2f}")
535 print("-" * 60, flush=True)
536
537 try:
538 data_hft = load_prepared_data(
539 lookback_days=LOOKBACK_DAYS_HFT, assets=ASSETS_HFT, interval="1m",
540 )
541 except RuntimeError as e:
542 print(f"FATAL: {e}", file=sys.stderr, flush=True)
543 print(f"data_error: {e}")
544 print("crps_total: 999999.0")
545 print(f"training_seconds: {time.time() - start_time:.1f}")
546 print("peak_vram_mb: 0.0")
547 sys.exit(1)
548
549 trained_model = train_model(data_hft, ASSETS_HFT)
550
551 predictions_hft = {}
552 actuals_hft = {}
553 per_asset_crps_hft = {}
554 per_asset_se_hft = {}
555 per_asset_segments = {}
556 wf_gbm_hft = {}
557
558 budget_hft = TIME_BUDGET * TIME_SPLIT_HFT
559
560 for asset in ASSETS_HFT:
561 if asset not in data_hft:
562 print(f" Skipping {asset} HFT (no data)", flush=True)
563 continue
564
565 if time.time() - start_time > budget_hft:
566 print(f" Time budget exhausted, skipping remaining assets", flush=True)
567 break
568
569 df = data_hft[asset]
570 feature_cols = get_available_features(df)
571
572 model = {
573 'model_params': trained_model['model_params'],
574 'current_asset': asset,
575 }
576
577 result = run_walk_forward_eval(
578 asset=asset,
579 df=df,
580 feature_cols=feature_cols,
581 generate_predictions_fn=generate_predictions,
582 input_len=INPUT_LEN_HFT,
583 horizon_steps=HORIZON_STEPS_HFT,
584 forecast_steps=FORECAST_STEPS_HFT,
585 time_increment=TIME_INCREMENT_HFT,
586 intervals=CRPS_INTERVALS_HFT,
587 model=model,
588 )
589
590 if result is not None:
591 current_price, paths, actual_prices, scores, gbm_scores, n_segs, se = result
592 predictions_hft[asset] = (current_price, paths)
593 actuals_hft[asset] = actual_prices
594 per_asset_crps_hft[asset] = scores
595 per_asset_se_hft[asset] = se
596 per_asset_segments[asset] = n_segs
597 wf_gbm_hft[asset] = gbm_scores
598 total_crps = sum(scores.values())
599 total_se = math.sqrt(sum(v * v for v in se.values()))
600 warn = " [INSUFFICIENT]" if n_segs < MIN_EVAL_SEGMENTS else ""
601 print(
602 f" {asset}: CRPS={total_crps:.4f} ± {total_se:.4f} SE "
603 f"({n_segs} segments × {N_SEEDS_PER_SEGMENT} seeds){warn}",
604 flush=True,
605 )
606
607 elapsed = time.time() - start_time
608
609 print_single_challenge_scores(
610 challenge="hft",
611 per_asset_crps=per_asset_crps_hft,
612 predictions=predictions_hft,
613 actuals=actuals_hft,
614 data=data_hft,
615 elapsed=elapsed,
616 peak_vram=peak_vram,
617 train_fraction=TRAIN_FRACTION,
618 input_len=INPUT_LEN_HFT,
619 max_eval_points=N_WALK_FORWARD_SEGMENTS,
620 )
621
622 hft_weights = {a: 1.0 for a in ASSETS_HFT}
623
624 print()
625 print_walk_forward_summary(
626 label="hft",
627 per_asset_scores=per_asset_crps_hft,
628 per_asset_gbm=wf_gbm_hft,
629 per_asset_se=per_asset_se_hft,
630 per_asset_segments=per_asset_segments,
631 expected_assets=ASSETS_HFT,
632 weights=hft_weights,
633 )
634
635
636if __name__ == "__main__":
637 main()1# Install dependencies
2pip install torch numpy pandas scipy huggingface_hub
3
4# Run the best model
5python train.pytrain.py — The evolved training script (best experiment)prepare.py — Frozen evaluation harness (data loading + CRPS scoring)task.yaml — Task configuration for evoloopreport.json — Full experiment report with metricsexperiments.jsonl — Complete experiment history