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,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import resource
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from quantbt import QuantBTEndpoint
|
|
13
|
+
from quantbt.core.orders import OrderCommand
|
|
14
|
+
from quantbt.core.schema import OrderSide, OrderType, TimeInForce
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _rss_mb() -> float:
|
|
18
|
+
return float(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) / 1024.0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _bars(rows: int) -> pd.DataFrame:
|
|
22
|
+
idx = pd.date_range("2020-01-01", periods=rows, freq="1h", tz="UTC")
|
|
23
|
+
x = np.arange(rows, dtype=np.float64)
|
|
24
|
+
close = pd.Series(100.0 + np.sin(x / 11.0) * 2.0 + x * 0.001, index=idx)
|
|
25
|
+
return pd.DataFrame(
|
|
26
|
+
{
|
|
27
|
+
"open": close,
|
|
28
|
+
"high": close + 2.0,
|
|
29
|
+
"low": close - 2.0,
|
|
30
|
+
"close": close,
|
|
31
|
+
"volume": 1_000.0,
|
|
32
|
+
},
|
|
33
|
+
index=idx,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TimedStrategy:
|
|
38
|
+
def __init__(self, entry_mod: int, hold: int, qty: float):
|
|
39
|
+
self.entry_mod = int(entry_mod)
|
|
40
|
+
self.hold = int(hold)
|
|
41
|
+
self.qty = float(qty)
|
|
42
|
+
self.open_bar = -1
|
|
43
|
+
|
|
44
|
+
def on_bar_close(self, context):
|
|
45
|
+
symbol = context.symbols[0]
|
|
46
|
+
if context.positions[symbol] == 0.0 and context.bar_index % self.entry_mod == 0:
|
|
47
|
+
self.open_bar = int(context.bar_index)
|
|
48
|
+
return [
|
|
49
|
+
OrderCommand(
|
|
50
|
+
timestamp=context.timestamp,
|
|
51
|
+
symbol=symbol,
|
|
52
|
+
side=OrderSide.BUY,
|
|
53
|
+
order_type=OrderType.MARKET,
|
|
54
|
+
qty=self.qty,
|
|
55
|
+
tif=TimeInForce.IOC,
|
|
56
|
+
order_id=f"entry-{context.bar_index}",
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
if context.positions[symbol] > 0.0 and self.open_bar >= 0 and context.bar_index - self.open_bar >= self.hold:
|
|
60
|
+
self.open_bar = -1
|
|
61
|
+
return [
|
|
62
|
+
OrderCommand(
|
|
63
|
+
timestamp=context.timestamp,
|
|
64
|
+
symbol=symbol,
|
|
65
|
+
side=OrderSide.SELL,
|
|
66
|
+
order_type=OrderType.MARKET,
|
|
67
|
+
qty=abs(context.positions[symbol]),
|
|
68
|
+
tif=TimeInForce.IOC,
|
|
69
|
+
reduce_only=True,
|
|
70
|
+
order_id=f"exit-{context.bar_index}",
|
|
71
|
+
)
|
|
72
|
+
]
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _params(trials: int):
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
"entry_mod": 5 + (i % 7),
|
|
80
|
+
"hold": 2 + (i % 5),
|
|
81
|
+
"qty": 0.1 + (i % 4) * 0.05,
|
|
82
|
+
}
|
|
83
|
+
for i in range(trials)
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _metrics_subset(report: dict) -> dict:
|
|
88
|
+
return {
|
|
89
|
+
"sharpe": report["sharpe"],
|
|
90
|
+
"max_drawdown_pct": report["max_drawdown_pct"],
|
|
91
|
+
"profit_factor": report["profit_factor"],
|
|
92
|
+
"num_trades": report["num_trades"],
|
|
93
|
+
"final_equity": report["final_equity"],
|
|
94
|
+
"liquidated": report["liquidated"],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def run(rows: int, trials: int) -> dict:
|
|
99
|
+
df = _bars(rows)
|
|
100
|
+
params = _params(trials)
|
|
101
|
+
public_endpoint = QuantBTEndpoint.native_event_strategy(
|
|
102
|
+
initial_capital=50_000,
|
|
103
|
+
leverage=10,
|
|
104
|
+
use_funding=False,
|
|
105
|
+
fee_rate=0.0002,
|
|
106
|
+
report_level="audit",
|
|
107
|
+
)
|
|
108
|
+
start = time.perf_counter()
|
|
109
|
+
public_reports = []
|
|
110
|
+
for param in params:
|
|
111
|
+
result = public_endpoint.simulate(data=df, strategy=TimedStrategy(**param), symbols=["BTC"])
|
|
112
|
+
public_reports.append(_metrics_subset(result.full_report(scope="full")))
|
|
113
|
+
public_seconds = time.perf_counter() - start
|
|
114
|
+
|
|
115
|
+
prepared_endpoint = QuantBTEndpoint.native_event_strategy(
|
|
116
|
+
initial_capital=50_000,
|
|
117
|
+
leverage=10,
|
|
118
|
+
use_funding=False,
|
|
119
|
+
fee_rate=0.0002,
|
|
120
|
+
report_level="audit",
|
|
121
|
+
)
|
|
122
|
+
prepared = prepared_endpoint.prepare_native_event_strategy(data=df, symbols=["BTC"])
|
|
123
|
+
start = time.perf_counter()
|
|
124
|
+
score_reports = []
|
|
125
|
+
for param in params:
|
|
126
|
+
score = prepared.score(TimedStrategy(**param))
|
|
127
|
+
score_reports.append(_metrics_subset(score.metrics))
|
|
128
|
+
prepared_seconds = time.perf_counter() - start
|
|
129
|
+
|
|
130
|
+
parity = public_reports == score_reports
|
|
131
|
+
return {
|
|
132
|
+
"rows": int(rows),
|
|
133
|
+
"trials": int(trials),
|
|
134
|
+
"public_audit_seconds": float(public_seconds),
|
|
135
|
+
"prepared_score_seconds": float(prepared_seconds),
|
|
136
|
+
"speedup": float(public_seconds / prepared_seconds) if prepared_seconds > 0.0 else np.inf,
|
|
137
|
+
"peak_rss_mb": float(_rss_mb()),
|
|
138
|
+
"metric_parity": bool(parity),
|
|
139
|
+
"prepared_scores": int(prepared.metadata["scores"]),
|
|
140
|
+
"public_last_report_level": public_endpoint.result.metadata["report_level"],
|
|
141
|
+
"prepared_endpoint_result_retained": prepared_endpoint.result is not None,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def main() -> None:
|
|
146
|
+
parser = argparse.ArgumentParser()
|
|
147
|
+
parser.add_argument("--rows", type=int, default=1_000)
|
|
148
|
+
parser.add_argument("--trials", type=int, default=20)
|
|
149
|
+
parser.add_argument("--json-out", default="benchmarks/phase34b_native_event_prepared_score.json")
|
|
150
|
+
parser.add_argument("--md-out", default="benchmarks/phase34b_native_event_prepared_score.md")
|
|
151
|
+
args = parser.parse_args()
|
|
152
|
+
payload = run(rows=args.rows, trials=args.trials)
|
|
153
|
+
Path(args.json_out).write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
|
154
|
+
lines = [
|
|
155
|
+
"# Phase 34B Native Event Prepared Score Benchmark",
|
|
156
|
+
"",
|
|
157
|
+
f"- Rows: `{payload['rows']}`",
|
|
158
|
+
f"- Trials: `{payload['trials']}`",
|
|
159
|
+
f"- Public audit seconds: `{payload['public_audit_seconds']:.6f}`",
|
|
160
|
+
f"- Prepared score seconds: `{payload['prepared_score_seconds']:.6f}`",
|
|
161
|
+
f"- Speedup: `{payload['speedup']:.3f}x`",
|
|
162
|
+
f"- Peak RSS MB: `{payload['peak_rss_mb']:.3f}`",
|
|
163
|
+
f"- Metric parity: `{payload['metric_parity']}`",
|
|
164
|
+
f"- Prepared endpoint result retained: `{payload['prepared_endpoint_result_retained']}`",
|
|
165
|
+
"",
|
|
166
|
+
"Prepared score reuses market arrays and returns `NativeEventScoreResult` rather than storing full public artifacts on the endpoint.",
|
|
167
|
+
]
|
|
168
|
+
Path(args.md_out).write_text("\n".join(lines) + "\n")
|
|
169
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
main()
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import resource
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from quantbt import OrderCommand, QuantBTEndpoint
|
|
13
|
+
from quantbt.core.schema import OrderSide, OrderType, TimeInForce
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _rss_mb() -> float:
|
|
17
|
+
return float(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) / 1024.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _bars(rows: int) -> pd.DataFrame:
|
|
21
|
+
idx = pd.date_range("2020-01-01", periods=rows, freq="1h", tz="UTC")
|
|
22
|
+
x = np.arange(rows, dtype=np.float64)
|
|
23
|
+
close = pd.Series(100.0 + np.sin(x / 9.0) * 3.0 + np.cos(x / 23.0) * 1.5, index=idx)
|
|
24
|
+
return pd.DataFrame(
|
|
25
|
+
{
|
|
26
|
+
"open": close.shift(1).fillna(close.iloc[0]),
|
|
27
|
+
"high": close + 2.5,
|
|
28
|
+
"low": close - 2.5,
|
|
29
|
+
"close": close,
|
|
30
|
+
"volume": 1_000.0 + (x % 50.0),
|
|
31
|
+
},
|
|
32
|
+
index=idx,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CyclicStrategy:
|
|
37
|
+
def __init__(self, entry_mod: int, hold: int, qty: float):
|
|
38
|
+
self.entry_mod = int(entry_mod)
|
|
39
|
+
self.hold = int(hold)
|
|
40
|
+
self.qty = float(qty)
|
|
41
|
+
self.open_bar = -1
|
|
42
|
+
|
|
43
|
+
def on_bar_close(self, context):
|
|
44
|
+
symbol = context.symbols[0]
|
|
45
|
+
if context.positions[symbol] == 0.0 and context.bar_index % self.entry_mod == 0:
|
|
46
|
+
self.open_bar = int(context.bar_index)
|
|
47
|
+
return [
|
|
48
|
+
OrderCommand(
|
|
49
|
+
timestamp=context.timestamp,
|
|
50
|
+
symbol=symbol,
|
|
51
|
+
side=OrderSide.BUY,
|
|
52
|
+
order_type=OrderType.MARKET,
|
|
53
|
+
qty=self.qty,
|
|
54
|
+
tif=TimeInForce.IOC,
|
|
55
|
+
order_id=f"entry-{context.bar_index}",
|
|
56
|
+
)
|
|
57
|
+
]
|
|
58
|
+
if context.positions[symbol] > 0.0 and self.open_bar >= 0 and context.bar_index - self.open_bar >= self.hold:
|
|
59
|
+
self.open_bar = -1
|
|
60
|
+
return [
|
|
61
|
+
OrderCommand(
|
|
62
|
+
timestamp=context.timestamp,
|
|
63
|
+
symbol=symbol,
|
|
64
|
+
side=OrderSide.SELL,
|
|
65
|
+
order_type=OrderType.MARKET,
|
|
66
|
+
qty=abs(context.positions[symbol]),
|
|
67
|
+
tif=TimeInForce.IOC,
|
|
68
|
+
reduce_only=True,
|
|
69
|
+
order_id=f"exit-{context.bar_index}",
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _params(trials: int):
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
"entry_mod": 4 + (i % 9),
|
|
79
|
+
"hold": 2 + (i % 6),
|
|
80
|
+
"qty": 0.1 + (i % 5) * 0.025,
|
|
81
|
+
}
|
|
82
|
+
for i in range(trials)
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _accounting_tuple(result) -> tuple:
|
|
87
|
+
return (
|
|
88
|
+
tuple(np.round(result.equity.to_numpy(dtype=np.float64), 12)),
|
|
89
|
+
tuple(np.round(result.returns.to_numpy(dtype=np.float64), 12)),
|
|
90
|
+
tuple(np.round(result.positions.to_numpy(dtype=np.float64).ravel(), 12)),
|
|
91
|
+
tuple(np.round(result.fees.to_numpy(dtype=np.float64), 12)),
|
|
92
|
+
tuple(np.round(result.funding.to_numpy(dtype=np.float64), 12)),
|
|
93
|
+
tuple(np.round(result.margin.to_numpy(dtype=np.float64).ravel(), 12)),
|
|
94
|
+
bool(result.liquidated),
|
|
95
|
+
int(result.liquidation_bar),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def run(rows: int, trials: int) -> dict:
|
|
100
|
+
df = _bars(rows)
|
|
101
|
+
params = _params(trials)
|
|
102
|
+
kwargs = dict(
|
|
103
|
+
initial_capital=50_000,
|
|
104
|
+
leverage=10,
|
|
105
|
+
use_funding=False,
|
|
106
|
+
fee_rate=0.0002,
|
|
107
|
+
report_level="minimal",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
replay_endpoint = QuantBTEndpoint.native_event_strategy(**kwargs, reactive_kernel_mode="replay_certified")
|
|
111
|
+
start = time.perf_counter()
|
|
112
|
+
replay_fingerprints = []
|
|
113
|
+
replay_static_replays = 0
|
|
114
|
+
for param in params:
|
|
115
|
+
result = replay_endpoint.simulate(data=df, strategy=CyclicStrategy(**param), symbols=["BTC"])
|
|
116
|
+
replay_fingerprints.append(_accounting_tuple(result))
|
|
117
|
+
replay_static_replays += int(result.metadata.get("reactive_static_replay_count", 0))
|
|
118
|
+
replay_seconds = time.perf_counter() - start
|
|
119
|
+
|
|
120
|
+
single_endpoint = QuantBTEndpoint.native_event_strategy(**kwargs, reactive_kernel_mode="single_pass")
|
|
121
|
+
start = time.perf_counter()
|
|
122
|
+
single_fingerprints = []
|
|
123
|
+
single_static_replays = 0
|
|
124
|
+
for param in params:
|
|
125
|
+
result = single_endpoint.simulate(data=df, strategy=CyclicStrategy(**param), symbols=["BTC"])
|
|
126
|
+
single_fingerprints.append(_accounting_tuple(result))
|
|
127
|
+
single_static_replays += int(result.metadata.get("reactive_static_replay_count", 0))
|
|
128
|
+
single_seconds = time.perf_counter() - start
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
"rows": int(rows),
|
|
132
|
+
"trials": int(trials),
|
|
133
|
+
"replay_certified_seconds": float(replay_seconds),
|
|
134
|
+
"single_pass_seconds": float(single_seconds),
|
|
135
|
+
"speedup": float(replay_seconds / single_seconds) if single_seconds > 0.0 else np.inf,
|
|
136
|
+
"replay_certified_static_replays": int(replay_static_replays),
|
|
137
|
+
"single_pass_static_replays": int(single_static_replays),
|
|
138
|
+
"accounting_parity": bool(replay_fingerprints == single_fingerprints),
|
|
139
|
+
"peak_rss_mb": float(_rss_mb()),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def main() -> None:
|
|
144
|
+
parser = argparse.ArgumentParser()
|
|
145
|
+
parser.add_argument("--rows", type=int, default=1_000)
|
|
146
|
+
parser.add_argument("--trials", type=int, default=20)
|
|
147
|
+
parser.add_argument("--json-out", default="benchmarks/phase34c_native_event_single_pass.json")
|
|
148
|
+
parser.add_argument("--md-out", default="benchmarks/phase34c_native_event_single_pass.md")
|
|
149
|
+
args = parser.parse_args()
|
|
150
|
+
payload = run(rows=args.rows, trials=args.trials)
|
|
151
|
+
json_path = Path(args.json_out)
|
|
152
|
+
md_path = Path(args.md_out)
|
|
153
|
+
json_path.parent.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
md_path.parent.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
json_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
|
156
|
+
lines = [
|
|
157
|
+
"# Phase 34C Native Event Single-Pass Benchmark",
|
|
158
|
+
"",
|
|
159
|
+
f"- Rows: `{payload['rows']}`",
|
|
160
|
+
f"- Trials: `{payload['trials']}`",
|
|
161
|
+
f"- Replay-certified seconds: `{payload['replay_certified_seconds']:.6f}`",
|
|
162
|
+
f"- Single-pass seconds: `{payload['single_pass_seconds']:.6f}`",
|
|
163
|
+
f"- Speedup: `{payload['speedup']:.3f}x`",
|
|
164
|
+
f"- Replay-certified static replays: `{payload['replay_certified_static_replays']}`",
|
|
165
|
+
f"- Single-pass static replays: `{payload['single_pass_static_replays']}`",
|
|
166
|
+
f"- Accounting parity: `{payload['accounting_parity']}`",
|
|
167
|
+
f"- Peak RSS MB: `{payload['peak_rss_mb']:.3f}`",
|
|
168
|
+
"",
|
|
169
|
+
"This benchmark isolates the Phase 34C mode switch: `single_pass` materializes accounting from the reactive session for minimal/score runs and skips the final static replay.",
|
|
170
|
+
]
|
|
171
|
+
md_path.write_text("\n".join(lines) + "\n")
|
|
172
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
main()
|