Financial data is multidimensional. Your research engine should be too.
Analyze market data and test trading ideas at any scale with one flexible toolkit. Build indicators and signals, simulate and optimize portfolios, validate strategies across time and market conditions, and understand every result without giving up control of your workflow.
Architected and built by Oleg Polakow, senior software engineer and original creator of VectorBT.
8 years
In development
VectorBT has evolved continuously since 2017
8.7k
Across the open-source VectorBT repository
510k+
Open-source VectorBT downloads from PyPI
65k+
Community messages
Archived across 15 topic channels
Workflow
A foundation for any quantitative trading workflow.
Acquire and align data, build indicators and signals, simulate portfolios, validate across time and parameters, and analyze the result through one connected workflow.
Data
Connect, cache, transform, combine, and stream local and remote market data
Indicators
Build, search, stream with stateful accumulators, parallelize, and visualize indicator and signal pipelines
Portfolio
Model orders, signals, leverage, stops, limits, cash flows, and simulation callbacks
Optimization
Explore parameter spaces with cross-validation, conditional grids, and portfolio optimizers
Analysis
Inspect trades, patterns, projections, benchmarks, and excursion metrics
Capabilities across every stage:
Philosophy
Built for people who want control over the workflow.
VBT is a composable toolkit, not a fixed pipeline. Use ready-made components, replace any stage with your own code, or work directly with the underlying functions and kernels.
VBT is not
- No-code strategy builder
- Hosted trading bot
- Strategy marketplace
- Framework that controls your workflow
With VBT
- Bring your own data, models, and execution
- Choose high-level components for convenience or the underlying functions and kernels for control and performance
- Explore many instruments, parameters, and scenarios at once
- Inspect, combine, and reuse intermediate results
- Scale functions with parameterization, chunking, and parallel/distributed execution
- Work in Python, call Rust from Python, or build in native Rust
- Give AI coding agents full freedom
Validation
Explore robustness, not just the best result.
VBT keeps parameter exploration and validation in the same workflow, so promising performance can be tested across configurations, periods, and market conditions.
Validate across time
Use rolling, expanding, walk-forward, purged, and combinatorial splits to measure behavior beyond one historical window.
Inspect parameter surfaces
Compare neighborhoods and complete result surfaces instead of reducing the search to one winning configuration.
Compare every split
Apply the same analytics to in-sample and out-of-sample results to understand where performance persists or breaks down.
One pipeline evaluates 6,475 parameter pairs across 24 rolling train/test splits, generating all 310,800 results in under a minute on an Apple M3. Each heatmap cell shows how consistently its in-sample Sharpe ratio carries into the next out-of-sample period.
@vbt.cv_split(
splitter="from_n_rolling",
splitter_kwargs=dict(
n=24, length=720,
split=0.75, set_labels=["train", "test"],
),
parameterized_kwargs=dict(
mono_chunk_len=128,
execute_kwargs=dict(chunk_len="auto", engine="threadpool"),
),
return_grid="all",
)
def sma_crossover_sharpe(
close: vbt.Takeable,
fast_window: vbt.Param(
condition="fast_window < slow_window",
mono_merge_func="concat",
),
slow_window: vbt.Param(mono_merge_func="concat"),
) -> vbt.MergeFunc("concat"):
fast_ma = vbt.MA.run(close, fast_window, hide_params=True)
slow_ma = vbt.MA.run(close, slow_window, hide_params=True)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
return vbt.PF.from_signals(close, entries, exits).sharpe_ratioCapabilities
One connected quantitative research toolkit.
A broad set of composable capabilities covers the path from acquiring data to validating and analyzing portfolios.
- Data sources and adapters
- 20+
- Remote, local, database, and synthetic data
- Indicators, signals, and labels
- 500+
- Built in, generated, and integrated
- Cross-validation factories
- 10+
- Including purged and walk-forward methods
- Portfolio optimization factories
- 10
- From allocation functions to integrated optimizers
- Portfolio simulation factories
- 8
- Unique behavior across Numba and Rust
- Metrics
- 200+
- Across portfolios, records, and statistical integrations
- Plot types
- 90+
- Reusable visual analysis across VBT objects
- Jitting backends
- 3
- Numba, NumPy, and Rust
- Execution engines
- 7
- Serial, local parallel, and distributed execution
Execution
One Python workflow, two low-level compute backends.
Each example below runs the same moving-average crossover pipeline. Python provides the high-level composition and convenience layer, while Numba and Rust expose the underlying indicator, signal, simulation, and returns APIs. Rust is also available as an independent native crate.
Python workflow
Compose data, indicators, signals, portfolios, optimization, and analysis through one expressive API. Python manages configuration, parameterization, metadata, and results, then dispatches computational work to a low-level compute backend.
data = vbt.YFData.pull("BTC-USD")
fast_ma = data.run("vbt:ma", window=20, hide_params=True)
slow_ma = data.run("vbt:ma", window=50, hide_params=True)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.PF.from_signals(data, entries, exits)
sharpe = pf.sharpe_ratioCompute backends
Numba backend
Write numerical kernels in Python and compile supported code at runtime to native machine code with C-like performance.
Use it through the high-level workflow, or call its kernels directly when lower-level control is useful.
close = vbt.to_2d_array(data.close)
fast_ma = vbt.nb.ma_nb(close, 20)
slow_ma = vbt.nb.ma_nb(close, 50)
entries = vbt.nb.crossed_above_nb(fast_ma, slow_ma)
exits = vbt.nb.crossed_above_nb(slow_ma, fast_ma)
sim_out = vbt.pf_nb.from_signals_nb(
target_shape=close.shape,
group_lens=np.full(close.shape[1], 1),
close=close,
long_entries=entries,
long_exits=exits,
save_returns=True,
)
sharpe = vbt.ret_nb.sharpe_ratio_nb(
sim_out.in_outputs.returns,
ann_factor=365,
)Rust backend
An independent native crate that can also serve as an optional, ahead-of-time compiled compute backend for Python.
Use it directly from Rust, or let compatible Python operations dispatch to it without changing the high-level workflow.
let fast_ma = ma().close(close.view()).window(20).call()?;
let slow_ma = ma().close(close.view()).window(50).call()?;
let entries = crossed_above().arr1(fast_ma.view()).arr2(slow_ma.view()).call()?;
let exits = crossed_above().arr1(slow_ma.view()).arr2(fast_ma.view()).call()?;
let group_lens = Array1::<i64>::ones(close.ncols());
let sim_out = from_signals()
.target_shape(close.dim())
.group_lens(group_lens.view())
.close(close.view())
.long_entries(entries.view())
.long_exits(exits.view())
.save_returns(true)
.call()?;
let sharpe = sharpe_ratio_1d()
.returns(sim_out.in_outputs.returns.column(0))
.ann_factor(365.0)
.call();Performance
Compare simulation throughput.
Higher is better. High-level covers the full portfolio workflow, mid-level runs the simulation with prepared inputs, and low-level processes each order directly.
1,000,000 one-minute bars and 1,388 filled orders per column.
- High-level
- 320M bars/s
- 25 ms for this workload
- Mid-level
- 579M bars/s
- 14 ms for this workload, 1.8x high-level
- Low-level
- 4.7B bars/s
- 1.7 ms for this workload, 14.6x high-level
| Simulator | Version | Mode | Status | Columns | Warm throughput (M bars/s) | First-call throughput (M bars/s) | Warm runtime (s) | First-call runtime (s) | Compilation (s) |
|---|---|---|---|---|---|---|---|---|---|
| PRO low-level Rust | 2026.6.27 | Parallel | Completed | 8 | 4665.743238317416 | — | 0.001714625 | — | — |
| PRO low-level Numba | 2026.6.27 | Parallel | Completed | 8 | 4163.3230220430005 | 2.116083257823813 | 0.00192154198884964 | 3.7805695831775665 | 3.778648041188717 |
| PRO low-level Rust | 2026.6.27 | Serial | Completed | 1 | 1424.9236240937485 | — | 0.000701792 | — | — |
| PRO low-level Numba | 2026.6.27 | Serial | Completed | 1 | 1226.9939863981183 | 0.351051430522905 | 0.0008149999193847179 | 2.84858545800671 | 2.847770458087325 |
| PRO mid-level Rust | 2026.6.27 | Parallel | Completed | 8 | 579.383003451964 | — | 0.013807792 | — | — |
| PRO high-level Rust | 2026.6.27 | Parallel | Completed | 8 | 319.6106135270101 | — | 0.025030457880347967 | — | — |
| Manifold-BT | 0.19.0 | Parallel | Completed | 8 | 190.96269066431148 | — | 0.041893 | — | — |
| PRO mid-level Rust | 2026.6.27 | Serial | Completed | 1 | 112.24867661616487 | — | 0.008908791 | — | — |
| Manifold-BT | 0.19.0 | Serial | Completed | 1 | 109.29210028648235 | — | 0.0091497921384871 | — | — |
| PRO mid-level Numba | 2026.6.27 | Parallel | Completed | 8 | 103.39379403094054 | 0.5160845768526553 | 0.07737408298999071 | 15.50133516639471 | 15.42396108340472 |
| PRO high-level Numba | 2026.6.27 | Parallel | Completed | 8 | 95.37486498427566 | 0.5073225550479601 | 0.08387954207137227 | 15.769060374703258 | 15.685180832631886 |
| PRO high-level Rust | 2026.6.27 | Serial | Completed | 1 | 82.13186841345684 | — | 0.012175541836768389 | — | — |
| PRO mid-level Numba | 2026.6.27 | Serial | Completed | 1 | 71.25001979919584 | 0.08527053056556519 | 0.014035083819180727 | 11.72738099982962 | 11.71334591601044 |
| PRO high-level Numba | 2026.6.27 | Serial | Completed | 1 | 54.48255143363268 | 0.08348744942597715 | 0.01835450017824769 | 11.97784824995324 | 11.959493749774992 |
| OSS Numba | 1.1.0 | Serial | Completed | 1 | 21.436705511337596 | 0.33670846162238993 | 0.046648959163576365 | 2.969928332604468 | 2.9232793734408915 |
| RaptorBT | 0.9.0 | Serial | Completed | 1 | 20.865011249168635 | — | 0.04792712489143014 | — | — |
| Nautilus Python v2 | 2.0.0rc3 | Serial | Completed | 1 | 0.7077891993928654 | — | 1.412850041873753 | — | — |
| Nautilus Rust v2 | 0.62.0 | Serial | Completed | 1 | 0.5931788730150103 | — | 1.6858321250000001 | — | — |
| Backtrader | 1.9.78.123 | Parallel | Completed | 8 | 0.12284478022451349 | — | 65.12283212505281 | — | — |
| PyBroker Numba | 2.0.0 | Serial | Completed | 1 | 0.08123612131557577 | 0.07393742333951846 | 12.309795000124723 | 13.524950624909252 | 1.2151556247845292 |
| Backtrader | 1.9.78.123 | Serial | Completed | 1 | 0.020621870727887733 | — | 48.49220583308488 | — | — |
Environment: Apple M3 (8 logical cores), arm64, macOS 26.5.2
Architecture
A consistent model from composition to execution.
You control the workflow while VBT carries the repetitive engineering across data alignment, array preparation, compiled execution, persistence, and analysis.
Multidimensional by design
Broadcasting, indexing, grouping, and wrapping preserve instruments, time frames, parameters, and scenarios throughout the workflow.
Scale the same workflow
Parameterize, chunk, cache, and distribute work across supported execution engines without changing the research model.
Inspect and resume
Keep intermediate objects available for analysis, continue simulations from saved state, and process long histories incrementally.
Source code
Built to be inspected, extended, and understood.
VectorBT has been architected and maintained by Oleg Polakow, its original creator and a senior software engineer, since 2017. VectorBT PRO has continued that architectural direction since 2021, shaped by community feedback and demanding quantitative workflows.
Membership includes source access, so you can inspect the implementation, trace a result through each layer, and adapt individual components to your data and infrastructure. Shared conventions keep those components predictable to use and practical to extend.
The MCP server, CLI, LLM-ready documentation, and agent tools make the same codebase easier for people and coding agents to navigate, explain, and automate.
Crowdfunding
Built for the community, funded by its members.
VectorBT PRO is an independent crowdfunding initiative. Membership revenue funds the infrastructure, software subscriptions, AI tokens, development, maintenance, and other resources that keep the project moving forward.
Community feedback
Loved by people who build with it.
Rewritten from recurring praise shared in Discord. Details are mixed to keep individual messages private.
this thing is ridiculously fast
First run compiled. After that, millions of rows were done in seconds. Had to check I had not accidentally skipped half the data.
bit of a learning curve, honestly
NumPy and broadcasting took me a while. Once I stopped trying to make VBT behave like the other backtesters I had used, it started to click.
There are things I can test now that I would not even know how to set up in those tools.
I keep thinking something is missing
Then I search the docs properly and there it is. Usually with an example.
The documentation is huge, but finding things gets easier once you know how it is organised.
yep, that fixed it
Spent ages changing the index and reshaping everything. The actual problem was tiny. One reply pointed it out and the backtest ran.
mostly lurking here
I rarely post, but someone will ask the exact question I could not put into words. Then the replies send me off to try something new.
Reading this Discord has become part of learning VBT for me.
Joined for the backtesting
Now I somehow have parameter grids, custom indicators, portfolio optimization, and lower-level functions in the same project.
There is always another part of VBT I have not explored yet.
Get complete access to VectorBT PRO.
Membership includes the complete source, private documentation and API references, tutorials, cookbook recipes, release notes, ongoing updates, and the private Discord community.