BackTrader 中文文档(二十一)(4)

简介: BackTrader 中文文档(二十一)

BackTrader 中文文档(二十一)(3)https://developer.aliyun.com/article/1505419

方法 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 天做这个。

结论

方法 1btrun方法 2的小脚本都使用完全相同的analyzer,因此提供相同的结果。

backtrader已经能够应对又一个小挑战。

两个最后的注意事项:

  • 这两种方法都使用内置的writer功能来提供输出。
  • 作为btrun的参数,带有--writer
  • 作为参数传递给cerebro.run时,带有writer=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()

带有信号的策略

原文:www.backtrader.com/blog/posts/2016-08-01-signal-strategy/signal-strategy/

也可以在不编写策略的情况下操作backtrader。尽管这是首选方式,由于构成机器的对象层次结构,使用信号也是可能的。

注意

从版本1.8.0.x起可用

快速总结:

  • 不是编写一个策略类,实例化指标,编写买入/卖出逻辑…
  • 最终用户添加信号(无论如何是指标),其余工作在后台完成

快速示例:

import backtrader as bt
data = bt.feeds.OneOfTheFeeds(dataname='mydataname')
cerebro.adddata(data)
cerebro.add_signal(bt.SIGNAL_LONGSHORT, MySignal)
cerebro.run()

Et voilá!。

当然,信号本身是缺失的。让我们定义一个非常简单的信号,它产生:

  • 如果收盘价格高于简单移动平均线,则为买入指示
  • 如果收盘价格低于简单移动平均线,则为卖出指示

定义:

class MySignal(bt.Indicator):
    lines = ('signal',)
    params = (('period', 30),)
    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)

现在真的完成了。当执行run时,Cerebro将负责实例化一个特殊的策略实例,该实例知道如何处理信号

初始常见问题

  • 买入/卖出操作的成交量如何确定?
    cerebro实例会自动向策略添加FixedSize调整器。最终用户可以通过cerebro.addsizer更改调整器以改变策略
  • 订单如何执行?
    执行类型为市价,有效期为直到取消

信号技术细节

从技术和理论角度来看可以描述为:

  • 调用时返回另一个对象的可调用函数(仅一次)
    这在大多数情况下是一个类的实例化,但不一定是
  • 支持__getitem__接口。唯一请求的/索引将是0

从实际角度看,看上面的示例,信号是:

  • 来自backtrader生态系统的lines对象,主要是指标
    当使用其他指标时,比如在示例中使用简单移动平均线时,这很有帮助。

信号指示

当使用signal[0]查询信号时,会提供指示,含义是:

  • > 0 -> 买入指示
  • ´< 0 -> 卖出指示
  • ´== 0 -> 无指示

示例使用self.data - SMA进行简单算术运算,并且:

  • 数据高于SMA时发出买入指示
  • 数据低于SMA时发出卖出指示

注意

当未指定特定价格字段用于数据时,收盘价格是参考价格。

信号类型

下面示例中指示的常量直接从主backtrader模块中获取,如:

import backtrader as bt
bt.SIGNAL_LONG

有 5 种信号类型,分为 2 组。

主要组

  • LONGSHORT:此信号的买入卖出指示都被采纳
  • LONG
  • 买入指示用于开多头头寸
  • 卖出指示用于平仓多头头寸。但是:
  • 如果系统中存在LONGEXIT(见下文)信号,将用于退出多头头寸
  • 如果有SHORT信号可用且没有LONGEXIT可用,则会用于在开空头之前关闭多头
  • 空头
  • 空头指示被用来做空
  • 多头指示被用来关闭空头头寸。但是:
  • 如果系统中存在SHORTEXIT信号,则将用于退出空头
  • 如果有LONG信号可用且没有SHORTEXIT可用,则会用于在开多头之前关闭空头

退出组

这两个信号旨在覆盖其他信号,并提供退出多头/空头头寸的标准

  • LONGEXIT空头指示被用来退出多头头寸
  • SHORTEXIT多头指示被用来退出空头头寸

积累和订单并发

上面显示的Signal示例将不断发出多头空头指示,因为它只是从SMA值中减去close价格,这将始终是> 0< 0(有几次== 0

这将导致持续生成订单,产生 2 种情况:

  • 积累:即使已经在市场中,信号也会产生新订单,这将增加市场中的头寸
  • 并发:新订单将被生成,而不必等待其他订单的执行

为了避免这种情况,默认行为是:

  • 不积累
  • 不允许并发

如果希望实现这两种行为中的任何一种,可以通过cerebro进行控制:

  • cerebro.signal_accumulate(True)(或False以重新禁用它)
  • cerebro.signal_concurrency(True)(或False以重新禁用它)

示例

backtrader源代码包含一个用于测试功能的示例。

要使用的主要信号。

class SMACloseSignal(bt.Indicator):
    lines = ('signal',)
    params = (('period', 30),)
    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)

以及如果指定了选项,则退出信号

class SMAExitSignal(bt.Indicator):
    lines = ('signal',)
    params = (('p1', 5), ('p2', 30),)
    def __init__(self):
        sma1 = bt.indicators.SMA(period=self.p.p1)
        sma2 = bt.indicators.SMA(period=self.p.p2)
        self.lines.signal = sma1 - sma2

第一次运行:多头和空头

$ ./signals-strategy.py --plot --signal longshort

输出

注意:

  • Signal被绘制。这是正常的,因为它只是一个指标,适用于它的绘图规则
  • 策略实际上是多头空头。这可以看出,因为现金水平从未回到价值水平
  • 旁注:即使是一个愚蠢的想法…(并且没有佣金),策略也没有亏钱…

第二次运行:仅多头

$ ./signals-strategy.py --plot --signal longonly

输出

注意:

  • 这里每次卖出后现金水平都会回到价值水平,这意味着策略已经退出市场
  • 旁注:再次没有损失金钱…

第三次运行:仅空头

$ ./signals-strategy.py --plot --signal shortonly

输出

注意:

  • 第 1 次操作是一个卖出,如预期的那样,比前面 2 个示例中的第 1 次操作晚发生。直到close低于SMA且简单的减法产生负数时才会发生
  • 这里每次买入后现金水平都会回到价值水平,这意味着策略已经退出市场
  • 旁注:最终系统会亏钱

第四次运行:多头 + 多头退出

$ ./signals-strategy.py --plot --signal longonly --exitsignal longexit

输出

注意:

  • 许多交易都是相同的,但有些会提前中断,因为快速移动平均线在退出信号中向下穿越慢速移动平均线。
  • 系统展现了其longonly属性,现金在每笔交易结束时成为价值。
  • 旁注:再次提到金钱……即使进行了一些修改的交易

用法

$ ./signals-strategy.py --help
usage: signals-strategy.py [-h] [--data DATA] [--fromdate FROMDATE]
                           [--todate TODATE] [--cash CASH]
                           [--smaperiod SMAPERIOD] [--exitperiod EXITPERIOD]
                           [--signal {longshort,longonly,shortonly}]
                           [--exitsignal {longexit,shortexit}]
                           [--plot [kwargs]]
Sample for Signal concepts
optional arguments:
  -h, --help            show this help message and exit
  --data DATA           Specific data to be read in (default:
                        ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE   Starting date in YYYY-MM-DD format (default: None)
  --todate TODATE       Ending date in YYYY-MM-DD format (default: None)
  --cash CASH           Cash to start with (default: 50000)
  --smaperiod SMAPERIOD
                        Period for the moving average (default: 30)
  --exitperiod EXITPERIOD
                        Period for the exit control SMA (default: 5)
  --signal {longshort,longonly,shortonly}
                        Signal type to use for the main signal (default:
                        longshort)
  --exitsignal {longexit,shortexit}
                        Signal type to use for the exit signal (default: None)
  --plot [kwargs], -p [kwargs]
                        Plot the read data applying any kwargs passed For
                        example: --plot style="candle" (to plot candles)
                        (default: None)

代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import collections
import datetime
import backtrader as bt
MAINSIGNALS = collections.OrderedDict(
    (('longshort', bt.SIGNAL_LONGSHORT),
     ('longonly', bt.SIGNAL_LONG),
     ('shortonly', bt.SIGNAL_SHORT),)
)
EXITSIGNALS = {
    'longexit': bt.SIGNAL_LONGEXIT,
    'shortexit': bt.SIGNAL_LONGEXIT,
}
class SMACloseSignal(bt.Indicator):
    lines = ('signal',)
    params = (('period', 30),)
    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)
class SMAExitSignal(bt.Indicator):
    lines = ('signal',)
    params = (('p1', 5), ('p2', 30),)
    def __init__(self):
        sma1 = bt.indicators.SMA(period=self.p.p1)
        sma2 = bt.indicators.SMA(period=self.p.p2)
        self.lines.signal = sma1 - sma2
def runstrat(args=None):
    args = parse_args(args)
    cerebro = bt.Cerebro()
    cerebro.broker.set_cash(args.cash)
    dkwargs = dict()
    if args.fromdate is not None:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        dkwargs['fromdate'] = fromdate
    if args.todate is not None:
        todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        dkwargs['todate'] = todate
    # if dataset is None, args.data has been given
    data = bt.feeds.BacktraderCSVData(dataname=args.data, **dkwargs)
    cerebro.adddata(data)
    cerebro.add_signal(MAINSIGNALS[args.signal],
                       SMACloseSignal, period=args.smaperiod)
    if args.exitsignal is not None:
        cerebro.add_signal(EXITSIGNALS[args.exitsignal],
                           SMAExitSignal,
                           p1=args.exitperiod,
                           p2=args.smaperiod)
    cerebro.run()
    if args.plot:
        pkwargs = dict(style='bar')
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)
        cerebro.plot(**pkwargs)
def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for Signal concepts')
    parser.add_argument('--data', required=False,
                        default='../../datas/2005-2006-day-001.txt',
                        help='Specific data to be read in')
    parser.add_argument('--fromdate', required=False, default=None,
                        help='Starting date in YYYY-MM-DD format')
    parser.add_argument('--todate', required=False, default=None,
                        help='Ending date in YYYY-MM-DD format')
    parser.add_argument('--cash', required=False, action='store',
                        type=float, default=50000,
                        help=('Cash to start with'))
    parser.add_argument('--smaperiod', required=False, action='store',
                        type=int, default=30,
                        help=('Period for the moving average'))
    parser.add_argument('--exitperiod', required=False, action='store',
                        type=int, default=5,
                        help=('Period for the exit control SMA'))
    parser.add_argument('--signal', required=False, action='store',
                        default=MAINSIGNALS.keys()[0], choices=MAINSIGNALS,
                        help=('Signal type to use for the main signal'))
    parser.add_argument('--exitsignal', required=False, action='store',
                        default=None, choices=EXITSIGNALS,
                        help=('Signal type to use for the exit signal'))
    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))
    if pargs is not None:
        return parser.parse_args(pargs)
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()


相关文章
|
19天前
BackTrader 中文文档(二十一)(1)
BackTrader 中文文档(二十一)
16 0
|
19天前
|
存储 算法 Python
BackTrader 中文文档(二十一)(3)
BackTrader 中文文档(二十一)
19 0
|
19天前
BackTrader 中文文档(二十一)(2)
BackTrader 中文文档(二十一)
15 0
|
19天前
BackTrader 中文文档(十九)(4)
BackTrader 中文文档(十九)
16 0
|
19天前
BackTrader 中文文档(十九)(2)
BackTrader 中文文档(十九)
14 0
|
19天前
BackTrader 中文文档(十九)(1)
BackTrader 中文文档(十九)
15 0
|
19天前
BackTrader 中文文档(十九)(3)
BackTrader 中文文档(十九)
15 0
|
19天前
BackTrader 中文文档(二十五)(2)
BackTrader 中文文档(二十五)
15 0
|
19天前
|
C++
BackTrader 中文文档(二十五)(3)
BackTrader 中文文档(二十五)
14 0
|
19天前
|
数据可视化 API Python
BackTrader 中文文档(二十五)(1)
BackTrader 中文文档(二十五)
14 0

相关实验场景

更多