在寻找其他一些东西时,我在StackOverlow家族网站之一上遇到了一个问题:Quantitative Finance aka Quant StackExchange。问题:
它被标记为Python,因此值得一看的是 backtrader 是否能够胜任这项任务。
分析仪本身
该问题似乎适合用于简单的分析器。虽然问题只是想要那些高于移动平均线的信息,但我们将保留额外的信息,例如不符合标准的股票,以确保谷物实际上与谷壳分离。
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)
注意
当然,还需要import backtrader as bt
这几乎解决了这个问题。分析仪分析:
-
有
period作为参数才有一个灵活的分析仪 -
start方法对于系统中的每个数据,为其创建一个简单的移动平均线(
SMA)。 -
stop方法查看哪些数据(
close如果未指定任何其他数据)高于其 sma,并将其存储在返回项 () 中键over下的清单中。rets该成员
rets是 analyzers 的标准,恰好是collections.OrderedDict.由基类创建。将不符合标准的那些保留在键下
under
现在的问题是:启动并运行分析器。
注意
我们假设代码已放入名为st-screener.py
方法 1
backtrader 几乎从一开始就包括一个自动脚本,该btrun脚本可以加载策略,指针, analyzers python模块,解析参数,当然还有绘图。
让我们运行一下:
$ 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)
我们使用了一组众所周知的股票代码:
AAPL,IBM,NVDA,ORCL,TSLA,YHOO
唯一一个碰巧在简单移动平均线下10 的日子是 TSLA。
让我们尝试一个50 几天的时间。是的,这也可以用 来控制 btrun。运行(输出缩短):
$ 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:
请注意,在50 命令 line中是如何指定天数的:
-
st-screener:Screener_SMA:period=50在上一次运行中,这是
st-screener:Screener_SMA并且使用了代码中的默认值10。
我们还需要进行调整fromdate ,以确保有足够的柱线来计算简单移动平均线
在这种情况下,所有股票代码都高于日移动50 平均线。
方法 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)
其余大部分是关于参数解析的。
几天10 (再次缩短输出):
$ ./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)
相同的结果。因此,让我们避免重复几天50 。
总结
btrun方法 1 中的小脚本和方法 2 中的小脚本都使用完全相同的分析器,因此提供相同的结果。
backtrader已经能够经受住另一个小挑战
最后两点:
-
这两种方法都使用内置的 writer 功能来提供输出。
-
作为参数 to
btrunwith--writer -
作为参数 to
cerebro.runwithwriter=True
-
-
在这两种情况下
runonce,都已停用。这是为了确保在线数据保持同步,因为结果可能具有不同的长度(其中一只股票的交易可能较少)
脚本用法
$ ./st-screener.py --help
usage: st-screener.py [-h] [--tickers TICKERS] [--period PERIOD]
SMA Stock Screener
optional 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)
完整脚本
#!/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 argparse
import datetime
import backtrader as bt
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)
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()