Files
manifoldbt/examples/18_csv_import.py
T
2026-10-23 23:06:12 +02:00

93 lines
3.0 KiB
Python

"""CSV Import -- load your own OHLCV data from a CSV file.
Demonstrates:
- bt.import_csv() -- auto-detects standard / MetaTrader 4 / MetaTrader 5
- Backtesting on the imported data, exactly like a built-in connector
- Free on all tiers (no Pro license required)
The standard format is a header row + `timestamp,open,high,low,close,volume`
where timestamp is Unix milliseconds. MT4/MT5 exports are auto-detected.
Data: synthetic (seed 18) — a sample generated by this file, reproducible
Usage:
python examples/18_csv_import.py
"""
import os
import tempfile
import numpy as np
import manifoldbt as mbt
from manifoldbt.indicators import close, ema
from manifoldbt.helpers import time_range, Interval
# -- 1. A sample CSV ----------------------------------------------------------
# In practice you'd point `import_csv` straight at your own file. Here we
# synthesize a small one so the example runs out of the box.
tmp = tempfile.mkdtemp()
csv_path = os.path.join(tmp, "SAMPLE_1m.csv")
# 120 days of 1-minute bars. The span is the point: annualised metrics (CAGR,
# Calmar) divide by the length of the window, so a fixture covering two days
# reports a growth rate with fifty digits in it and teaches the reader nothing
# except that nobody read the output.
N = 120 * 1440
rng = np.random.default_rng(18)
steps = rng.normal(1.4e-6, 4.0e-4, N) # ~+20% over the window
px = 100.0 * np.exp(np.cumsum(steps))
o = px
c = np.roll(px, -1)
c[-1] = px[-1]
amp = np.abs(rng.normal(0.0, 2.0e-4, N))
hi = np.maximum(o, c) * (1 + amp)
lo = np.minimum(o, c) * (1 - amp)
vol = rng.uniform(1_000, 5_000, N)
base_ms = 1_704_067_200_000 # 2024-01-01 00:00 UTC
with open(csv_path, "w") as f:
f.write("timestamp,open,high,low,close,volume\n")
f.write("\n".join(
f"{base_ms + i * 60_000},{o[i]:.4f},{hi[i]:.4f},{lo[i]:.4f},{c[i]:.4f},{vol[i]:.0f}"
for i in range(N)
))
f.write("\n")
# -- 2. Import into the store (free, all tiers) -------------------------------
store = mbt.import_csv(
csv_path,
symbol="SAMPLE",
symbol_id=1,
interval="1m",
data_root=os.path.join(tmp, "data"),
metadata_db=os.path.join(tmp, "meta.sqlite"),
asset_class="crypto_spot",
)
print("Imported:", store.list_symbols())
# -- 3. Backtest on it like any other data ------------------------------------
strategy = (
mbt.Strategy.create("ema_cross")
.signal("fast", ema(close, 10))
.signal("slow", ema(close, 30))
.size(mbt.when(ema(close, 10) > ema(close, 30), 0.5, 0.0))
.describe("EMA(10/30) crossover on CSV-imported data")
)
start, end = time_range("2024-01-01", "2024-05-01")
config = mbt.BacktestConfig(
universe=[1],
time_range_start=start,
time_range_end=end,
bar_interval=Interval.minutes(1),
initial_capital=10_000,
warmup_bars=30,
)
if __name__ == "__main__":
result = mbt.run(strategy, config, store)
print("The series carries a mild upward drift, so the figures below")
print("describe the fixture. The mechanism on show is the import.\n")
print(result.summary())