股票筛选
原文: https://www.backtrader.com/blog/posts/2016-08-15-stock-screening/stock-screening/
在寻找其他东西时,我在StackOverlow家族网站上遇到了一个问题:定量金融又名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之上,并将其存储在返回(rets
中的over
键下的列表中成员
rets
是分析仪中的标准成员,正好是一个collections.OrderedDict
。由基类创建。将不符合条件的保留在一个键
under
下
现在的问题是:启动并运行分析器。
笔记
我们假设代码已放入名为st-screener.py
的文件中
方法 1
backtrader几乎从一开始就包括一个名为btrun
的自动脚本,它可以从 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
。
让我们试试 10 天。是的,这也可以通过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
天周期:
-
st-screener:Screener_SMA:period=50
在上一次运行中,这是
st-screener:Screener_SMA
,使用了代码中的默认10
。
我们还需要调整 EntT0,以确保有足够的杆来考虑 ALE T1 简单移动平均值的计算。
在这种情况下,所有股票价格都高于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
天。
总结
来自方法 1的btrun
和来自方法 2的小脚本使用完全相同的分析器,因此提供相同的结果。
而且反向交易者已经能够经受住另一个小挑战
最后两项说明:
-
这两种方法都使用内置的writer功能来传递输出。
-
以
--writer
作为btrun
的参数 -
以
writer=True
作为cerebro.run
的参数
-
-
在这两种情况下,
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()