Files
manifoldbt/benchmarks/bench_backtrader_only.py
Jimmy7892 67ab17280b Initial commit: manifoldbt public repo
Python DSL, examples, docs, benchmarks, and tests.
Rust engine distributed as pre-compiled wheel via PyPI.
2026-03-17 16:15:40 +01:00

87 lines
2.8 KiB
Python

"""Standalone backtrader benchmark - EMA(12/26) crossover on synthetic 1m data."""
import argparse
import time
import numpy as np
import pandas as pd
import backtrader as btdr
def generate_ohlcv(rows, seed=42):
rng = np.random.default_rng(seed)
returns = rng.normal(0.0, 0.0003, size=rows)
mid = 100.0 * np.exp(np.cumsum(returns))
noise = rng.uniform(0.0001, 0.001, size=rows) * mid
timestamps = pd.date_range("2022-01-01", periods=rows, freq="1min", tz="UTC")
return pd.DataFrame({
"timestamp": timestamps,
"open": mid + rng.uniform(-0.5, 0.5, size=rows) * noise,
"high": mid + noise,
"low": mid - noise,
"close": mid + rng.uniform(-0.5, 0.5, size=rows) * noise,
"volume": rng.uniform(100, 10_000, size=rows),
})
class EmaCross(btdr.Strategy):
def __init__(self):
self.fast = btdr.indicators.EMA(self.data.close, period=12)
self.slow = btdr.indicators.EMA(self.data.close, period=26)
self.crossover = btdr.indicators.CrossOver(self.fast, self.slow)
def next(self):
if self.crossover > 0:
self.order_target_percent(target=0.5)
elif self.crossover < 0:
self.close()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--rows", type=int, default=500_000)
parser.add_argument("--runs", type=int, default=5)
args = parser.parse_args()
print(f"Generating {args.rows:,} synthetic 1-min bars...")
df = generate_ohlcv(args.rows)
bt_df = df[["timestamp", "open", "high", "low", "close", "volume"]].copy()
bt_df = bt_df.rename(columns={"timestamp": "datetime"}).set_index("datetime")
bt_df.index = bt_df.index.tz_localize(None)
# Warmup
print("Warmup run...")
cerebro = btdr.Cerebro()
cerebro.addstrategy(EmaCross)
cerebro.adddata(btdr.feeds.PandasData(dataname=bt_df))
cerebro.broker.set_cash(10_000)
cerebro.broker.setcommission(commission=0.0005)
cerebro.run()
# Timed runs
print(f"Running {args.runs}x timed...")
times = []
for i in range(args.runs):
cerebro = btdr.Cerebro()
cerebro.addstrategy(EmaCross)
cerebro.adddata(btdr.feeds.PandasData(dataname=bt_df))
cerebro.broker.set_cash(10_000)
cerebro.broker.setcommission(commission=0.0005)
t0 = time.perf_counter()
cerebro.run()
elapsed = time.perf_counter() - t0
times.append(elapsed)
print(f" run {i+1}: {elapsed*1000:.1f} ms")
med = np.median(times)
avg = np.mean(times)
print(f"\nbacktrader results ({args.rows:,} bars):")
print(f" median = {med*1000:.1f} ms")
print(f" mean = {avg*1000:.1f} ms")
print(f" min = {min(times)*1000:.1f} ms")
print(f" max = {max(times)*1000:.1f} ms")
if __name__ == "__main__":
main()