quantbt-engine 1.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantbt/__init__.py +862 -0
- quantbt/adapters/__init__.py +5 -0
- quantbt/adapters/nautilus/__init__.py +42 -0
- quantbt/adapters/nautilus/_dependency.py +53 -0
- quantbt/adapters/nautilus/backend.py +639 -0
- quantbt/adapters/nautilus/instruments.py +275 -0
- quantbt/adapters/nautilus/options.py +465 -0
- quantbt/adapters/nautilus/reports.py +231 -0
- quantbt/backends/__init__.py +29 -0
- quantbt/backends/_native_event_rust.py +2168 -0
- quantbt/backends/native_event.py +5048 -0
- quantbt/backends/native_option.py +796 -0
- quantbt/backends/native_portfolio.py +928 -0
- quantbt/backends/native_vectorized.py +1299 -0
- quantbt/backtester.py +485 -0
- quantbt/benchmarks/__init__.py +1 -0
- quantbt/benchmarks/compare_phase9_parity.py +302 -0
- quantbt/benchmarks/gamma_scalping_backtestsample.py +802 -0
- quantbt/benchmarks/profile_phase7.py +415 -0
- quantbt/benchmarks/run_arbitrage_phase_e.py +160 -0
- quantbt/benchmarks/run_optimization_overhead.py +195 -0
- quantbt/benchmarks/run_options_engine.py +272 -0
- quantbt/benchmarks/run_pct_equity_nautilus_smoke.py +227 -0
- quantbt/benchmarks/run_phase12_arbitrage_cert.py +402 -0
- quantbt/benchmarks/run_phase12_benchmark_nautilus_cert.py +430 -0
- quantbt/benchmarks/run_phase13_portfolio_report.py +97 -0
- quantbt/benchmarks/run_phase13_wfo_cache.py +162 -0
- quantbt/benchmarks/run_phase14_service_loop.py +694 -0
- quantbt/benchmarks/run_phase15a_nautilus_certification.py +514 -0
- quantbt/benchmarks/run_phase15b_synthetic_depth.py +175 -0
- quantbt/benchmarks/run_phase16_performance_debt.py +389 -0
- quantbt/benchmarks/run_phase30e_reactive_runner.py +161 -0
- quantbt/benchmarks/run_phase31_intrabar.py +441 -0
- quantbt/benchmarks/run_phase34a_native_event_memory.py +179 -0
- quantbt/benchmarks/run_phase34b_native_event_prepared_score.py +173 -0
- quantbt/benchmarks/run_phase34c_native_event_single_pass.py +176 -0
- quantbt/benchmarks/run_phase7.py +654 -0
- quantbt/benchmarks/run_portfolio_real_parity.py +537 -0
- quantbt/core/__init__.py +323 -0
- quantbt/core/arbitrage.py +767 -0
- quantbt/core/basket.py +234 -0
- quantbt/core/certification.py +274 -0
- quantbt/core/constraints.py +155 -0
- quantbt/core/engine.py +1144 -0
- quantbt/core/event.py +869 -0
- quantbt/core/execution_contract.py +197 -0
- quantbt/core/execution_depth.py +613 -0
- quantbt/core/intrabar_kernel.py +1906 -0
- quantbt/core/intrabar_reference.py +907 -0
- quantbt/core/intrabar_session.py +156 -0
- quantbt/core/market_tape.py +480 -0
- quantbt/core/native_event_capabilities.py +121 -0
- quantbt/core/native_event_parity.py +336 -0
- quantbt/core/order_compiler.py +422 -0
- quantbt/core/orders.py +319 -0
- quantbt/core/portfolio.py +392 -0
- quantbt/core/preprocessor.py +256 -0
- quantbt/core/reactive.py +173 -0
- quantbt/core/results.py +341 -0
- quantbt/core/schema.py +211 -0
- quantbt/core/scopes.py +96 -0
- quantbt/core/structured_orders.py +378 -0
- quantbt/core/types.py +87 -0
- quantbt/core/vectorized.py +203 -0
- quantbt/endpoint.py +4465 -0
- quantbt/engines.py +1014 -0
- quantbt/metrics/__init__.py +45 -0
- quantbt/metrics/options_analytics.py +46 -0
- quantbt/metrics/performance.py +541 -0
- quantbt/optimization/__init__.py +94 -0
- quantbt/optimization/callbacks.py +116 -0
- quantbt/optimization/candidate_selection.py +397 -0
- quantbt/optimization/config.py +84 -0
- quantbt/optimization/constraints.py +22 -0
- quantbt/optimization/evaluator.py +21 -0
- quantbt/optimization/evaluators/__init__.py +32 -0
- quantbt/optimization/evaluators/arbitrage.py +22 -0
- quantbt/optimization/evaluators/generic.py +34 -0
- quantbt/optimization/evaluators/grid_dca.py +22 -0
- quantbt/optimization/evaluators/intrabar.py +54 -0
- quantbt/optimization/evaluators/native_event.py +51 -0
- quantbt/optimization/evaluators/options.py +22 -0
- quantbt/optimization/evaluators/portfolio.py +42 -0
- quantbt/optimization/evaluators/signal.py +43 -0
- quantbt/optimization/multiseed.py +176 -0
- quantbt/optimization/objectives.py +221 -0
- quantbt/optimization/optimizer.py +500 -0
- quantbt/optimization/result.py +80 -0
- quantbt/optimization/samplers.py +84 -0
- quantbt/optimization/space.py +223 -0
- quantbt/options/__init__.py +215 -0
- quantbt/options/cache.py +116 -0
- quantbt/options/conventions.py +158 -0
- quantbt/options/data.py +177 -0
- quantbt/options/execution.py +600 -0
- quantbt/options/fees.py +135 -0
- quantbt/options/greeks.py +173 -0
- quantbt/options/hedging.py +232 -0
- quantbt/options/iv.py +188 -0
- quantbt/options/ledger.py +263 -0
- quantbt/options/lifecycle.py +94 -0
- quantbt/options/margin.py +311 -0
- quantbt/options/packages.py +147 -0
- quantbt/options/pricing.py +191 -0
- quantbt/options/schema.py +227 -0
- quantbt/options/selectors.py +281 -0
- quantbt/options/strategy.py +249 -0
- quantbt/options/surface.py +142 -0
- quantbt/options/tape.py +226 -0
- quantbt/options/templates/__init__.py +33 -0
- quantbt/options/templates/packages.py +354 -0
- quantbt/portfolio.py +606 -0
- quantbt/py.typed +1 -0
- quantbt/reporting/__init__.py +38 -0
- quantbt/reporting/arbitrage_audit.py +211 -0
- quantbt/reporting/nautilus_bundle.py +758 -0
- quantbt/reporting/nautilus_certification.py +226 -0
- quantbt/reporting/nautilus_diagnostics.py +225 -0
- quantbt/reporting/parity.py +496 -0
- quantbt/reporting/portfolio_audit.py +206 -0
- quantbt/reporting/portfolio_nautilus.py +140 -0
- quantbt/sizing/__init__.py +3 -0
- quantbt/sizing/fast.py +67 -0
- quantbt/sizing/modes.py +162 -0
- quantbt/viz/__init__.py +4 -0
- quantbt/viz/plots.py +310 -0
- quantbt/viz/themes.py +102 -0
- quantbt/walkforward.py +3136 -0
- quantbt_engine-1.0.7.dist-info/METADATA +891 -0
- quantbt_engine-1.0.7.dist-info/RECORD +133 -0
- quantbt_engine-1.0.7.dist-info/WHEEL +5 -0
- quantbt_engine-1.0.7.dist-info/licenses/LICENSE +21 -0
- quantbt_engine-1.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Phase 13A WFO prepared market-cache benchmark.
|
|
4
|
+
|
|
5
|
+
This runner verifies that portfolio WFO endpoint scoring can reuse prepared
|
|
6
|
+
market arrays across Optuna trials without changing selected params, objective,
|
|
7
|
+
or final backtest equity.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Dict
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import pandas as pd
|
|
21
|
+
|
|
22
|
+
PACKAGE_DIR = Path(__file__).resolve().parents[1]
|
|
23
|
+
PROJECT_DIR = PACKAGE_DIR.parent
|
|
24
|
+
if str(PROJECT_DIR) not in sys.path:
|
|
25
|
+
sys.path.insert(0, str(PROJECT_DIR))
|
|
26
|
+
|
|
27
|
+
from quantbt import QuantBTEndpoint # noqa: E402
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_benchmark(rows: int = 720, trials: int = 16) -> Dict:
|
|
31
|
+
data = _make_data(rows)
|
|
32
|
+
_run_wfo(data, trials=max(2, min(int(trials), 4)), use_cache=True)
|
|
33
|
+
_run_wfo(data, trials=max(2, min(int(trials), 4)), use_cache=False)
|
|
34
|
+
cached_seconds, cached = _time_run(data, trials=trials, use_cache=True)
|
|
35
|
+
uncached_seconds, uncached = _time_run(data, trials=trials, use_cache=False)
|
|
36
|
+
cached_wf = cached.metadata["walk_forward"]
|
|
37
|
+
uncached_wf = uncached.metadata["walk_forward"]
|
|
38
|
+
cache_meta = cached_wf.get("prepared_scoring_cache", {})
|
|
39
|
+
final_equity_diff = float(abs(cached.equity.iloc[-1] - uncached.equity.iloc[-1]))
|
|
40
|
+
objective_diff = float(abs(cached_wf["best_trial"]["objective"] - uncached_wf["best_trial"]["objective"]))
|
|
41
|
+
params_match = cached_wf["params"] == uncached_wf["params"]
|
|
42
|
+
speedup = float(uncached_seconds / cached_seconds) if cached_seconds > 0.0 else 0.0
|
|
43
|
+
status = "pass" if final_equity_diff <= 1e-9 and objective_diff <= 1e-12 and params_match else "fail"
|
|
44
|
+
return {
|
|
45
|
+
"status": status,
|
|
46
|
+
"rows": int(rows),
|
|
47
|
+
"trials": int(trials),
|
|
48
|
+
"cached_seconds": float(cached_seconds),
|
|
49
|
+
"uncached_seconds": float(uncached_seconds),
|
|
50
|
+
"speedup": speedup,
|
|
51
|
+
"final_equity_diff": final_equity_diff,
|
|
52
|
+
"objective_diff": objective_diff,
|
|
53
|
+
"params_match": bool(params_match),
|
|
54
|
+
"selected_params": cached_wf["params"],
|
|
55
|
+
"cache_metadata": cache_meta,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def make_markdown(report: Dict) -> str:
|
|
60
|
+
cache = report["cache_metadata"]
|
|
61
|
+
lines = [
|
|
62
|
+
"# Phase 13A WFO Prepared Market Cache",
|
|
63
|
+
"",
|
|
64
|
+
f"Status: **{report['status']}**",
|
|
65
|
+
"",
|
|
66
|
+
f"- Rows: `{report['rows']}`",
|
|
67
|
+
f"- Optuna trials: `{report['trials']}`",
|
|
68
|
+
f"- Cached seconds: `{report['cached_seconds']:.6f}`",
|
|
69
|
+
f"- Uncached seconds: `{report['uncached_seconds']:.6f}`",
|
|
70
|
+
f"- Speedup: `{report['speedup']:.3f}x`",
|
|
71
|
+
f"- Final equity diff: `{report['final_equity_diff']}`",
|
|
72
|
+
f"- Objective diff: `{report['objective_diff']}`",
|
|
73
|
+
f"- Params match: `{report['params_match']}`",
|
|
74
|
+
f"- Selected params: `{report['selected_params']}`",
|
|
75
|
+
"",
|
|
76
|
+
"## Cache Metadata",
|
|
77
|
+
"",
|
|
78
|
+
f"- Enabled: `{cache.get('enabled')}`",
|
|
79
|
+
f"- Prepared runs: `{cache.get('prepared_runs')}`",
|
|
80
|
+
f"- Fallback runs: `{cache.get('fallback_runs')}`",
|
|
81
|
+
f"- Market cache hits: `{cache.get('market_cache_hits')}`",
|
|
82
|
+
f"- Market cache misses: `{cache.get('market_cache_misses')}`",
|
|
83
|
+
f"- Market cache entries: `{cache.get('market_cache_entries')}`",
|
|
84
|
+
"",
|
|
85
|
+
"The benchmark is a deterministic parity/reuse guard, not a universal speed claim.",
|
|
86
|
+
"Full WFO runtime can still be dominated by Optuna and report construction.",
|
|
87
|
+
]
|
|
88
|
+
return "\n".join(lines) + "\n"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _time_run(data: Dict[str, pd.DataFrame], *, trials: int, use_cache: bool):
|
|
92
|
+
start = time.perf_counter()
|
|
93
|
+
result = _run_wfo(data, trials=trials, use_cache=use_cache)
|
|
94
|
+
return time.perf_counter() - start, result
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run_wfo(data: Dict[str, pd.DataFrame], *, trials: int, use_cache: bool):
|
|
98
|
+
def strategy(data, params, train_index, test_index, fold):
|
|
99
|
+
scale = float(params["scale"])
|
|
100
|
+
return pd.DataFrame({"BTC": scale, "ETH": -scale}, index=test_index)
|
|
101
|
+
|
|
102
|
+
endpoint = QuantBTEndpoint.train_test_split(
|
|
103
|
+
strategy_class=strategy,
|
|
104
|
+
test_start="2022-01-01",
|
|
105
|
+
target_mode="portfolio",
|
|
106
|
+
portfolio_mode="longshort",
|
|
107
|
+
optimization_mode="mode_1_decay",
|
|
108
|
+
optimization_config={
|
|
109
|
+
"scoring_backend": "endpoint",
|
|
110
|
+
"use_prepared_scoring_cache": bool(use_cache),
|
|
111
|
+
},
|
|
112
|
+
optuna_trials=int(trials),
|
|
113
|
+
random_seed=123,
|
|
114
|
+
initial_capital=100_000.0,
|
|
115
|
+
leverage=5.0,
|
|
116
|
+
alloc_per_trade=1_000.0,
|
|
117
|
+
fee=0.0,
|
|
118
|
+
use_funding=False,
|
|
119
|
+
)
|
|
120
|
+
return endpoint.backtest(data=data, param_ranges={"scale": (0.5, 1.5, 0.05)})
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _make_data(rows: int) -> Dict[str, pd.DataFrame]:
|
|
124
|
+
idx = pd.date_range("2021-01-01", periods=int(rows), freq="1D", tz="UTC")
|
|
125
|
+
x = np.linspace(0.0, 16.0, len(idx))
|
|
126
|
+
btc_close = 100.0 + np.sin(x) * 2.0 + np.arange(len(idx)) * 0.01
|
|
127
|
+
eth_close = 50.0 + np.cos(x) * 1.5 + np.arange(len(idx)) * 0.005
|
|
128
|
+
return {
|
|
129
|
+
"BTC": _frame(idx, btc_close),
|
|
130
|
+
"ETH": _frame(idx, eth_close),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _frame(idx: pd.DatetimeIndex, close: np.ndarray) -> pd.DataFrame:
|
|
135
|
+
return pd.DataFrame(
|
|
136
|
+
{
|
|
137
|
+
"open": close,
|
|
138
|
+
"high": close * 1.01,
|
|
139
|
+
"low": close * 0.99,
|
|
140
|
+
"close": close,
|
|
141
|
+
"volume": 1_000.0,
|
|
142
|
+
},
|
|
143
|
+
index=idx,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def main() -> int:
|
|
148
|
+
parser = argparse.ArgumentParser()
|
|
149
|
+
parser.add_argument("--rows", type=int, default=720)
|
|
150
|
+
parser.add_argument("--trials", type=int, default=16)
|
|
151
|
+
parser.add_argument("--json", type=Path, default=PACKAGE_DIR / "benchmarks" / "phase13_wfo_cache.json")
|
|
152
|
+
parser.add_argument("--markdown", type=Path, default=PACKAGE_DIR / "benchmarks" / "phase13_wfo_cache.md")
|
|
153
|
+
args = parser.parse_args()
|
|
154
|
+
report = run_benchmark(rows=args.rows, trials=args.trials)
|
|
155
|
+
args.json.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n")
|
|
156
|
+
args.markdown.write_text(make_markdown(report))
|
|
157
|
+
print(json.dumps(report, indent=2, sort_keys=True))
|
|
158
|
+
return 0 if report["status"] == "pass" else 1
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
raise SystemExit(main())
|