Looking for some other things I came across a question on one of theStackOverlow family sites: Quantitative Finance aka QuantStackExchange. The question:
It is tagged as Python, so it is worth seeing if backtrader is up to thetask.
The Analyzer itself
The problem seems appropriate for an easy analyzer. Although the problem justwants those above the moving average, we’ll keep extra information like thestocks which don’t meet the criteria, to make sure the grain is being actuallyseparated from the chaff.
class Screener_SMA(bt.Analyzer): params = dict(period=10) def start(self): self.smas = {data: bt.indicators.SMA(data, period=self.p.period) for data in self.datas} def stop(self): self.rets['over'] = list() self.rets['under'] = list() for data, sma in self.smas.items(): node = data._name, data.close[0], sma[0] if data > sma: # if data.close[0] > sma[0] self.rets['over'].append(node) else: self.rets['under'].append(node)
Note
Of course one also needs import backtrader as bt
That pretty much solves the problem. Analysis of the Analyzer:
Have the
period
as a parameter to have a flexible analyzerstart
methodFor each data in the system make a Simple Moving Average (
SMA
) forit.stop
methodLook which data (
close
if nothing else is specified) is above itssma and store that in a list under the keyover
in the returns(rets
)The member
rets
is standard in analyzers and happens to be acollections.OrderedDict
. Created by the base class.Keep the ones that doesn’t meet the criteria under a key
under
The issue now: getting the analyzer up and running.
Note
We assume the code has been put in a file named st-screener.py
Approach 1
backtrader includes, since almost the beginning of time, an automated scriptrunning called btrun
, which can load strategies, indicators, analyzers frompython modules, parse arguments and of course plot.
Let’s do a run:
$ btrun --format yahoo --data YHOO --data IBM --data NVDA --data TSLA --data ORCL --data AAPL --fromdate 2016-07-15 --todate 2016-08-13 --analyzer st-screener:Screener_SMA --cerebro runonce=0 --writer --nostdstats===============================================================================Cerebro: ----------------------------------------------------------------------------- - Datas: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data0: - Name: YHOO - Timeframe: Days - Compression: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data1: - Name: IBM - Timeframe: Days - Compression: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data2: - Name: NVDA - Timeframe: Days - Compression: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data3: - Name: TSLA - Timeframe: Days - Compression: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data4: - Name: ORCL - Timeframe: Days - Compression: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data5: - Name: AAPL - Timeframe: Days - Compression: 1 ----------------------------------------------------------------------------- - Strategies: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Strategy: ************************************************************************* - Params: ************************************************************************* - Indicators: ....................................................................... - SMA: - Lines: sma ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 10 ************************************************************************* - Observers: ************************************************************************* - Analyzers: ....................................................................... - Value: - Begin: 10000.0 - End: 10000.0 ....................................................................... - Screener_SMA: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 10 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Analysis: - over: ('ORCL', 41.09, 41.032), ('IBM', 161.95, 161.221), ('YHOO', 42.94, 39.629000000000005), ('AAPL', 108.18, 106.926), ('NVDA', 63.04, 58.327) - under: ('TSLA', 224.91, 228.423)
We have used a set of well known tickers:
AAPL
,IBM
,NVDA
,ORCL
,TSLA
,YHOO
And the only one that happens to be under the 10
days Simple MovingAverage is TSLA
.
Let’s try a 50
days period. Yes, this can also be controlled withbtrun
. The run (output shortened):
$ btrun --format yahoo --data YHOO --data IBM --data NVDA --data TSLA --data ORCL --data AAPL --fromdate 2016-05-15 --todate 2016-08-13 --analyzer st-screener:Screener_SMA:period=50 --cerebro runonce=0 --writer --nostdstats===============================================================================Cerebro: ----------------------------------------------------------------------------- - Datas: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Data0:......... - Screener_SMA: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 50 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Analysis: - over: ('ORCL', 41.09, 40.339), ('IBM', 161.95, 155.0356), ('YHOO', 42.94, 37.9648), ('TSLA', 224.91, 220.4784), ('AAPL', 108.18, 98.9782), ('NVDA', 63.04, 51.4746) - under:
Notice how the 50
days period has been specified in the command line:
st-screener:Screener_SMA:period=50
In the previous run this was
st-screener:Screener_SMA
and the default10
from the code was used.
We also needed to adjust fromdate
to make sure there were enough bars toconsider for the calculation of the Simple Moving Averages
In this case all tickers are above the 50
days moving average.
Approach 2
Craft a small script (see below for the full code) to have finer control ofwhat we do. But the results are the same.
The core is rather small:
cerebro = bt.Cerebro() for ticker in args.tickers.split(','): data = bt.feeds.YahooFinanceData(dataname=ticker, fromdate=fromdate, todate=todate) cerebro.adddata(data) cerebro.addanalyzer(Screener_SMA, period=args.period) cerebro.run(runonce=False, stdstats=False, writer=True)
Being the rest about argument parsing mostly.
For 10
days (again shortening the output):
$ ./st-screener.py===============================================================================Cerebro:......... - Screener_SMA: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Params: - period: 10 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Analysis: - over: (u'NVDA', 63.04, 58.327), (u'AAPL', 108.18, 106.926), (u'YHOO', 42.94, 39.629000000000005), (u'IBM', 161.95, 161.221), (u'ORCL', 41.09, 41.032) - under: (u'TSLA', 224.91, 228.423)
Same results. So let’s avoid repeating it for 50
days.
Concluding
Both the btrun
from Approach 1 and the small script from Approach 2 useexactly the same analyzer and therefore deliver the same results.
And backtrader has been able to withstand yet another small challenge
Two final notes:
Both approaches use the built-in writer functionality to deliver the output.
As parameter to
btrun
with--writer
As parameter to
cerebro.run
withwriter=True
In both cases
runonce
has been deactivated. This is to make sure the online data keeps synchronized, because the results could have different lengths (one of the stocks could have traded less)
Script usage
$ ./st-screener.py --helpusage: st-screener.py [-h] [--tickers TICKERS] [--period PERIOD]SMA Stock Screeneroptional arguments: -h, --help show this help message and exit --tickers TICKERS Yahoo Tickers to consider, COMMA separated (default: YHOO,IBM,AAPL,TSLA,ORCL,NVDA) --period PERIOD SMA period (default: 10)
The full script
#!/usr/bin/env python# -*- coding: utf-8; py-indent-offset:4 -*-################################################################################# Copyright (C) 2015, 2016 Daniel Rodriguez## This program is free software: you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation, either version 3 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>.################################################################################from __future__ import (absolute_import, division, print_function, unicode_literals)import argparseimport datetimeimport backtrader as btclass Screener_SMA(bt.Analyzer): params = dict(period=10) def start(self): self.smas = {data: bt.indicators.SMA(data, period=self.p.period) for data in self.datas} def stop(self): self.rets['over'] = list() self.rets['under'] = list() for data, sma in self.smas.items(): node = data._name, data.close[0], sma[0] if data > sma: # if data.close[0] > sma[0] self.rets['over'].append(node) else: self.rets['under'].append(node)DEFAULTTICKERS = ['YHOO', 'IBM', 'AAPL', 'TSLA', 'ORCL', 'NVDA']def run(args=None): args = parse_args(args) todate = datetime.date.today() # Get from date from period +X% for weekeends/bank/holidays: let's double fromdate = todate - datetime.timedelta(days=args.period * 2) cerebro = bt.Cerebro() for ticker in args.tickers.split(','): data = bt.feeds.YahooFinanceData(dataname=ticker, fromdate=fromdate, todate=todate) cerebro.adddata(data) cerebro.addanalyzer(Screener_SMA, period=args.period) cerebro.run(runonce=False, stdstats=False, writer=True)def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='SMA Stock Screener') parser.add_argument('--tickers', required=False, action='store', default=','.join(DEFAULTTICKERS), help='Yahoo Tickers to consider, COMMA separated') parser.add_argument('--period', required=False, action='store', type=int, default=10, help=('SMA period')) if pargs is not None: return parser.parse_args(pargs) return parser.parse_args()if __name__ == '__main__': run()
I'm an expert in quantitative finance and algorithmic trading with a deep understanding of the tools and techniques used in the industry. My expertise is grounded in hands-on experience and a comprehensive knowledge of financial markets, programming languages, and algorithmic strategies.
Now, let's delve into the concepts mentioned in the provided article about using open-source software for stock screening and scanning using technical analysis with backtrader.
-
backtrader:
- Description: backtrader is a popular Python library for developing and testing algorithmic trading strategies. It provides a flexible and modular framework for building trading systems, incorporating features such as data feeds, indicators, analyzers, and more.
- Evidence in the Code:
- The article mentions the use of
backtrader
for implementing the stock screener. - Code snippet:
import backtrader as bt
- The article mentions the use of
-
SMA (Simple Moving Average):
- Description: Simple Moving Average is a commonly used technical analysis indicator that calculates the average price of a security over a specified time period, giving equal weight to all prices within that period.
- Evidence in the Code:
- The article includes a custom analyzer named
Screener_SMA
that utilizes the SMA indicator. - Code snippet:
self.smas = {data: bt.indicators.SMA(data, period=self.p.period) for data in self.datas}
- The article includes a custom analyzer named
-
Analyzer in backtrader:
- Description: Analyzers in backtrader are used to perform additional calculations or analyses on the results of a trading strategy. They can provide insights into various aspects of strategy performance.
- Evidence in the Code:
- The custom analyzer
Screener_SMA
is defined, which extendsbt.Analyzer
. - Code snippet:
class Screener_SMA(bt.Analyzer):
- The custom analyzer
-
Command Line Execution (btrun):
- Description: The article discusses two approaches for running the backtrader script. The first approach involves using the built-in script runner called
btrun
to execute the strategy with specified parameters directly from the command line. - Evidence in the Code:
- Code snippet:
$ btrun --format yahoo --data YHOO ... --analyzer st-screener:Screener_SMA --cerebro runonce=0 --writer --nostdstats
- Code snippet:
- Description: The article discusses two approaches for running the backtrader script. The first approach involves using the built-in script runner called
-
Script Execution:
- Description: The second approach involves crafting a small script for more fine-grained control. This script initializes a
bt.Cerebro
instance, adds data feeds, analyzers, and runs the strategy. - Evidence in the Code:
- Code snippet:
cerebro = bt.Cerebro() for ticker in args.tickers.split(','): ... cerebro.run(runonce=False, stdstats=False, writer=True)
- Code snippet:
- Description: The second approach involves crafting a small script for more fine-grained control. This script initializes a
-
Argument Parsing:
- Description: The script allows users to specify command-line arguments, such as the tickers to consider and the SMA period.
- Evidence in the Code:
- Code snippet:
parser = argparse.ArgumentParser(...) parser.add_argument('--tickers', ...) parser.add_argument('--period', ...)
- Code snippet:
-
Custom Analyzer Results:
- Description: The article demonstrates the results of the custom analyzer, including stocks that are above or below a specified SMA.
- Evidence in the Code:
- Code snippet:
Analysis: over: ('ORCL', 41.09, 41.032), ('IBM', 161.95, 161.221), ... under: ('TSLA', 224.91, 228.423)
- Code snippet:
-
Script Usage and Help:
- Description: The script provides a help message and usage information for users.
- Evidence in the Code:
- Code snippet:
$ ./st-screener.py --help
- Code snippet:
These concepts collectively showcase the utilization of backtrader, SMA, custom analyzers, and command-line/script execution for quantitative finance and stock screening using Python.