Getting Started with VectorBT

Welcome to VectorBT - the most powerful backtesting framework for Indian stock markets. This module will take you from zero to your first profitable strategy on NSE stocks.

What You'll Learn

  • Install and configure VectorBT
  • Download NSE/BSE stock data
  • Create your first strategy
  • Analyze performance metrics

Prerequisites

  • Python 3.8 or higher
  • Basic Python knowledge
  • Understanding of stock markets
  • Jupyter Notebook (optional)

Why VectorBT?

Lightning Fast Performance

Test thousands of parameter combinations on entire NIFTY 50 in seconds, not hours.

Comprehensive Metrics

Get 50+ performance metrics including Sharpe ratio, maximum drawdown, and Indian market-specific calculations.

Pythonic & Intuitive

Works seamlessly with pandas, NumPy, and other data science libraries you already know.

Installation & Setup

Step 1: Create Virtual Environment

It's recommended to use a virtual environment to avoid package conflicts:

# Create virtual environment
python -m venv vbt_env

# Activate on Windows
vbt_env\Scripts\activate

# Activate on Mac/Linux
source vbt_env/bin/activate

Step 2: Install VectorBT

Install VectorBT with all optional dependencies for full functionality:

# Install VectorBT with all features
pip install vectorbt[full]

# Or install specific components
pip install vectorbt  # Core library
pip install vectorbt[data]  # Data download features
pip install vectorbt[plotting]  # Visualization features

Step 3: Install Indian Market Libraries

Additional libraries for working with Indian stock data:

# Yahoo Finance for NSE/BSE data
pip install yfinance

# NSEPy for NSE specific data
pip install nsepy

# Technical indicators
pip install ta-lib  # Requires C++ libraries
pip install pandas-ta  # Pure Python alternative

# Visualization
pip install plotly
pip install matplotlib

Pro Tip

If you face issues installing TA-Lib on Windows, download the pre-compiled wheel from here and install using pip.

Step 4: Verify Installation

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

print(f"VectorBT version: {vbt.__version__}")
print(f"Pandas version: {pd.__version__}")
print(f"NumPy version: {np.__version__}")
print("Installation successful! 🎉")

Indian Market Data Sources

Yahoo Finance (Recommended)

Yahoo Finance provides free historical data for NSE and BSE stocks. Add '.NS' for NSE and '.BO' for BSE:

import vectorbt as vbt

# Download single stock
reliance = vbt.YFData.download(
    "RELIANCE.NS",
    start="2023-01-01",
    end="2024-01-01"
)

# Download multiple stocks
stocks = ["RELIANCE.NS", "TCS.NS", "HDFCBANK.NS", "INFY.NS"]
data = vbt.YFData.download(
    stocks,
    start="2023-01-01",
    end="2024-01-01"
)

# Download index data
nifty = vbt.YFData.download(
    "^NSEI",  # NIFTY 50
    start="2023-01-01",
    end="2024-01-01"
)

# Bank NIFTY
bank_nifty = vbt.YFData.download(
    "^NSEBANK",
    start="2023-01-01",
    end="2024-01-01"
)

NSEPy Library

NSEPy provides more detailed NSE data including F&O chain:

from nsepy import get_history
from datetime import date

# Get stock data
sbin = get_history(
    symbol="SBIN",
    start=date(2023,1,1),
    end=date(2024,1,1)
)

# Get index data
nifty = get_history(
    symbol="NIFTY",
    start=date(2023,1,1),
    end=date(2024,1,1),
    index=True
)

# Get F&O data
sbin_fut = get_history(
    symbol="SBIN",
    start=date(2023,1,1),
    end=date(2024,1,1),
    futures=True,
    expiry_date=date(2024,1,25)
)

Data Quality Note

Always verify data quality, especially for splits and bonuses. Indian stocks often have corporate actions that may not be properly adjusted in free data sources.

Loading Data from CSV Files

You can also load historical data from CSV files stored locally:

import pandas as pd
import vectorbt as vbt

# Load data from CSV file
# Sample data available in data/samples/ folder
reliance_df = pd.read_csv('data/samples/RELIANCE_sample.csv', 
                          parse_dates=['Date'], 
                          index_col='Date')

# Convert to VectorBT format
data = vbt.Data.from_data(reliance_df)
close = data.get('Close')

# Load NIFTY index data
nifty_df = pd.read_csv('data/indices/NIFTY.csv',
                       parse_dates=['DATE'],
                       index_col='DATE')

# Load multiple stocks data
stocks_info = pd.read_csv('data/samples/sample_stocks.csv')
print(f"Available stocks: {stocks_info['Symbol'].tolist()}")

# For intraday data with date and time
intraday_df = pd.read_csv('data/indices/NIFTY.csv')
intraday_df['DATETIME'] = pd.to_datetime(intraday_df['DATE'] + ' ' + intraday_df['TIME'])
intraday_df.set_index('DATETIME', inplace=True)

Common NSE/BSE Symbols

Indices

  • ^NSEI - NIFTY 50
  • ^NSEBANK - Bank NIFTY
  • ^NSMIDCP - NIFTY Midcap
  • ^BSESN - SENSEX

Top Stocks

  • RELIANCE.NS - Reliance Industries
  • TCS.NS - Tata Consultancy Services
  • HDFCBANK.NS - HDFC Bank
  • INFY.NS - Infosys

Your First Backtest

Let's create a simple moving average crossover strategy on RELIANCE:

Complete Code Example

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

# Step 1: Download RELIANCE data
print("Downloading RELIANCE data...")
data = vbt.YFData.download(
    "RELIANCE.NS",
    start="2022-01-01",
    end="2024-01-01",
    missing_index='drop'  # Remove any missing data
)

# Get the closing prices
close = data.get('Close')
print(f"Data shape: {close.shape}")
print(f"Date range: {close.index[0]} to {close.index[-1]}")

# Step 2: Calculate Moving Averages
fast_ma = vbt.MA.run(close, window=10, short_name='SMA_10')
slow_ma = vbt.MA.run(close, window=30, short_name='SMA_30')

# Step 3: Generate Trading Signals
entries = fast_ma.ma_crossed_above(slow_ma)  # Buy signal
exits = fast_ma.ma_crossed_below(slow_ma)    # Sell signal

print(f"Total buy signals: {entries.sum()}")
print(f"Total sell signals: {exits.sum()}")

# Step 4: Run Backtest
portfolio = vbt.Portfolio.from_signals(
    close,
    entries,
    exits,
    init_cash=10_00_000,  # ₹10 Lakhs initial capital
    fees=0.0003,  # 0.03% transaction cost (typical for Indian markets)
    slippage=0.0005,  # 0.05% slippage
    freq='1D'  # Daily frequency
)

# Step 5: View Results
print("\n" + "="*50)
print("BACKTEST RESULTS")
print("="*50)
print(portfolio.stats())

# Key Metrics
print("\n" + "="*50)
print("KEY PERFORMANCE METRICS")
print("="*50)
print(f"Total Return: {portfolio.total_return():.2%}")
print(f"Annual Return: {portfolio.annualized_return():.2%}")
print(f"Sharpe Ratio: {portfolio.sharpe_ratio():.2f}")
print(f"Max Drawdown: {portfolio.max_drawdown():.2%}")
print(f"Win Rate: {portfolio.trades.win_rate():.2%}")
print(f"Total Trades: {portfolio.trades.count()}")

Understanding the Results

Positive Indicators

  • ✅ Positive total return
  • ✅ Sharpe ratio > 1.0
  • ✅ Win rate > 50%
  • ✅ Low maximum drawdown < 20%

Warning Signs

  • ❌ Negative returns
  • ❌ Sharpe ratio < 0.5
  • ❌ Very few trades (< 10)
  • ❌ High drawdown > 30%

NIFTY 50 Portfolio Example

Let's create a momentum strategy on top NIFTY 50 stocks:

# Top 10 NIFTY 50 stocks by weight
nifty_stocks = [
    "RELIANCE.NS", "HDFCBANK.NS", "INFY.NS", "ICICIBANK.NS",
    "TCS.NS", "KOTAKBANK.NS", "LT.NS", "HINDUNILVR.NS",
    "AXISBANK.NS", "ITC.NS"
]

# Download data for all stocks
print("Downloading NIFTY 50 stocks data...")
data = vbt.YFData.download(
    nifty_stocks,
    start="2022-01-01",
    end="2024-01-01",
    missing_index='drop'
)

close = data.get('Close')

# Calculate 20-day momentum (Rate of Change)
roc = vbt.ROC.run(close, window=20)

# Rank stocks by momentum
def get_top_stocks(roc_values, n=3):
    """Select top N stocks by momentum"""
    rankings = roc_values.rank(axis=1, ascending=False)
    return rankings <= n

# Generate signals: Buy top 3 momentum stocks
entries = get_top_stocks(roc.roc, n=3)

# Hold for 20 days
exits = entries.shift(20).fillna(False)

# Run backtest with equal weights
portfolio = vbt.Portfolio.from_signals(
    close,
    entries,
    exits,
    init_cash=50_00_000,  # ₹50 Lakhs
    fees=0.0003,
    slippage=0.0005,
    freq='1D',
    group_by=True,  # Treat as single portfolio
    cash_sharing=True,  # Share cash between assets
    size=1/3,  # Equal weight (1/3 for each of 3 stocks)
    size_type='targetpercent'
)

# Results
print("\nNIFTY 50 Momentum Strategy Results:")
print("="*50)
print(f"Total Return: {portfolio.total_return():.2%}")
print(f"Annual Return: {portfolio.annualized_return():.2%}")
print(f"Sharpe Ratio: {portfolio.sharpe_ratio():.2f}")
print(f"Max Drawdown: {portfolio.max_drawdown():.2%}")

# Compare with buy-and-hold NIFTY
nifty_index = vbt.YFData.download("^NSEI", start="2022-01-01", end="2024-01-01")
nifty_return = (nifty_index.get('Close').iloc[-1] / nifty_index.get('Close').iloc[0] - 1)
print(f"\nNIFTY 50 Buy & Hold Return: {nifty_return:.2%}")
print(f"Strategy Alpha: {portfolio.total_return() - nifty_return:.2%}")

Understanding Performance Metrics

Essential Metrics for Indian Markets

Returns Metrics

Total Return
Overall profit/loss percentage
CAGR
Compound Annual Growth Rate
Annualized Return
Yearly return normalized

Risk Metrics

Sharpe Ratio
Risk-adjusted returns (> 1 is good)
Max Drawdown
Largest peak-to-trough decline
Volatility
Standard deviation of returns

Accessing All Metrics

# Get all statistics
stats = portfolio.stats()
print(stats)

# Access specific metrics
print(f"Total Return: {portfolio.total_return():.2%}")
print(f"Sharpe Ratio: {portfolio.sharpe_ratio():.2f}")
print(f"Sortino Ratio: {portfolio.sortino_ratio():.2f}")
print(f"Calmar Ratio: {portfolio.calmar_ratio():.2f}")
print(f"Max Drawdown: {portfolio.max_drawdown():.2%}")
print(f"Annual Volatility: {portfolio.annualized_volatility():.2%}")

# Trade statistics
trades = portfolio.trades.records_readable
print(f"\nTotal Trades: {len(trades)}")
print(f"Win Rate: {portfolio.trades.win_rate():.2%}")
print(f"Average Win: {portfolio.trades.avg_winning_trade():.2%}")
print(f"Average Loss: {portfolio.trades.avg_losing_trade():.2%}")
print(f"Profit Factor: {portfolio.trades.profit_factor():.2f}")

# Custom metric for Indian markets (considering STT)
stt_cost = len(trades) * 0.001 * portfolio.value().iloc[-1]  # 0.1% STT
net_return = portfolio.total_return() - (stt_cost / portfolio.init_cash)
print(f"\nReturn after STT: {net_return:.2%}")

Visualization & Analysis

Interactive Plots

# 1. Complete portfolio plot
fig = portfolio.plot()
fig.show()

# 2. Individual component plots
# Cumulative returns
portfolio.plot_cumulative_returns().show()

# Drawdown
portfolio.plot_drawdowns().show()

# Underwater plot (time spent in drawdown)
portfolio.plot_underwater().show()

# 3. Trade analysis
portfolio.trades.plot().show()
portfolio.trades.plot_pnl().show()

# 4. Custom plot with entry/exit points
import plotly.graph_objects as go

fig = go.Figure()

# Add price line
fig.add_trace(go.Scatter(
    x=close.index,
    y=close.values,
    mode='lines',
    name='Price',
    line=dict(color='blue', width=1)
))

# Add entry points
entry_points = close[entries]
fig.add_trace(go.Scatter(
    x=entry_points.index,
    y=entry_points.values,
    mode='markers',
    name='Buy',
    marker=dict(
        color='green',
        size=10,
        symbol='triangle-up'
    )
))

# Add exit points
exit_points = close[exits]
fig.add_trace(go.Scatter(
    x=exit_points.index,
    y=exit_points.values,
    mode='markers',
    name='Sell',
    marker=dict(
        color='red',
        size=10,
        symbol='triangle-down'
    )
))

# Add moving averages
fig.add_trace(go.Scatter(
    x=close.index,
    y=fast_ma.ma.values,
    mode='lines',
    name='Fast MA (10)',
    line=dict(color='orange', width=1, dash='dash')
))

fig.add_trace(go.Scatter(
    x=close.index,
    y=slow_ma.ma.values,
    mode='lines',
    name='Slow MA (30)',
    line=dict(color='purple', width=1, dash='dash')
))

fig.update_layout(
    title='RELIANCE Trading Strategy',
    xaxis_title='Date',
    yaxis_title='Price (₹)',
    hovermode='x unified',
    template='plotly_white'
)

fig.show()

Performance Dashboard

# Create a comprehensive dashboard
from plotly.subplots import make_subplots

fig = make_subplots(
    rows=3, cols=2,
    subplot_titles=(
        'Cumulative Returns', 'Drawdown',
        'Monthly Returns', 'Trade Distribution',
        'Rolling Sharpe', 'Volume'
    ),
    vertical_spacing=0.1,
    horizontal_spacing=0.1
)

# Cumulative returns
cum_returns = portfolio.cumulative_returns()
fig.add_trace(
    go.Scatter(x=cum_returns.index, y=cum_returns.values, name='Returns'),
    row=1, col=1
)

# Drawdown
drawdown = portfolio.drawdown()
fig.add_trace(
    go.Scatter(x=drawdown.index, y=drawdown.values, name='Drawdown', fill='tozeroy'),
    row=1, col=2
)

# Monthly returns heatmap
monthly_returns = portfolio.resample_returns('M')
fig.add_trace(
    go.Bar(x=monthly_returns.index, y=monthly_returns.values, name='Monthly'),
    row=2, col=1
)

# Trade PnL distribution
trade_returns = portfolio.trades.returns
fig.add_trace(
    go.Histogram(x=trade_returns, name='Trade Returns', nbinsx=20),
    row=2, col=2
)

# Rolling Sharpe (252 days)
rolling_sharpe = portfolio.rolling_sharpe(window=252)
fig.add_trace(
    go.Scatter(x=rolling_sharpe.index, y=rolling_sharpe.values, name='Sharpe'),
    row=3, col=1
)

# Volume
volume = data.get('Volume')
fig.add_trace(
    go.Bar(x=volume.index, y=volume.values, name='Volume'),
    row=3, col=2
)

fig.update_layout(height=900, showlegend=False)
fig.show()

Practice Exercises

Exercise 1: RSI Strategy on TCS

Create an RSI-based strategy on TCS.NS with the following rules:

  • • Buy when RSI(14) crosses below 30
  • • Sell when RSI(14) crosses above 70
  • • Use ₹5 Lakhs capital
  • • Compare with buy-and-hold returns
Show Solution
# Solution
data = vbt.YFData.download("TCS.NS", start="2022-01-01", end="2024-01-01")
close = data.get('Close')

# Calculate RSI
rsi = vbt.RSI.run(close, window=14)

# Generate signals
entries = rsi.rsi_crossed_below(30)
exits = rsi.rsi_crossed_above(70)

# Backtest
portfolio = vbt.Portfolio.from_signals(
    close, entries, exits,
    init_cash=5_00_000,
    fees=0.0003
)

print(f"Strategy Return: {portfolio.total_return():.2%}")
bh_return = (close.iloc[-1] / close.iloc[0] - 1)
print(f"Buy & Hold Return: {bh_return:.2%}")
print(f"Alpha: {portfolio.total_return() - bh_return:.2%}")

Exercise 2: Bank NIFTY Bollinger Bands

Implement a Bollinger Bands strategy on Bank NIFTY:

  • • Buy when price touches lower band
  • • Sell when price touches upper band
  • • Use 20-period SMA and 2 standard deviations
  • • Calculate Sharpe ratio and maximum drawdown
Show Solution
# Solution
data = vbt.YFData.download("^NSEBANK", start="2022-01-01", end="2024-01-01")
close = data.get('Close')

# Calculate Bollinger Bands
bb = vbt.BBANDS.run(close, window=20, stdev=2)

# Generate signals
entries = close <= bb.lower_band
exits = close >= bb.upper_band

# Backtest
portfolio = vbt.Portfolio.from_signals(
    close, entries, exits,
    init_cash=10_00_000,
    fees=0.0003
)

print(f"Total Return: {portfolio.total_return():.2%}")
print(f"Sharpe Ratio: {portfolio.sharpe_ratio():.2f}")
print(f"Max Drawdown: {portfolio.max_drawdown():.2%}")
portfolio.plot().show()

Exercise 3: NIFTY 50 Sector Rotation

Create a sector rotation strategy:

  • • Use IT, Banking, and Pharma sector leaders
  • • Rotate based on 1-month momentum
  • • Rebalance monthly
  • • Compare with equal-weight portfolio

Try to implement this advanced strategy using the concepts learned!

Next Steps

Congratulations! 🎉

You've completed the Getting Started module. You now know how to:

  • Install and configure VectorBT
  • Download Indian stock market data
  • Create and backtest trading strategies
  • Analyze performance metrics
  • Visualize results with interactive charts