Scope
This tutorial aims to set up a simple indicator based strategy using as simple code as possible. I will try to avoid some more advanced concepts found in the documentation and Python in general. For example lines such as:
if __name__ == '__main__’:
The Strategy
- When RSI < 30
- When RSI > 70
- No trade management shall be implemented. No scaling in / out. Just simple buying and selling with a single open position at a time.
- Position size wise, we will keep things simple and just buy / sell 100 shares at a time without doing any calculations to see if we have enough cash for position size. (if we don’t have enough cash, backtrader is smart enough to reject the order)
- Period = 21
- Lets use a longer look back period than the default 14. In theory this should result in less false signals and price should have to come down / rise much further before it is considered overbought / over sold.
Before we start
Requirements
pip3 list
pip3 list | grep backtrader
import backtrader as bt print(bt.__version__)
pip3 install --upgrade backtrader
pip3 install matplotlib
And so we begin….
''' Author: www.backtest-rookies.com MIT License Copyright (c) 2017 backtest-rookies.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' import backtrader as bt from datetime import datetime class firstStrategy(bt.Strategy): def __init__(self): self.rsi = bt.indicators.RSI_SMA(self.data.close, period=21) def next(self): if not self.position: if self.rsi < 30: self.buy(size=100) else: if self.rsi > 70: self.sell(size=100) #Variable for our starting cash startcash = 10000 #Create an instance of cerebro cerebro = bt.Cerebro() #Add our strategy cerebro.addstrategy(firstStrategy) #Get Apple data from Yahoo Finance. data = bt.feeds.Quandl( dataname='AAPL', fromdate = datetime(2016,1,1), todate = datetime(2017,1,1), buffered= True ) #Add the data to Cerebro cerebro.adddata(data) # Set our desired cash start cerebro.broker.setcash(startcash) # Run over everything cerebro.run() #Get final portfolio Value portvalue = cerebro.broker.getvalue() pnl = portvalue - startcash #Print out the final result print('Final Portfolio Value: ${}'.format(portvalue)) print('P/L: ${}'.format(pnl)) #Finally plot the end results cerebro.plot(style='candlestick')
Important Note: The data in this tutorial uses the Quandl API. With this API you are limited to the number calls you can make per day. To have unlimited access to their free data, sign up for an API key here and add the apikey
keyword to the quandl data call like so:
data = bt.feeds.Quandl( dataname='F', fromdate = datetime(2016,1,1), todate = datetime(2017,1,1), buffered= True, apikey="INSERT YOUR API KEY" )
Reference: https://www.quandl.com/?modal=register
Imports
import backtrader as bt from datetime import datetime
The first import should be pretty obvious. We are importing the backtrader framework. In addition to this, we import the datetime module from the standard python library. This is used for setting the start and end dates for our backtest. Backtrader expects to receive datetime objects when creating data feeds. For more information regarding the datetime module, please check out the documentation here:
The Strategy.
class firstStrategy(bt.Strategy): def __init__(self): self.rsi = bt.indicators.RSI_SMA(self.data.close, period=21) def next(self): if not self.position: if self.rsi < 30: self.buy(size=100) else: if self.rsi > 70: self.sell(size=100)
When you create a strategy in backtrader, you inherit many methods and attributes from the base class `bt.Strategy`. If that sounds confusing, basically we are taking an existing skeleton strategy that has been written in the backtest framework and adding our logic to it. In programming when we use inheritance, we get to use all of the code that was written for the base strategy and just overwrite the bits we want to change. This means you don’t have to worry about all the code behind the scenes which allows you to interact with the data, orders, broker notifications and so on. The framework takes care of that! The new strategy class can be short, clean and easy to read. However, if you wish, you can dig deeper and tweak to your heart’s content.
The Setup
- Calling the backtrader engine (cerebro)
- Getting some data and loading it into the engine.
- Setting how much cash we have to trade (startcash)
- Loading our strategy
- Running the test
- Plotting the results
A few things to point out:
cerebro.plot(style='candlestick')
data = bt.feeds.YahooFinanceData( dataname='AAPL', fromdate = datetime(2016,1,1), todate = datetime(2017,1,1), buffered= True )
Running the script
The results
Find This Post Useful?
If this post saved you time and effort, please consider support the site! There are many ways to support us and some won’t even cost you a penny.
Backtest Rookies is a registered with Brave publisher!
Brave users can drop us a tip.
Alternatively, support us by switching to Brave using this referral link and we will receive some BAT!
Referral Link
Enjoying the content and thinking of subscribing to Tradingview? Support this site by clicking the referral link before you sign up!
3HxNVyh5729ieTfPnTybrtK7J7QxJD9gjD
0x9a2f88198224d59e5749bacfc23d79507da3d431
M8iUBnb8KamHR18gpgi2sSG7jopddFJi8S
[…] Backtrader: First Script […]
[…] Backtrader: First Script […]
[…] 4 – Backtrader First Script […]
[…] 4 – Backtrader First Script […]
[…] you have read through the Backtrader: First Script post or seen any of the other code snippets on this site, you will see that most examples work […]
when i run the above code i get the following.
Traceback (most recent call last):
File “C:/Users/GB/PycharmProjects/Trading/Script.py”, line 43, in
cerebro.run()
File “C:\Users\GB\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\cerebro.py”, line 1127, in run
runstrat = self.runstrategies(iterstrat)
File “C:\Users\GB\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\cerebro.py”, line 1212, in runstrategies
data.preload()
File “C:\Users\GB\AppData\Local\Programs\Python\Python36-32\lib\site-packages\backtrader\feed.py”, line 692, in preload
self.f.close()
AttributeError: ‘NoneType’ object has no attribute ‘close’
Process finished with exit code 1
Hi Gray,
Are you sure that you are running the code above?
I see your
AttributeError
message is taking exception toself.f.close()
The code above does not have a close call.
Note that is looks like you have an extra
f
inself.close()
you haveself.f.close()
To anyone else seeing this issue – I have reproduced it with Quandl – I think it might be an intermittent issue retrieving the data.
If you see it, try again in a few minutes.
Hi, Thanx for the examples as I’m truly struggling to understand even the basics just to get this running. I have plenty experience with MT4 and have built great trading plans and indicators on it but would really like to be able to use something like Python that is not as resource heavy to do trading during the day while I’m at work.
In the above example if I change the from date 1year back and step it back and look at the results some of them overlap so don’t show a trade because It was still in a position, but when I go to 2010 start date it didn’t parse many of the other trades that is later on even though there’s still enough cash in the account and no open position.
PS: Can you also please help me understand what I need to put into the data feed to see at least just one forex pair.
Thank you.
Holy thank you. The official documentation is so voluminous I can’t tell where to begin. This is PERFECT. Thanks so much.
Hello, I get same result as Gray. Backtrader :
# preloaded – no need to keep the object around – breaks multip in 3.x
self.f.close()
Traceback (most recent call last):
File “C:\Users\K\Desktop\DAP Python\TradingStrategy1.py”, line 45, in
cerebro.run()
File “C:\Users\K\AppData\Local\Programs\Python\Python37\lib\site-packages\backtrader\cerebro.py”, line 1127, in run
runstrat = self.runstrategies(iterstrat)
File “C:\Users\K\AppData\Local\Programs\Python\Python37\lib\site-packages\backtrader\cerebro.py”, line 1212, in runstrategies
data.preload()
File “C:\Users\K\AppData\Local\Programs\Python\Python37\lib\site-packages\backtrader\feed.py”, line 692, in preload
self.f.close()
AttributeError: ‘NoneType’ object has no attribute ‘close’
Great introduction to backtrader. It’s nice to see a beginner article which is actually doable for beginners. Most assume a level of familiarity with terms and concepts that would be beyond a novice. Easy to understand – a simple implementation giving a great foundation to build on.
Thanks!
Notes: I had to import matplotlib to get the plot to show
Hi,
when I run the code I’m getting the following when I try to plot the result but the plot will not display
[[]]
Hi Tom,
Do you have matplotlib installed?
Thanks for your reply, you’re right I didn’t have matplotlib installed for some reason…but the plot works now.
Anyway I have another question. I tried to run the code with a few other stocks but it only worked with US stocks. I also tried few German stocks e.g. Wirecard or Daimler but then I get an error.
Traceback (most recent call last):
File “”, line 1, in
File “C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.3\helpers\pydev\_pydev_bundle\pydev_umd.py”, line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File “C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.3\helpers\pydev\_pydev_imps\_pydev_execfile.py”, line 18, in execfile
exec(compile(contents+”\n”, file, ‘exec’), glob, loc)
File “C:/Users/tom/PycharmProjects/MyStrategy/venv/MyStrategy.py”, line 44, in
cerebro.run()
File “C:\Users\tom\AppData\Local\Programs\Python\Python37-32\lib\site-packages\backtrader\cerebro.py”, line 1127, in run
runstrat = self.runstrategies(iterstrat)
File “C:\Users\tom\AppData\Local\Programs\Python\Python37-32\lib\site-packages\backtrader\cerebro.py”, line 1212, in runstrategies
data.preload()
File “C:\Users\tom\AppData\Local\Programs\Python\Python37-32\lib\site-packages\backtrader\feed.py”, line 695, in preload
self.f.close()
AttributeError: ‘NoneType’ object has no attribute ‘close’
Is it not possible to receive the data from Quandl for some stocks?
Hi Tom,
The default data set used by Backtrader in Quandl is the WIKI dataset.
https://www.quandl.com/databases/WIKIP
It only covers US stocks and unfortunately has not been updated since March 2018.
I do cover some alternative ways to get stock data using Alpha Vantage in these articles. I can’t say whether they have German stocks available but they do have UK stocks so I think it is possible.
Getting CSV files:
https://backtest-rookies.com/2018/04/20/replacing-quandl-wiki-data-with-alpha-vantage/
and Directly ingesting into Backtrader:
https://backtest-rookies.com/2019/01/04/backtrader-alpha-vantage-data-direct-ingest/
Good luck!
I enjoyed the exercise, and I have installed matplotlib, and added import matplotlib to the code. When I run the script, I don’t get the matplotlib output window. Thank you for making this effort to share your knowledge!!!