Skip to content

Instantly share code, notes, and snippets.

@kongmunist
Last active October 23, 2023 14:03
Show Gist options
  • Save kongmunist/9517915de64e8dc6427997f6b8674657 to your computer and use it in GitHub Desktop.
Save kongmunist/9517915de64e8dc6427997f6b8674657 to your computer and use it in GitHub Desktop.
Code for Medium article "Accelerating backtesting using index trick"
import backtrader as bt
import pandas as pd
import time
############### Preprocess data
filename = "../data/BTC-USD_15min_2017-4-5.csv"
df = pd.read_csv(filename, header=0, parse_dates=['time'], index_col='time')
############### Create backtrader data feed, set up strategy and broker
# Create Cerebro backtesting instance
cerebro = bt.Cerebro()
# Set up broker with enough money for 1 BTC at peak value
cerebro.broker.set_cash(1000000)
# Add backtrader strategy
class SmaCross(bt.SignalStrategy):
def __init__(self):
sma1, sma2 = bt.ind.SMA(period=10), bt.ind.SMA(period=30)
crossover = bt.ind.CrossOver(sma1, sma2)
self.signal_add(bt.SIGNAL_LONG, crossover)
cerebro.addstrategy(SmaCross)
# Add data feed
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data)
############### Run backtrader
ret = cerebro.run()
import pandas as pd
import time
import numpy as np
######################## Preprocess data
filename = "../data/BTC-USD_15min_2017-4-5.csv"
df = pd.read_csv(filename, header=0, parse_dates=['time'])
# Create the two indicators and the crossover triggers
df['sma1'] = df['close'].rolling(10).mean()
df['sma2'] = df['close'].rolling(30).mean()
df['entry_trig'] = (df['sma1'] > df['sma2']) & (df['sma1'].shift(1) < df['sma2'].shift(1)) # Crossover
df['exit_trig'] = (df['sma1'] < df['sma2']) & (df['sma1'].shift(1) > df['sma2'].shift(1)) # Crossunder
# Filter NaNs
df = df.dropna()
######################## Generate trade entries
# Find all entry triggers, and create "views" of the two weeks after an entry trigger
maxInd = df.index[-1]; maxLen = 672 # a week in 15M candles
entryTriggers = df[df['entry_trig']].index
allViews = [df.loc[np.r_[x:min(x+maxLen,maxInd)]] for x in entryTriggers]
######################## Filter through and finalize list of trades (add exit info)
# Find the trade exit for each trade entry
now = time.time()
completeTrades = []
for v, et in zip(allViews, entryTriggers):
# Find the first exit trigger after this trade's entry
exitTriggerIndex = v.index[np.flatnonzero(v['exit_trig'])[0]]
# Add the entry/exit to a list
completeTrades.append([et, exitTriggerIndex, # entry/exit times
v['close'].at[et], v['close'].at[exitTriggerIndex]]) # entry/exit prices
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment