"""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())