- Platform
- Quantitative research engine
- License
- Proprietary (individual and organization terms)
- Pricing
- Paid individual or organization license
- Live trading
- Live data and incremental simulation tools, not turnkey broker execution
- Best for
- Broad strategy research, execution-aware portfolio simulation, time-aware validation, and Python-to-Rust workflows
VectorBT PRO, usually shortened to VBT PRO, is made for people who want to test many strategy ideas without giving up detailed portfolio rules. Parameter searches, compiled simulations, train/test splits, data tools, portfolio analysis, and streaming updates preserve labeled results. Supported algorithms are also available in native Rust.
The same workflow can grow from a quick pandas-style test into deeper research. You can test many parameter values at once, write compiled logic that reacts to earlier trades, continue an existing portfolio with new rows, and move supported calculations to native Rust. You can still inspect the orders, trades, and other records behind each result.
VBT PRO is a separately licensed product from the same author as source-available VectorBT. It is not just a faster edition of the public package. The PRO package has its own code, docs, features, license, and standalone Rust library. See the official overview, getting-started guide, and documentation.
How VectorBT PRO works
VBT PRO represents assets and strategy variants as labeled array dimensions through multidimensional broadcasting. This lets a parameter grid remain part of one calculation instead of becoming an outer Python loop over separate backtests. When a rule depends on earlier trades, it can run sequentially inside Numba-compiled callbacks. Supported kernels can also use the optional Rust extension, while Rust programs can call the same algorithms without Python.
Most portfolio workflows progress through four levels:
PF.from_signals()handles entries, exits, conflicts, direction, limit orders, and stops.PF.from_orders()accepts specific order instructions.- Order-function and flexible-order-function simulators run compiled callbacks against changing cash, positions, records, and valuation state.
- Native Rust simulators accept typed strategies for full-array or row-by-row execution without Python.
Parameter exploration and stateful accounting can therefore remain in the same test. A callback can run inside a broadcast parameter space, and the resulting orders, trades, positions, drawdowns, values, and statistics retain their labeled coordinates.
What makes it different
| Capability | What is distinctive | Why it matters |
|---|---|---|
| Universal parameterization | Parameters can become labeled dimensions, with conditions, random subsets, lazy grids, and parameterized decorators | A grid is part of the calculation rather than a Python loop launching unrelated tests |
| Hybrid simulation | Static signal and order arrays sit beside Numba callbacks and typed native Rust strategies | Simple rules stay concise while path-dependent logic can inspect portfolio state |
| Three compute backends | NumPy, Numba, and optional Rust work with chunking and multiple serial, threaded, process, and distributed engines | Work can move between convenient and lower-level paths as its scale changes |
| Portfolio continuation | Portfolio.update() preserves global rows, order IDs, positions, pending execution settings, and stop state |
Incremental work does not need to restart the full history or forget its prior path |
| Batch and streaming parity | Numba and Rust accumulators share formulas with batch indicators, while Rust steppers retain state one row at a time | Incremental behavior can be checked against batch behavior |
| Execution-aware sweeps | Limits, time-in-force, stop ladders, time stops, leverage, contract multipliers, cash flows, fees, and slippage are composable | Large searches can still model decisions that depend on the evolving portfolio |
| Validation factories | Rolling and expanding splits, purging, embargoing, split decorators, and cross-validation factories use the same labeled objects | Robustness work is integrated into the research model |
| Data preparation | More than 20 adapters plus caching, resampling, SQL, Arrow, Parquet, DuckDB, and transformations | Data preparation can use the same labels as the research results |
| Deep analysis | Trades, positions, drawdowns, MAE/MFE, edge ratio, projections, expanding metrics, and more than 200 metrics are available | Results can be investigated far beyond a single score |
| Portfolio construction | Riskfolio-Lib, PyPortfolioOpt, Universal Portfolios, allocation, and range optimizers are integrated | Signal and allocation research do not need separate systems |
| Fill reconstruction | External fill-like records can become a portfolio with the same trade, value, drawdown, and statistics tools | Live or third-party executions can be analyzed without pretending they were simulated there |
| Documentation search | SearchVBT, ChatVBT, QuickSearch, release snapshots, and direct documentation tools cover Python and Rust APIs | Version-matched material is easier to find and verify |
The official catalog currently reports more than 500 indicators, signals, and labels, more than 10 cross-validation factories, 10 optimization factories, eight portfolio-simulation factories, more than 200 metrics, and more than 90 plot types. Counts are only orientation. The real value is that these parts share indexing, broadcasting, parameter, record, and plotting conventions. Explore the official portfolio, optimization, analysis, data, and productivity feature pages for the current catalog.
Strengths
- Research throughput. Broadcasting removes repeated Python orchestration, compiled kernels handle sequential work, chunking controls memory, and execution engines distribute independent calls.
- Execution modeling. Order types and time-in-force, multi-asset cash sharing, position and exposure controls, limits, stop laddering, time stops, and callbacks expose far more state than a simple signal-to-return transform.
- Data and indicators. Local files, SQL, Parquet and Arrow, DuckDB, and remote adapters share one data model.
IndicatorFactoryturns array functions into parameter-aware, cacheable objects, while streaming accumulators cover rolling calculations and widely used indicators. - Validation tooling. Rolling and expanding splitters, walk-forward testing, purged and embargoed cross-validation, and optimization factories sit next to probabilistic and deflated Sharpe ratios.
- Analysis depth. Record-aware accessors expose trades, positions, drawdowns, MAE/MFE, edge ratio, projections, expanding metrics, and benchmark comparisons rather than reducing a run to one performance number.
- Documentation tools. ChatVBT, SearchVBT, QuickSearch, and direct query tools can search versioned documentation, retrieve Python and Rust API material, and run focused checks using hosted or local models.
Limitations
- Paid, proprietary access. Individual and organization rights differ, source access does not permit redistribution, and continued updates require active access under the applicable plan.
- Learning curve. Its real power requires understanding broadcasting, indexing, parameter levels, simulation state, compilation, and the boundary between high-level and kernel APIs.
- Compilation and debugging. Numba and Rust introduce warm-up or build costs. Errors at compiled boundaries can be less direct than ordinary Python, and not every Python callback can dispatch to Rust.
- No bundled data or turnkey broker runtime. Users bring data, models, and execution. Native streaming advances calculations but does not submit, reconcile, or supervise broker orders.
- Information remains finite. A bar does not reveal queue position or the true intrabar path. Historical trades or quotes do not reveal the counterfactual impact of an order that never occurred.
- Search capacity raises overfitting risk. Testing more variants cheaply creates more opportunities for selection bias. Throughput must be paired with chronological validation and complete experiment accounting.
Example: test many parameters and keep their labels
Many frameworks run a Python loop over parameter combinations and return disconnected backtests. VBT PRO can broadcast those combinations into labeled columns, calculate indicators for the grid, simulate every column in one call, and return metrics indexed by the original parameter values.
This complete example uses synthetic prices, so it needs no network connection, credentials, or market-data license. It forms a conditional Cartesian grid and keeps the parameter labels throughout the result.
import numpy as np
import pandas as pd
import vectorbtpro as vbt
index = pd.date_range("2026-01-01", periods=60, freq="D")
close = pd.Series(
100
+ np.linspace(0, 6, len(index))
+ 5 * np.sin(np.linspace(0, 8 * np.pi, len(index))),
index=index,
name="close",
)
param_grid, _ = vbt.combine_params(
{
"fast": vbt.Param([2, 3], name="fast"),
"slow": vbt.Param([4, 5], name="slow", condition="fast < slow"),
}
)
fast = vbt.MA.run(close, param_grid["fast"], short_name="fast")
slow = vbt.MA.run(close, param_grid["slow"], short_name="slow")
entries = fast.ma.vbt.crossed_above(slow.ma)
exits = fast.ma.vbt.crossed_below(slow.ma)
portfolio = vbt.PF.from_signals(
close,
entries,
exits,
init_cash=10_000,
fees=0.001,
slippage=0.0005,
)
summary = pd.DataFrame(
{
"trades": portfolio.trades.count(),
"total_return_pct": portfolio.total_return * 100,
}
)
print(summary.round(2).to_string())
Verified with VBT PRO 2026.9.5:
trades total_return_pct
fast_window slow_window
2 4 4 27.13
5 4 20.99
3 4 4 20.99
5 4 16.72
The deliberately rising, oscillating series makes positive returns unsurprising. The run verifies parameter broadcasting, indicator alignment, simulation, explicit fees and slippage, and labeled output. It does not demonstrate an edge. A real study should retain every attempted variant, validate chronologically, and report the search process rather than only its winner.
Example: continue a portfolio with new data
Release 2026.9.5 can continue the major portfolio simulations as new rows arrive. Portfolio.update() preserves global row coordinates, cumulative record identifiers, positions, pending execution settings, and stop state. This example begins with two rows, adds two chunks, and confirms that the resulting orders equal a one-shot simulation.
import pandas as pd
import vectorbtpro as vbt
close = pd.Series(
[100.0, 110.0, 108.0, 98.0, 103.0, 115.0],
index=pd.date_range("2026-09-01", periods=6, freq="D"),
)
entries = pd.Series(
[True, False, False, False, True, False],
index=close.index,
)
settings = dict(size=1, init_cash=1000, tsl_stop=0.05, freq="1D")
continued = vbt.PF.from_signals(
close.iloc[:2],
entries=entries.iloc[:2],
attach_preparer=True,
**settings,
)
for start in (2, 4):
continued = continued.update(
close.iloc[start : start + 2],
entries=entries.iloc[start : start + 2],
)
one_shot = vbt.PF.from_signals(close, entries=entries, **settings)
columns = ["Fill Index", "Side", "Price", "Stop Type"]
print(continued.orders.readable[columns].to_string(index=False))
print("Matches one-shot:", continued.orders.records.equals(one_shot.orders.records))
Verified output:
Fill Index Side Price Stop Type
2026-09-01 Buy 100.0 None
2026-09-04 Sell 98.0 TSL
2026-09-05 Buy 103.0 None
Matches one-shot: True
The 5% trailing stop remembers the 110 peak from the first chunk and exits when the later close reaches 98. Continuation avoids both recomputing a growing history and incorrectly treating each new chunk as an independent simulation. It is not order routing: update() advances simulated state, while live submission, acknowledgements, partial fills, cancellation, reconciliation, recovery, and production risk controls remain separate responsibilities.
Example: compare parameters across time periods
The example below is the original large-scale test supplied for this page. It runs a conditional SMA crossover grid separately by calendar year, distributes chunks through pathos, concatenates labeled Sharpe ratios, and turns them into an interactive heatmap. The 190 by 190 candidate grid contains 17,955 valid fast/slow pairs before the yearly splits, so the combined calculation can exceed 100,000 strategy-period evaluations.
Runtime depends on the exchange response, cached data, CPU, process startup, compilation state, output retention, and available memory. An earlier warmed run was reported at under 10 seconds, but this review did not reproduce that timing because it would require a current CCXT download and the same hardware and cache state. Treat it as a workflow example, then benchmark the exact dataset and machine you intend to use.
import pandas as pd
import vectorbtpro as vbt
def backtest(data, fast, slow, param_index):
sma_fast = data.run("talib:sma", fast, short_name="fast")
sma_slow = data.run("talib:sma", slow, short_name="slow")
pf = vbt.PF.from_signals(
data,
sma_fast.crossed_above(sma_slow),
sma_fast.crossed_below(sma_slow),
fees=0.0005,
slippage=0.0005
)
return pd.Series(pf.sharpe_ratio.values, index=param_index)
data = vbt.CCXTData.pull("BTCUSDT", start="2020", end="2026", timeframe="4h", cache=True)
years = data.index.year
splitter = vbt.Splitter.from_grouper(data.index, years, split_labels=years.unique().rename("year"))
param_grid, param_index = vbt.combine_params(dict(
fast=vbt.Param(range(10, 200)),
slow=vbt.Param(range(10, 200), condition="fast < slow")
))
sharpe_ratios = data.split_apply(
backtest, param_grid["fast"], param_grid["slow"],
splitter=splitter,
execute_kwargs=dict(chunk_len="auto", engine="pathos"),
param_index=param_index,
merge_func="concat"
)
sharpe_ratios.vbt.heatmap(
x_level="fast", y_level="slow", slider_level="year", symmetric=True,
trace_kwargs=dict(zmid=0, colorscale="Spectral", colorbar=dict(title="Sharpe")),
title="BTC SMA Crossover Heatmap by Year", template="plotly_dark"
).show()
This example was contributed by the VectorBT project.
The example intentionally shows more than choosing the maximum cell. The slider exposes whether a parameter region persists across years. A serious analysis should also reserve untouched data, include every explored variant in its multiple-testing accounting, widen cost assumptions, and inspect stability across neighboring cells. Cheap search capacity increases the importance of parameter robustness.
The Rust crate: Python is optional
vectorbtpro-rust is a standalone Rust compute library, not merely a hidden accelerator for the Python package. Cargo builds an rlib for native programs and a cdylib for the optional PyO3 extension. The python feature controls the PyO3 and NumPy bindings, so an ordinary Rust program can use ndarray inputs, typed VbtError errors, Rayon parallel paths, and builder-based functions without installing or embedding Python.
[dependencies]
ndarray = "0.16"
vectorbtpro-rust = { git = "https://github.com/polakowo/vectorbt.pro.git", tag = "2026.9.5" }
The repository is private and requires an entitled GitHub account. The crate is not currently published on crates.io. A local checkout can instead use vectorbtpro-rust = { path = "../vectorbt.pro/rust" }. When used as the Python extension, its version must match the main VBT PRO package.
Native functions own the actual algorithms. Python wrappers prepare Python and NumPy inputs, call those functions, and convert results back. Public Rust builders mirror their Numba counterparts in argument order, defaults, validation, enums, record types, and serial or parallel dispatch, with contract and parity tests covering the supported surface. Modules span base operations, data generation, generic reductions, indicators, labels, OHLCV operations, portfolio simulation, records, returns, signals, and utilities.
This native example generates a deterministic price path, calculates returns, and reduces them to an annualized Sharpe ratio without a Python runtime:
use ndarray::Array1;
use vectorbtpro_rust::data::generate_random_data_1d;
use vectorbtpro_rust::returns::{returns_1d, sharpe_ratio_1d};
fn main() {
let prices = Array1::from_vec(
generate_random_data_1d()
.n_rows(252)
.start_value(100.0)
.mean(0.0005)
.std(0.01)
.seed(42)
.call(),
);
let returns = Array1::from_vec(returns_1d().arr(prices.view()).call());
let sharpe = sharpe_ratio_1d()
.returns(returns.view())
.ann_factor(252.0)
.call();
println!("{sharpe:.6}");
assert!(sharpe.is_finite());
}
Compiled and executed against the 2026.9.5 crate without the python feature, it prints:
-0.017308
The generated series is a deterministic API check, not a market forecast or performance claim.
"Matches Numba" needs one precise qualification. The crate mirrors the supported algorithm contracts, but an arbitrary Python callback cannot cross into native Rust. Dynamic Rust simulation uses a typed Rust strategy or closure rather than an ABI-compatible Python callback. In Python, compatible calls prefer the matching Rust extension and retain a Numba fallback when native execution is unavailable or an input is unsupported. This is broad, tested structural parity, not a claim that every possible Python object runs in Rust.
For Rust users, the direct API goes beyond indicator kernels:
OrderSimulator,FlexOrderSimulator, andSignalSimulatorrun typed strategies over complete arrays.OrderStepper,FlexOrderStepper, andSignalStepperprocess one timestamped row at a time while retaining positions, cash, stops, limits, records, and strategy state.- The optional
serdefeature provides bounded checkpoints that can resume a stream exactly. - NPY record and NPZ simulation persistence can exchange native results with Python analysis.
- Streaming indicators own their rolling buffers and use the same per-observation formulas as batch calculations.
- Native callback engines cover mapping, rolling and grouped reductions, allocation, range optimization, signal generation, and ranking.
This lets a Rust program manage its data loop and simulation state without a Python runtime, then export records for higher-level VBT analysis. Typed builders, compiler feedback, Rust API pages, and version-matched documentation make an implementation easier to verify. They do not remove the need for tests, parity checks, data controls, or review.
Native steppers handle streaming calculations, but they are not a complete live-trading application. They do not connect to a broker, route orders, reconcile external fills, or supervise live trading. The official Rust documentation covers native setup, API structure, simulators, streaming, and parity, while the productivity feature guide shows backend selection and native examples.
Validation, data, and analysis
VBT PRO's Splitter and cross-validation factories support rolling, expanding, random, grouped, purged, and embargoed schemes. Decorators can parameterize functions and apply them over splits, while lazy grids and random subsets control combinatorial growth. Use those capabilities to ask whether behavior persists across nearby parameters, assets, regimes, and chronological test windows, not only which configuration maximizes a score.
Purging prevents a specific form of label overlap. It does not repair bad timestamps, survivorship bias, revised data, leakage during feature fitting, repeated researcher discretion, or an optimistic fill model. Record raw data or immutable extracts, adjustment rules, time zones, symbol mappings, package versions, and the complete experiment history. The optimization feature guide documents the available machinery.
The data layer integrates local files, SQL, Parquet and Arrow, DuckDB, and remote providers such as CCXT, Alpaca, Alpha Vantage, Polygon, TradingView, and Databento. Availability varies by provider and release. VBT PRO does not bundle provider entitlements or guarantee historical-data quality. Its indicator factories and streaming accumulators cover a large catalog, but custom functions can use the same parameter, caching, and plotting conventions. See the data and indicator feature guides.
Analysis operates on simulation records rather than only an equity curve. Trade and position views, expanding metrics, MAE/MFE, edge ratio, entry and exit signals, projections, drawdowns, and benchmark comparisons help explain why a result occurred. VBT PRO can also construct a portfolio from fill-like records, which is useful for comparing external executions with modeled behavior without relabeling those fills as a backtest.
Performance claims need context
Broadcasting reduces interpreter work, Numba compiles sequential kernels, Rust supplies native serial and Rayon paths, chunking bounds memory, and execution engines distribute independent jobs. The official site publishes interactive benchmarks with first-call and warmed timings plus downloadable materials.
No single benchmark proves universal speed. JIT compilation makes cold and warm runs different. Broadcasting can trade runtime for memory. Serialization can dominate process execution. Parallel work may lose to serial work on small arrays, and Rust cannot accelerate provider calls or pandas preparation around a kernel. Benchmark the actual shapes, dtypes, callbacks, retained outputs, hardware, and warm-up policy used in your research.
Membership and license
At the time of review, individual access starts at $25 monthly, $240 for 12 months, or $500 for lifetime access. Prices and terms can change, so use the current membership page when making a purchase decision.
Individual membership is for personal, noncommercial use. Commercial or organizational use requires the applicable organization agreement. The software license controls source use, deployment, and redistribution. Source access does not make the package open source.
If time-limited access expires, the last installed version continues to work under its license, but access to new releases, fixes, private documentation, and support ends. Pin matching Python and Rust releases, preserve permitted installation artifacts securely, and plan how a long-lived system will receive compatibility and security updates.
Bottom line
VectorBT PRO supports research across many settings, stateful portfolio rules, time-aware validation, portfolio continuation, trade analysis, and native Rust. These parts share the same labels and records, so the work can become more detailed without moving to a different tool. Sound tests, realistic data, and separate controls for live trading are still required.