Rajandran R Creator of OpenAlgo - OpenSource Algo Trading framework for Indian Traders. Telecom Engineer turned Full-time Derivative Trader. Mostly Trading Nifty, Banknifty, High Liquid Stock Derivatives. Trading the Markets Since 2006 onwards. Using Market Profile and Orderflow for more than a decade. Designed and published 100+ open source trading systems on various trading tools. Strongly believe that market understanding and robust trading frameworks are the key to the trading success. Building Algo Platforms, Writing about Markets, Trading System Design, Market Sentiment, Trading Softwares & Trading Nuances since 2007 onwards. Author of Marketcalls.in

Mastering VectorBT Backtesting and Optimization – Part 1 – Python Tutorial

5 min read

Who Should Use VectorBT?

VectorBT is an advanced backtesting library designed for traders and quantitative analysts. It is particularly beneficial for those who require a fast, flexible, and powerful tool for backtesting trading strategies. Its users typically include:

  • Quantitative traders who need to test and optimize complex trading strategies.
  • Data scientists seeking to incorporate financial data analysis into their workflows.
  • Algorithmic traders looking for a library that can handle large datasets

Speed Comparison with Other Backtesting Libraries

VectorBT is known for its high-speed performance, primarily due to its vectorized operations and the use of Numba, a JIT compiler that translates Python functions into optimized machine code. This makes it significantly faster than many other backtesting libraries, especially when dealing with large datasets.

When compared to other Python backtesting libraries like Backtrader or PyAlgoTrade, VectorBT can perform backtests significantly faster, especially for large datasets or complex strategies.

  1. Backtrader: Popular for its simplicity and community support.
  2. PyAlgoTrade: Known for its flexibility and support for multiple data sources.
  3. Zipline: Used mainly for its integration with Quantopian for strategy development.
  4. QuantConnect: Offers cloud-based backtesting with a wide range of data.
  5. bt: Focuses on strategy combination and portfolio management.

The integration of Numba into VectorBT results in a performance that is orders of magnitude faster than traditional Python backtesting libraries. This speed is crucial in quantitative finance, where analyzing massive datasets and iterating over numerous strategies and parameters is the norm. It allows traders and analysts to test more hypotheses in less time, leading to more refined and tested trading strategies.

VectorBT supports multiprocessing, enabling the parallel processing of data and speeding up computations, especially useful for strategy optimization and parameter scanning.

Installing the VectorBT Library

VectorBT library supports Python 3.6 – 3.10 version and currently, it doesn’t support Python 3.11 at this moment and it is expected to support from 3.12 onwards. Here is the pip command to install the library and its dependencies. The entire VectorBT Backtesting and Optimization is tested using Google Colab

pip install vectorBT

However if in case you are using Python 3.11 or higher version and would like to install then here is the tutorial on Installing VectorBT in a Virtual environment

Simple EMA Crossover Backtesting with VectorBT

The following Python code fetches data from Yahoo Finance and a simple 10 and 20 EMA crossover strategy is applied with an Initial capital of Rs1,00,000/- with position sizing as 50% of the current equity and 0.01% as fixed trading commissions.


import vectorbt as vbt
import numpy as np
import pandas as pd

# Fetch historical data for HDFCBANK using VectorBT
hdfc_data = vbt.YFData.download('HDFCBANK.NS', start='2010-01-01', end='2023-01-01').get('Close')

# Calculate 10 and 20 days exponential moving averages
short_ema = vbt.MA.run(hdfc_data, 10, short_name='fast', ewm=True)
long_ema = vbt.MA.run(hdfc_data, 20, short_name='slow', ewm=True)


# Generate crossover signals
entries = short_ema.ma_crossed_above(long_ema)
exits = short_ema.ma_crossed_below(long_ema)

# Set initial capital
initial_capital = 100000

# Create and run the portfolio with 50% of equity position sizing
portfolio = vbt.Portfolio.from_signals(
    hdfc_data, entries, exits,
    direction = 'longonly',
    size=0.5,  # 50% of current equity
    size_type='percent',  # Use percent of equity for sizing
    fees=0.001,
    init_cash=initial_capital,
    freq='1D',
    min_size =1,
    size_granularity = 1
)

# Retrieve and print the backtesting stats
stats = portfolio.stats()
print("Backtesting Stats:")
print(stats)

Backtesting Metrics Output of the EMA Crossover Strategy

Backtesting Stats:
Start                          2010-01-03 18:30:00+00:00
End                            2022-12-29 18:30:00+00:00
Period                                3209 days 00:00:00
Start Value                                     100000.0
End Value                                  184053.173875
Total Return [%]                               84.053174
Benchmark Return [%]                          945.324187
Max Gross Exposure [%]                         60.705702
Total Fees Paid                              8736.891845
Max Drawdown [%]                               19.285277
Max Drawdown Duration                 1051 days 00:00:00
Total Trades                                          64
Total Closed Trades                                   63
Total Open Trades                                      1
Open Trade PnL                               9825.657568
Win Rate [%]                                    39.68254
Best Trade [%]                                 50.972884
Worst Trade [%]                               -10.359501
Avg Winning Trade [%]                           10.54576
Avg Losing Trade [%]                           -3.612213
Avg Winning Trade Duration              65 days 05:45:36
Avg Losing Trade Duration     10 days 11:22:06.315789473
Profit Factor                                   1.777249
Expectancy                                   1178.214545
Sharpe Ratio                                      0.7258
Calmar Ratio                                    0.372581
Omega Ratio                                      1.13629
Sortino Ratio                                   1.098154
dtype: object

Accessing Trade Details

# Accessing trade details
trades = portfolio.trades.records_readable
print("\nTrade Details:")
trades

Output

Trade Details:
Exit Trade Id	Column	Size	Entry Timestamp	Avg Entry Price	Entry Fees	Exit Timestamp	Avg Exit Price	Exit Fees	PnL	Return	Direction	Status	Position Id
0	0	0	326.0	2010-02-21 18:30:00+00:00	153.168030	49.932778	2010-05-06 18:30:00+00:00	166.840836	54.390112	4353.011796	0.087177	Long	Closed	0
1	1	0	292.0	2010-05-11 18:30:00+00:00	178.292801	52.061498	2010-05-17 18:30:00+00:00	171.151672	49.976288	-2187.247320	-0.042013	Long	Closed	1
2	2	0	288.0	2010-06-10 18:30:00+00:00	176.839020	50.929638	2010-07-04 18:30:00+00:00	174.013275	50.115823	-914.859914	-0.017963	Long	Closed	2
3	3	0	283.0	2010-07-05 18:30:00+00:00	178.447205	50.500559	2010-10-19 18:30:00+00:00	212.205887	60.054266	9443.152252	0.186991	Long	Closed	3
4	4	0	253.0	2010-11-09 18:30:00+00:00	217.684738	55.074239	2010-11-11 18:30:00+00:00	210.329651	53.213402	-1969.124722	-0.035754	Long	Closed	4
...	...	...	...	...	...	...	...	...	...	...	...	...	...	...
59	59	0	64.0	2022-02-15 18:30:00+00:00	1481.340942	94.805820	2022-02-23 18:30:00+00:00	1387.178345	88.779414	-6209.991484	-0.065502	Long	Closed	59
60	60	0	63.0	2022-03-21 18:30:00+00:00	1460.231445	91.994581	2022-04-18 18:30:00+00:00	1311.730713	82.639035	-9530.179759	-0.103595	Long	Closed	60
61	61	0	63.0	2022-05-29 18:30:00+00:00	1385.659424	87.296544	2022-06-13 18:30:00+00:00	1297.124756	81.718860	-5746.699485	-0.065830	Long	Closed	61
62	62	0	63.0	2022-06-29 18:30:00+00:00	1332.716553	83.961143	2022-09-25 18:30:00+00:00	1410.474854	88.859916	4725.951891	0.056287	Long	Closed	62
63	63	0	60.0	2022-10-23 18:30:00+00:00	1444.484863	86.669092	2022-12-29 18:30:00+00:00	1609.690308	0.000000	9825.657568	0.113370	Long	Open	63
64 rows × 14 columns

VectorBT doesn’t just stop at backtesting; it integrates with Plotly and ipywidgets for advanced data visualization. This integration enables the creation of complex charts and interactive dashboards directly in Jupyter notebooks. This means that the plots are interactive, allowing for zooming, panning, and viewing data points in detail.

Plotting Equity Curve and Drawdown Curve

import plotly.graph_objs as go

# Extracting equity and drawdown data
equity_data = portfolio.value()
drawdown_data = portfolio.drawdown()*100

# Plotting the equity curve with Plotly
equity_trace = go.Scatter(x=equity_data.index, y=equity_data, mode='lines', name='Equity Curve')
equity_layout = go.Layout(title='Equity Curve', xaxis_title='Date', yaxis_title='Equity')
equity_fig = go.Figure(data=[equity_trace], layout=equity_layout)
equity_fig.show()

# Plotting the drawdown curve as a reddish-brown area plot with Plotly
drawdown_trace = go.Scatter(
    x=drawdown_data.index,
    y=drawdown_data,
    mode='lines',
    name='Drawdown Curve',
    fill='tozeroy',
    line=dict(color='brown')
)
drawdown_layout = go.Layout(
    title='Drawdown Curve',
    xaxis_title='Date',
    yaxis_title='Drawdown %',
    template='plotly_white'
)
drawdown_fig = go.Figure(data=[drawdown_trace], layout=drawdown_layout)
drawdown_fig.show()

Equity Curve

Drawdown Curve

portfolio.plot().show() in VectorBT is used to generate and display a graphical representation of a trading strategy’s performance, utilizing the capabilities of Plotly for interactive and detailed visualizations.

portfolio.plot().show()

VectorBT Position Sizing Methods

  1. Fixed Size: This is the simplest form of position sizing. A fixed number of shares or contracts are traded regardless of the account size or other factors. In VectorBT, this can be specified using the size parameter in the from_signals or from_orders method.
  2. Percent of Equity: In this method, the size of the position is a fixed percentage of the current account equity. This means that as the account grows, the size of each trade in terms of the number of shares or contracts also grows. This is useful for compounding growth. In VectorBT, this is achieved using the size_type='percent' parameter, combined with the size parameter representing the percentage.
  3. Target Percent of Equity: Similar to Percent of Equity, but here the size is calculated to adjust the portfolio to a target percentage allocation for each asset. For instance, if you wish to maintain a certain asset at 30% of your portfolio, the sizing will automatically adjust to achieve this target.
  4. Fixed Cash Amount: This method involves allocating a fixed cash amount for each trade. No matter the price of the asset, the same amount of money is invested. In VectorBT, this can be set using size_type='value', where size would be the cash amount per trade.

EMA Crossover Optimization with VectorBT

Optimizing the EMA crossover strategy involves testing various combinations of short and long EMA spans:

import vectorbt as vbt
import numpy as np
import pandas as pd

# Fetch data from yahoo finance
hdfc_data = vbt.YFData.download('HDFCBANK.NS', start='2010-01-01', end='2023-01-01').get('Close')

# Parameter grid
short_spans = np.arange(5, 15, 1)  # Test spans from 5 to 14
long_spans = np.arange(15, 30, 1)  # Test spans from 15 to 29

# Backtesting with different EMA spans
results = []
for short_span in short_spans:
    for long_span in long_spans:
        # Calculate 10 and 20 days exponential moving averages
        short_ema = vbt.MA.run(hdfc_data, short_span, short_name='fast', ewm=True)
        long_ema = vbt.MA.run(hdfc_data, long_span, short_name='slow', ewm=True)

        # Generate crossover signals
        entries = short_ema.ma_crossed_above(long_ema)
        exits = short_ema.ma_crossed_below(long_ema)
        portfolio = vbt.Portfolio.from_signals(
            hdfc_data, entries, exits,
            size=0.5, size_type='percent', fees=0.001,
            init_cash=100000,freq='1D',
            min_size =1, size_granularity = 1
        )
        results.append((short_span, long_span, portfolio.total_return()))

# Find the best performing parameter set
best_params = max(results, key=lambda x: x[2])
print("Best Parameters:", best_params)

Output

Best Parameters: (14, 24, 1.1464926467324832)

VectorBT emerges as a powerful tool for backtesting and optimizing trading strategies in Python. Its speed and flexibility make it a superior choice for quantitative analysts and algorithmic traders. Through this guide, we explored a basic EMA Crossover strategy, visualized its performance, and performed optimization to find the best parameters. In the next part, we will delve deeper into the advanced features of VectorBT. Stay tuned to further enhance your trading strategy development skills with VectorBT.

In the next tutorial, we will be exploring Portfolio Backtesting and Rebalancing using VectorBT

Rajandran R Creator of OpenAlgo - OpenSource Algo Trading framework for Indian Traders. Telecom Engineer turned Full-time Derivative Trader. Mostly Trading Nifty, Banknifty, High Liquid Stock Derivatives. Trading the Markets Since 2006 onwards. Using Market Profile and Orderflow for more than a decade. Designed and published 100+ open source trading systems on various trading tools. Strongly believe that market understanding and robust trading frameworks are the key to the trading success. Building Algo Platforms, Writing about Markets, Trading System Design, Market Sentiment, Trading Softwares & Trading Nuances since 2007 onwards. Author of Marketcalls.in

Voice Commands to Trade on OpenAlgo Platform Using Google…

Trading platforms are always getting better by using the newest technologies to make things easier and more efficient for users. A great example of...
Rajandran R
5 min read

[Live Coding Webinar] Build Your First Trading Bridge for…

In this course, you will be learning to build your own trading bridge using Python. This 60-minute session is perfect for traders, Python enthusiasts,...
Rajandran R
1 min read

How to Place Orders Concurrently using ThreadPoolExecutor – Python…

Creating concurrent orders is essential for active traders, especially those handling large funds, as it allows for executing multiple trade orders simultaneously, thereby maximizing...
Rajandran R
2 min read

Leave a Reply

Get Notifications, Alerts on Market Updates, Trading Tools, Automation & More