-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_backtest.py
More file actions
129 lines (111 loc) · 4.75 KB
/
Copy pathrun_backtest.py
File metadata and controls
129 lines (111 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""Example: Running a backtest via the API.
Demonstrates how to call the backtesting endpoint to evaluate a model
on historical data using time-series cross-validation.
Prerequisites:
- API server running: uv run uvicorn app.main:app --reload --port 8123
- Database with sales data (run seed_demo_data.py first)
Usage:
python examples/backtest/run_backtest.py
"""
import httpx
API_BASE = "http://localhost:8123"
def main():
# 1. Prepare backtest request
request_payload = {
"store_id": 1,
"product_id": 1,
"start_date": "2024-01-01",
"end_date": "2024-06-30",
"config": {
"split_config": {
"strategy": "expanding",
"n_splits": 5,
"min_train_size": 30,
"gap": 0,
"horizon": 14,
},
"model_config_main": {
"model_type": "naive",
},
"include_baselines": True,
"store_fold_details": True,
},
}
print("=" * 60)
print("BACKTEST REQUEST")
print("=" * 60)
print(f"Store ID: {request_payload['store_id']}")
print(f"Product ID: {request_payload['product_id']}")
print(f"Date Range: {request_payload['start_date']} to {request_payload['end_date']}")
print(f"Strategy: {request_payload['config']['split_config']['strategy']}")
print(f"N Splits: {request_payload['config']['split_config']['n_splits']}")
print(f"Horizon: {request_payload['config']['split_config']['horizon']} days")
print()
# 2. Send request to API
print("Sending request to API...")
with httpx.Client(timeout=30.0) as client:
response = client.post(
f"{API_BASE}/backtesting/run",
json=request_payload,
)
if response.status_code != 200:
print(f"Error: {response.status_code}")
print(response.text)
return
result = response.json()
# 3. Display results
print("\n" + "=" * 60)
print("BACKTEST RESULTS")
print("=" * 60)
print(f"Backtest ID: {result['backtest_id']}")
print(f"Config Hash: {result['config_hash']}")
print(f"Duration: {result['duration_ms']:.1f} ms")
print(f"Leakage Check: {'PASSED' if result['leakage_check_passed'] else 'FAILED'}")
# 4. Main model results
main_results = result["main_model_results"]
print(f"\n--- Main Model: {main_results['model_type']} ---")
print("Aggregated Metrics:")
for metric, value in main_results["aggregated_metrics"].items():
stability = main_results["metric_std"].get(f"{metric}_stability", "N/A")
if isinstance(stability, float):
print(f" {metric}: {value:.4f} (stability: {stability:.2f}%)")
else:
print(f" {metric}: {value:.4f}")
# 5. Per-fold details
if main_results["fold_results"]:
print("\nPer-Fold Results:")
for fold in main_results["fold_results"]:
split = fold["split"]
print(
f" Fold {fold['fold_index']}: "
f"train={split['train_start']} to {split['train_end']} ({split['train_size']} days), "
f"test={split['test_start']} to {split['test_end']} ({split['test_size']} days)"
)
print(f" MAE: {fold['metrics']['mae']:.4f}, sMAPE: {fold['metrics']['smape']:.2f}")
# 6. Baseline comparisons
if result.get("baseline_results"):
print("\n--- Baseline Comparisons ---")
for baseline in result["baseline_results"]:
print(f"\n{baseline['model_type']}:")
for metric, value in baseline["aggregated_metrics"].items():
print(f" {metric}: {value:.4f}")
# 7. Comparison summary
if result.get("comparison_summary"):
print("\n--- Comparison Summary (vs Baselines) ---")
for metric, comparison in result["comparison_summary"].items():
print(f"\n{metric}:")
print(f" Main model: {comparison['main']:.4f}")
if "naive" in comparison:
print(f" Naive: {comparison['naive']:.4f}")
if "vs_naive_pct" in comparison:
imp = comparison["vs_naive_pct"]
direction = "better" if imp > 0 else "worse"
print(f" vs Naive: {abs(imp):.1f}% {direction}")
if "seasonal_naive" in comparison:
print(f" Seasonal Naive: {comparison['seasonal_naive']:.4f}")
if "vs_seasonal_pct" in comparison:
imp = comparison["vs_seasonal_pct"]
direction = "better" if imp > 0 else "worse"
print(f" vs Seasonal: {abs(imp):.1f}% {direction}")
if __name__ == "__main__":
main()