Can't get indicator trigger date to show up in logs
TL;DR: I used backtrader boilerplate code with small adjustments to prepare a prototype that shows me the dates on which an indicator was triggered but it's not working. Please help!
Hope everyone is safe and healthy here. I am trying to build a backtest that makes trades based on how many days have passed since an indicator was triggered. In order to build this, I began by creating a simple prototype to ensure I am capturing these dates in the
next()method thats part of the strategy class.
Below you can find my code. I am just trying to get the Cerebro instance to print out the dates on which the indicator was triggered but am not getting the relevant output. I include my code below along with the output. I use python version 3.7.4 for this. Most of this was prepared using boilerplate code in the backtrader documentation.
#Import libraries import numpy as np import pandas as pd import backtrader as bt import backtrader.analyzers as btanalyzers import matplotlib from IPython.display import display, Image from datetime import datetime import yfinance as yf import sys #We instantiate a class used to inform QQQ call buying and selling class LocalPeakStrategy(bt.Strategy): #Hoping to log date in which local peaks are triggered #We use this data to inform our actual buying def log(self, dt=None): dt = dt or self.datas.datetime.date(0) print('%s' % (dt.isoformat())) #We calculate different indicators/signals that inform buying/selling def __init__(self): local_peak_threshold = 0.08 one_day_rate_of_change =\ bt.ind.RateOfChange(period = 1) two_day_rate_of_change =\ bt.ind.RateOfChange(period = 2) three_day_rate_of_change =\ bt.ind.RateOfChange(period = 3) if one_day_rate_of_change >= local_peak_threshold: self.local_peak = one_day_rate_of_change elif two_day_rate_of_change >= local_peak_threshold: self.local_peak = two_day_rate_of_change elif three_day_rate_of_change >= local_peak_threshold: self.local_peak = three_day_rate_of_change else: self.local_peak = 0 #We tell backtrader when to buy and sell def next(self): #Testing to see if I can at least get the dates of when local peaks are set if self.local_peak > 0: self.log() #Instantiate Cerebro object cerebro = bt.Cerebro() #Important relevant data qqq = yf.Ticker("QQQ") columns_to_use = ['Open', 'High', 'Low', 'Close', 'Volume'] df_qqq = qqq.history(period = 'max')[columns_to_use] df_qqq['Open Interest'] = -1 #We are missing this data #Add data feed feed = bt.feeds.PandasData(dataname = df_qqq) cerebro.adddata(feed) #Set cash position and run cerebro.broker.setcash(5000) display(cerebro.run())
[<backtrader.strategy.Strategy at 0x7fdd236e5450>]
Any feedback deeply appreciated. Thanks for making this far.
Logging is described in the Quickstart
Is this boilerplate? I don't think this is going to do what you want.
if one_day_rate_of_change >= local_peak_threshold: self.local_peak = one_day_rate_of_change elif two_day_rate_of_change >= local_peak_threshold: self.local_peak = two_day_rate_of_change elif three_day_rate_of_change >= local_peak_threshold: self.local_peak = three_day_rate_of_change else: self.local_peak = 0
self.log()method is introduced and it should be called to print the data. You can call it in the
next()or in the other strategy methods.
Ad to @run-out comment - you may want to look into docs more to understand how
btworks and how lines should be processed in the
initand in the
@alechter You are working with lines, not scalar values. This changes how you use if and then because it will apply to the whole line at once. In the example below, I put line three first because your code suggest line one is most important, so I'm having it over write last.
local_peak_threshold = 0.08 one_day_rate_of_change = bt.ind.RateOfChange(period=1) two_day_rate_of_change = bt.ind.RateOfChange(period=2) three_day_rate_of_change = bt.ind.RateOfChange(period=3) # Create an empty line of zeros. self.local_peak = bt.Max(0, 0) # In reverse order so day one will take priority over day three? # Not sure how you want it. self.local_peak = bt.If( three_day_rate_of_change >= local_peak_threshold, three_day_rate_of_change, self.local_peak, ) self.local_peak = bt.If( two_day_rate_of_change >= local_peak_threshold, two_day_rate_of_change, self.local_peak, ) self.local_peak = bt.If( one_day_rate_of_change >= local_peak_threshold, one_day_rate_of_change, self.local_peak, )
@run-out Thank you so much for providing some actionable guidance!
I am entering this backtesting exercise with intermediate python experience (i.e. I can write scripts to automate some of my recurring workflows and have no problem using Pandas/numpy and the standard ML libraries). As I'm digging deeper, I am feeling like this material may be a bit "over my head".
Is there a good uDemy/Coursera or other online course you'd recommend to gain a stronger familiarity working with this package? Given my background, I dont think the existing documentation alone will get me to pick this up.
@alechter I'm sorry but I can't think of any courses off the top of my head. I would encourage you to perservere though. Backtrader has a bit of a learning curve for everyone.
Hang in there. And don't be shy to ask questions. :)
@alechter I started where you are at just a few weeks ago: comfortable Python developer and familiar with Pandas/numpy. I evaluated a number of backtesting platforms (zipline, QuantConnect/LEAN, QuantRocket, PyInvesting) and settled on Backtrader.
I think Backtrader is pretty comprehensive, has a good design with a lot of useful functionality built in but also lots of extension points to customize where you need to (custom feeds, indicators, analyzers, observers). The documentation is pretty good, although not as well organized as it could be (I've picked up useful nuggets from the Articles archive, for example, that weren't easy to find in the standard documentation). The code is also pretty easy to follow so I'll read through it when I want to better understand how something works.
And the community is really good. Many of my questions have been answered by simply searching this forum and reading through past responses. And I'm learning enough to even post the occasional answer to other people's questions now, too!
I'd encourage you to stick with it and fight through a real (but not overly complicated) example to force you to learn how all the pieces fit together and you'll probably find it makes a lot more sense at the end of that exercise.
If you decide then that it's not for you, a lot of what you've learned will apply to other backtesting environments. Many of them share the same concepts (strategies, indicators, analyzers, data feeds). Good luck!