Last active
October 23, 2023 14:03
-
-
Save kongmunist/9517915de64e8dc6427997f6b8674657 to your computer and use it in GitHub Desktop.
Code for Medium article "Accelerating backtesting using index trick"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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