High-performance Python backtesting engine powered by Numba JIT, with indicators, optimization, walk-forward validation, and reporting
Project description
NeutrinoBT
A high-performance Python backtesting library powered by Numba JIT.
NeutrinoBT runs your trading strategies fast — the core engine is compiled to machine code via Numba so that even large parameter sweeps complete in seconds. The API is modeled after Pine Script, so moving from TradingView concepts to Python is straightforward.
Features
Core Engine
- Numba JIT backtesting —
@njit(cache=True)compiled execution loop; orders, stops, and equity curves computed at native speed - Two signal modes — vectorized (NumPy arrays) for simple strategies; Pine Script-style order API (
order(),exit(),cancel()) for complex logic - Multiple positions — FIFO, LIFO, or BY_ID close selection; configurable
max_positions - Stops & exits — per-trade stop loss, take profit, and trailing stops (activation by price or points)
- Leverage & liquidation — configurable leverage with automatic liquidation price calculation
- Entry on bar close — optional
entry_on_bar_close=Truefills at next bar open (matches Pine Scriptstrategy.entrybehaviour) - Commission & slippage — applied separately to entries and stops
Indicators (self.I / nt.*)
25+ built-in indicators, all Numba-accelerated, organized into five categories:
| Category | Indicators |
|---|---|
| Moving Averages | EMA, SMA, RMA, WMA, DEMA, TEMA, HMA, VWMA |
| Momentum | RSI, MACD, Stoch, StochRSI, CCI, Williams %R, ADX, MFI |
| Volatility | ATR, STDEV, SuperTrend, Bollinger Bands, Keltner Channel, Donchian Channel |
| Trend | Parabolic SAR, Ichimoku Cloud, Linear Regression |
| Volume | OBV, VWAP |
All indicators support a timeframe= parameter for multi-timeframe analysis without look-ahead bias.
Optimization
- Grid search — exhaustive sweep over all parameter combinations
- Random search — sampled trials with reproducible seeds
- Parallel workers —
n_jobs=viamultiprocessing.Pool(Windows-safe) - Built-in objectives —
net_profit,sharpe,profit_factor,gt_score,negative_max_drawdown; or pass any callable
Statistics
Professional-grade metrics in the style of TradingView's Strategy Tester: Net Profit, Gross Profit/Loss, Profit Factor, Sharpe, Sortino, Calmar, Max Drawdown, PROM, GT-Score, Win Rate, Avg Win/Loss, Avg Bars in Trade, CAGR, T-Test — all broken down by All / Long / Short.
Jupyter / In-Notebook Visualization
plot_equity— equity curve chart from mark-to-market or trade-step fallbackplot_drawdown— underwater / drawdown chart (% from running peak)plot_price_with_trades— close price overlaid with long/short entry & exit markersdisplay_statistics_summary— styled statistics table rendered inline in Jupyter- QuantStats export —
quantstats_returns()converts the equity curve to per-bar decimal returns compatible with the QuantStats tear-sheet library (optional install)
Dashboard & Export
- HTML dashboard — self-contained interactive report with TradingView price chart, equity curve, drawdown, returns heatmap, MFE/MAE scatter, and paginated trade log
- Pine Script export — generate a Pine Script indicator from your strategy's signals
- CSV export — statistics table and full trade list
Performance
Benchmark: EMA(12/26) crossover strategy, 8,640 rows of BTCUSDT 30 m data, measured at 10 / 100 / 1,000 repeated runs (Numba JIT pre-warmed).
| Engine | Avg time / run | × NeutrinoBT | × backtrader |
|---|---|---|---|
| Hardcoded (raw NumPy) | ~1 ms | 22× faster | 1,090× faster |
| NeutrinoBT | ~22 ms | — | ~50× faster |
| backtesting.py | ~77 ms | 3.5× slower | 14× faster |
| backtrader | ~1,090 ms | 50× slower | — |
NeutrinoBT is only ~22× behind a raw NumPy loop (the theoretical floor) while delivering a production-grade framework: stops, leverage, multi-position, full statistics, and HTML export. At 1,000-parameter-sweep scale, backtrader takes ~18 min total vs ~22 s for NeutrinoBT.
Source:
backtesting-showcase.ipynbin the repo root. Results vary by CPU; the first per-session run includes Numba JIT compilation (~1–2 s one-time cost).
Installation
# Using uv (recommended)
uv pip install neutrinobt
# Or with pip
pip install neutrinobt
Requirements: Python 3.12+, NumPy, Numba, Pandas, Polars, Jinja2, Matplotlib
Optional extras:
# Development tools from a source checkout (JupyterLab, pytest, ruff)
uv sync --group dev
# Binance / CCXT data downloader helpers
pip install "neutrinobt[data]"
# QuantStats tear sheets (pulls scipy, seaborn, yfinance)
pip install "neutrinobt[analytics]"
# Parquet-backed WFO trade stores
pip install "neutrinobt[wfo]"
# Everything optional
pip install "neutrinobt[all]"
Quick Start
import numpy as np
from neutrinobt.data.loader import CSVLoader
from neutrinobt.strategy.base import Strategy
from neutrinobt.core.reactor import Reactor
import neutrinobt.indicators as nt
# 1. Load OHLCV data
loader = CSVLoader("sampledata/BTCUSDTUSDT/1h.csv")
bars = loader.load({
"time": "timestamp", "open": "open", "high": "high",
"low": "low", "close": "close", "volume": "volume",
})
# 2. Define a strategy
class EMACrossover(Strategy):
def init(self):
self.ema_fast = self.I.EMA(20)
self.ema_slow = self.I.EMA(50)
def generate_signals(self):
valid = ~(np.isnan(self.ema_fast) | np.isnan(self.ema_slow))
self.buy_cond(nt.crossover(self.ema_fast, self.ema_slow) & valid)
self.sell_cond(nt.crossunder(self.ema_fast, self.ema_slow) & valid)
def create_reactor(self):
return Reactor(bars, initial_balance=10_000.0, commission_rate=0.001)
# 3. Run
results = EMACrossover(bars).run(show_summary=True)
print(f"Final equity: ${results['final_equity']:,.2f}")
Jupyter Plotting
# Pass export_timeseries=True to record bar-by-bar equity
results = EMACrossover(bars).run(export_timeseries=True, show_summary=False)
from neutrinobt.notebook.plots import plot_equity, plot_drawdown, display_statistics_summary
from neutrinobt.notebook.quantstats_export import quantstats_returns
plot_equity(bars, equity_curve=results["equity_curve"])
plot_drawdown(bars, equity_curve=results["equity_curve"])
display_statistics_summary(results["statistics"])
# Export per-bar decimal returns ready for QuantStats
qs = quantstats_returns(bars=bars, equity_curve=results["equity_curve"])
# import quantstats; quantstats.reports.basic(qs.strategy, benchmark=qs.benchmark)
See examples/notebooks/09_jupyter_plotting.ipynb for a full walkthrough.
Tutorials
The examples/ folder is a step-by-step curriculum. Scripts 01–08 cover the core engine; the notebooks/ subfolder has interactive Jupyter examples.
Scripts — run from the project root:
| File | Topic | What you learn |
|---|---|---|
01_getting_started.py |
First backtest | Load data, validate, define a strategy, read results |
02_strategy_basics.py |
Strategy class | init(), generate_signals(), create_reactor(), statistics keys |
03_indicators.py |
All 25+ indicators | Every indicator category, standalone use, multi-timeframe |
04_risk_management.py |
Risk controls | Fixed/ATR stop loss & take profit, trailing stops, leverage, sizing |
05_advanced_orders.py |
Order-based API | order(), exit(), cancel(), limit/stop entries, multiple positions |
06_optimization.py |
Parameter search | Optimizer.run(), grid vs random, built-in objectives, parallel |
07_dashboard_report.py |
HTML report | export_html=True, generate_report(), all dashboard sections |
08_real_world_strategy.py |
Full strategy | Date range, warmup, production backtest, trade analysis |
python examples/01_getting_started.py
Notebooks — open in JupyterLab (uv sync --group dev && uv run jupyter lab):
| Notebook | Topic | What you learn |
|---|---|---|
notebooks/09_jupyter_plotting.ipynb |
In-notebook visualization | Equity curve, drawdown, trade markers, statistics table, QuantStats export |
Architecture
neutrinobt/
├── core/
│ ├── reactor_core.py # Numba @njit compiled engine (the hot loop)
│ ├── reactor.py # Python wrapper — validation, results, statistics
│ └── statistics.py # ~30 performance metrics
├── strategy/
│ └── base.py # Strategy base class + IndicatorFactory (self.I)
├── indicators/ # 25+ @njit indicators
├── notebook/ # Jupyter helpers (no mandatory new deps)
│ ├── plots.py # plot_equity, plot_drawdown, plot_price_with_trades, display_statistics_summary
│ └── quantstats_export.py # quantstats_returns, export_quantstats_returns_csv
├── optimization/ # Optimizer — grid/random search
├── dashboard/ # HTML report generator (Jinja2 + ECharts)
├── export/ # Pine Script exporter
└── data/ # CSVLoader, Resampler, validation
Execution flow:
Strategy.init()— indicators computed, warmup trackedStrategy.generate_signals()— boolean arrays or Pine-style ordersReactor.run()— Numba JIT loop processes every bar: pending fills, trailing updates, liquidations, SL/TP, exits, entries- Results —
TradeList,Statistics, optional HTML report
Sample Data
CSV bars live under sampledata/<BASE>USDTUSDT/<timeframe>.csv. That directory is gitignored
— fetch Binance USDT‑M perpetual OHLCV with CCXT:
uv sync --group data
# Default list: BTC, XRP, ETH, HYPE, BNB, DOGE — timeframes 30m, 1h, 2h
uv run --group data python scripts/fetch_binance_usdt_perp_ohlcv.py --output-dir sampledata
# Tests and ``research_pipeline/configs/datasets.json`` also expect daily BTC / XRP files:
uv run --group data python scripts/fetch_binance_usdt_perp_ohlcv.py --output-dir sampledata \
--bases BTC XRP --timeframes 1d
See the script docstring for the exact default symbol/timeframe lists.
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neutrinobt-0.1.0.tar.gz.
File metadata
- Download URL: neutrinobt-0.1.0.tar.gz
- Upload date:
- Size: 185.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d9fc295ecb188a733907c98f914c7e16b2b30c476fb1162fc87b6798f3661a3
|
|
| MD5 |
46ccefd8b416543d954d2e8187e93190
|
|
| BLAKE2b-256 |
d5d4f95d2c52a76f7bd99959bb6fa00a8e4639b36ab396990bf0f8e269bfa7f3
|
File details
Details for the file neutrinobt-0.1.0-py3-none-any.whl.
File metadata
- Download URL: neutrinobt-0.1.0-py3-none-any.whl
- Upload date:
- Size: 218.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52156b9a82bc24bdf54bd29542f4953faa2afd630635f5921e0b62073526c5d9
|
|
| MD5 |
71494a20d9ec60b1c9623069a61fcb05
|
|
| BLAKE2b-256 |
09b97aeea9d40e72bd625a2ae2789b64022a91e8a52b0aa202c7c8a99f646839
|