This is a topic that has been widely discussed but I haven't seen a straightforward answer on how to work with multiple that have different start dates.
The major challenges are:
- How to pass an indicator to multiple data
- How to start a strategy even if some of the datafeeds haven't started yet (for instance, in the case of IPOs)
- How to handle the datafeeds that have the right timeframe
This is the code I came up with. It's really unclear, but I hope it can be useful for others as I struggled with this a lot. If anyone has recommendations on how to improve it, you are more than welcome!
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import math
import argparse
import datetime
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
from backtrader import ResamplerDaily, ResamplerWeekly, ResamplerMonthly
from backtrader import ReplayerDaily, ReplayerWeekly, ReplayerMonthly
from backtrader.utils import flushfile
def log(self, txt, dt=None):
""" Logging function fot this strategy"""
dt = dt or self.data.datetime[0]
if isinstance(dt, float):
dt = bt.num2date(dt)
print("%s, %s" % (dt.date(), txt))
class multitimeframeStrategy(bt.Strategy):
period = 60
wait = 0
def __init__(self):
self.mo = dict()
#1) Passing the indicator to all the datas here
for d in self.datas:
self.mo[d] = bt.ind.momentum(d.close, period=self.period)
#2) starting a strategy even if some of the datafeeds haven't started yet her (for instance, in the case of IPOs)
def prenext(self):
self.next()
#3) Handling the datafeeds that have the right timeframe here
def next(self):
self.wait = self.wait + 1
if (self.period < self.wait):
best_mo_val = None
best_mo_data = None
for dmo, mom in self.mo.items():
#3) Handling the datafeeds that have the right timeframe also here
if len(dmo) > self.period:
# log(self,f"{dmo._name} is being analysed. it's length is {len(dmo)}",dmo.datetime[0])
if not best_mo_val or mom[0] > best_mo_val:
best_mo_val = mom[0]
best_mo_data = dmo
# self.log(f"{best_mo_data._name}, {best_mo_val:5.2f}")
for data in self.datas:
if data!= best_mo_data:
self.close(data)
amount_to_invest = 0.95 * self.broker.cash
self.size = math.floor(amount_to_invest/best_mo_data.close)
# self.buy(best_mo_data,percent=100)
self.buy(best_mo_data,size=self.size)
log(self,f"Buying {best_mo_data._name}")
if __name__ == '__main__':
cerebro = bt.Cerebro()
startdate =datetime.date(2010, 7, 1)
enddate=datetime.date(2021, 2, 1)
""" NEW DATA PARAMETERS """
for ticker in [
"AAPL",
"ADBE",
"ADSK",
"AMZN",
"ANSS",
"AQB",
"ARCT",
"AVAV",
"AY",
"BEEM",
"BIDU",
"CDNA",
"CDXS",
"CERS",
"CGEN",
"CLLS",
"CRSP",
"ARKW",
"CHIK",
"TAN",
"VGT",
"VOO"
]:
data = bt.feeds.YahooFinanceData(
dataname=ticker,
timeframe=bt.TimeFrame.Days,
fromdate=startdate,
todate=enddate,
reverse=False,
)
cerebro.adddata(data)
cerebro.addobserver(bt.observers.Broker)
# cerebro.broker.setcommission(commission=0.005)
print('Initial Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.addstrategy(multitimeframeStrategy)
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot()[0][0]