Back to Openbb

**Backtesting Momentum Trading Strategies using OpenBB**

examples/BacktestingMomentumTrading.ipynb

4.7.03.9 KB
Original Source

Backtesting Momentum Trading Strategies using OpenBB

This notebook demonstrates how to perform backtesting of a momentum trading strategy using historical stock price data from OpenBB. A momentum trading strategy involves buying or selling assets based on recent price movements. In this notebook, we will:

  • Fetch Historical Stock Data using OpenBB.
  • Apply a Momentum Strategy based on moving averages.
  • Simulate Trades to backtest the strategy.
  • Analyze Performance by comparing the strategy’s returns to a buy-and-hold strategy.

The goal of the analysis is to test the effectiveness of a momentum-based trading strategy over time and to see how it performs in comparison to a simple buy-and-hold approach.

Author:

Sanchit Mahajan

python
!pip install openbb -q
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from openbb import obb
python
symbols = ['AAPL', 'GOOG', 'MSFT', 'NVDA']
start_date = '2015-01-01'
initial_capital = 10000
short_window = 40
long_window = 100
dataframes = []

for symbol in symbols:
    try:
        data = obb.equity.price.historical(
            symbol=symbol,
            start_date=start_date,
            provider="yfinance"
        ).to_df()
        data['Symbol'] = symbol
        dataframes.append(data)
    except Exception as e:
        print(f"Failed to fetch data for {symbol}: {str(e)}")

combined_data = pd.concat(dataframes)
combined_data = combined_data.reset_index()

combined_data.head()
python
def momentum_strategy(data, short_window, long_window):
    data['Short MA'] = data['close'].rolling(window=short_window, min_periods=1).mean()
    data['Long MA'] = data['close'].rolling(window=long_window, min_periods=1).mean()

    data['Signal'] = 0
    signal_values = np.where(
        data['Short MA'][short_window:] > data['Long MA'][short_window:], 1, -1
    )
    data.loc[data.index[short_window:], 'Signal'] = signal_values
    data['Position'] = data['Signal'].shift(1)

    return data

def backtest(data, initial_capital):
    data['Daily Return'] = data['close'].pct_change()
    data['Strategy Return'] = data['Position'] * data['Daily Return']
    data['Cumulative Market Return'] = (1 + data['Daily Return']).cumprod()
    data['Cumulative Strategy Return'] = (1 + data['Strategy Return']).cumprod()
    data['Portfolio Value'] = initial_capital * data['Cumulative Strategy Return']

    return data

def visualize_backtest(data, symbol):
    plt.figure(figsize=(12, 7))

    plt.plot(data['date'], data['Cumulative Market Return'], label='Market Return (Buy & Hold)', color='blue')
    plt.plot(data['date'], data['Cumulative Strategy Return'], label='Momentum Strategy Return', color='green')

    plt.title(f'{symbol} Backtest: Momentum Strategy vs Buy & Hold', fontsize=16, fontweight='bold')
    plt.xlabel('Date', fontsize=12)
    plt.ylabel('Cumulative Return', fontsize=12)
    plt.xticks(rotation=45)

    plt.legend()
    plt.show()

for symbol in symbols:
    stock_data = combined_data[combined_data['Symbol'] == symbol].copy()

    stock_data = momentum_strategy(stock_data, short_window, long_window)
    stock_data = backtest(stock_data, initial_capital)

    visualize_backtest(stock_data, symbol)

    final_portfolio_value = stock_data['Portfolio Value'].iloc[-1]
    print(f"Final portfolio value for {symbol}: ${final_portfolio_value:.2f}")

    total_market_return = stock_data['Cumulative Market Return'].iloc[-1] - 1
    total_strategy_return = stock_data['Cumulative Strategy Return'].iloc[-1] - 1
    print(f"Total market return for {symbol}: {total_market_return * 100:.2f}%")
    print(f"Total strategy return for {symbol}: {total_strategy_return * 100:.2f}%")
    print("="*40)