Backtrader中文笔记之 Strategy - Signals(二次修复)

参考连接:https://www.backtrader.com/docu/signal_strategy/signal_strategy/

前面没有很好的学习这个框架,走马观花的写了一遍,导致前面错误一堆,罪过,罪过,现在补上

该信号主要用来不独立写strategy的情况下,通过指标的形式与信号的形式进行交易

Strategy with Signals

Operating backtrader is also possible without having to write a Strategy. Although this is the preferred way, due to the object hierarchy which makes up the machinery, using Signals is also possible.

操作backtrader也可以不必编写策略。尽管这是首选方法,但由于构成机械的对象层次结构,使用信号也是可能的。

Quick summary:

  • Instead of writing a Strategy class, instantiating Indicators, writing the buy/sell logic …

  • The end user add Signals (indicators anyhow) and the rest is done in the background

  • 代替编写一个策略类,实例化指标,编写买/卖逻辑…
  • 最终用户添加信号(指标),其余的在后台完成
import backtrader as bt

data = bt.feeds.OneOfTheFeeds(dataname='mydataname')
cerebro.adddata(data)

# 直接添加信号 cerebro.add_signal(bt.SIGNAL_LONGSHORT, MySignal) cerebro.run()

 

Of course the Signal itself is missing. Let’s define a very dumb Signal which yields:

当然,信号本身已经消失了。让我们定义一个非常哑的信号,它会产生:

  • Long indication if the close price is above a Simple Moving Average

  • 多头指收盘价高于简单移动平均线
  • Short indication if the close price is below a Simple Moving Average

  • 收盘价低于简单移动平均线时的空头指示

The definition:

class MySignal(bt.Indicator):
    lines = ('signal',)
    params = (('period', 30),)

    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)

 

And now it is really done. When run is executed Cerebro will take care of instantiating a special Strategy instance which knows what to do with the Signals.

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

 

Initial FAQ

问题

  • How is the volume of buy/sell operations determined?

  • 买卖操作的数量是如何确定的?
  •  A cerebro instance adds automatically a FixedSize sizer to strategies. The end user can change the sizer to alter the policy with cerebro.addsizer

  • cerebro实例会自动向策略添加一个固定大小的sizer。最终用户可以用cerebro .addsizer来改变规格来改变策略
  • How are orders executed?

  • 订单是如何执行的?
  •  The execution type is Market and the validity is Good Until Canceled

  • 执行类型为市场,有效期至取消

Signals technicalities

信号技术问题

From a technical and theoretical point of view can be as described:

从技术和理论的角度可以描述如下:

  • A callable that returns another object when called (only once)

  • 当调用一个可调用对象,返回另外一个对象
  •  This is in most cases the instantiation of a class, but must not be

  • 在大多数情况下,这是类的实例化,但并不是一定这样
  • Supports the __getitem__ interface. The only requested key/index will be 0

  • 支持__getitem__接口。唯一请求的键/索引为0

From a practical point of view and looking at the example above a Signal is:

从实际的角度来看,上面的例子一个信号是:

  • A lines object from the backtrader ecosystem, mostly an Indicator

  • lines对象来自backtrader生态系统,主要是一个指标

    This helps when using other Indicators like when in the example the Simple Moving Average is used.

  • 这有助于使用其他指标,如在示例中使用简单移动平均线。

Signals indications

信号指标

The signals delivers indications when queried with signal[0] and the meaning is:

当信号[0]询问时,该信号给出指示,其意义为:

  • > 0 -> long indication

  • >0为多单
  • < 0 -> short indication

  • <0为空单
  • == 0 -> No indication

  • 等于0,没有显示

The example does simple arithmetic with self.data - SMA and:

这个例子用self.data - SMA做了简单的算术

  • Issues a long indication when the data is above the SMA

  • 当数据高于SMA指数时,发出长信号
  • Issues a short indication when the data is below the SMA

  • 当数据低于SMA时发出一个简短的指示

Note

When no specific price field is indicated for the data, the close price is the reference price is.

请注意

当数据没有指定价格字段时,收盘价为参考价格。

Signals Types

信号类型

The constants indicated below as seen in the example above, are directly available from the main backtrader module as in:

如下所示的常量如上图所示,可以直接从主backtrader模块中获得,如下所示:

import backtrader as bt

bt.SIGNAL_LONG

 There are 5 types of Signals, broken in 2 groups.

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

Main Group:

主要小组

  • LONGSHORT: both long and short indications from this signal are taken

  • 长-短:从这个信号中取长和短的指标
  • LONG:

    • long indications are taken to go long

    • 多头的指标会执行多单
    • short indications are taken to close the long position. But:

    • 空头指标用来平仓。但是:
    • If a LONGEXIT (see below) signal is in the system it will be used to exit the long

    • 如果LONGEXIT(见下面)信号在系统中,它将被用来退出多单
    • If a SHORT signal is available and no LONGEXIT is available , it will be used to close a long before opening a short

    • 如果有空头信号,而没有LONGEXIT,则在打开空头之前,它将用来关闭多头。也就是存在LONGSHORT
  • SHORT:

    • short indications are taken to go short

    • 空头指标会执行空单
    • long indications are taken to close the short position. But:

    • If a SHORTEXIT (see below) signal is in the system it will be used to exit the short

    • If a LONG signal is available and no SHORTEXIT is available , it will be used to close a short before opening a long

 

Exit Group:

退出小组

This 2 signals are meant to override others and provide criteria for exitins a long / short position

这两个信号是用来覆盖其他信号,并提供做多/做空的标准

  • LONGEXIT: short indications are taken to exit long positions

  • 多头退出:空头指示用于退出多头头寸
  • SHORTEXIT: long indications are taken to exit short positions

  • 做空指标:多头指标用来退出空头头寸

 这个主要是用来提供额外的退出标准的

Accumulation and Order Concurrency

累加和订单并发

The sample Signal shown above will issue long and short indications on a constant basis, because it simply substracts the SMA value from the close price and this will always be either > 0 and < 0 ( 0 is mathematically possible, but unlikely to really happen)

上面显示的样本信号将在一个常数的基础上发出多和空的指示,因为它只是简单地从收盘价减去SMA值,它将总是> 0和< 0(0在数学上是可能的,但不太可能真的发生)

This would lead to a continuous generation of orders that would produce 2 situations:

这将导致订单的连续产生,从而产生两种情况:

  • Accumulation: even if already in the market, the signals would produce new orders which would increase the possition in the market

  • 积累:即使已经持有订单,信号也会产生新的订单,从而增加在市场中的位置
  • Concurrency: new orders would be generated without waiting for the execution of other orders

  • 并发性:在不等待其他订单执行的情况下生成新订单

To avoid this the default behavior is:

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

  • To Not Accumulate

  • 不积累
  • To Not allow Concurrency

  • 不允许并发

Should any of these two behaviors be wished, this can be controlled via cerebro with:

如果你希望这两种行为中的任何一种,都可以通过cerebro来控制:

  • cerebro.signal_accumulate(True) (or False to re-disable it)

  • cerebro.signal_concurrency(True) (or False to re-disable it)

 

The sample

The backtrader sources contain a sample to test the functionality.

Main signal to be used.

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)

 And the Exit Signal in case the option is specified.

以及指定选项时的退出信号。

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

 First run: long and short

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

The output 

输出:

To notice:

  • The Signal is plotted. This is normal given it is simply an indicator and the plotting rules for it apply

  • 信号被绘制出来。这是正常的,因为它只是一个指标,并且适用于它的绘图规则
  • The strategy is really long and short. This can be seen because the cash level never goes back to be the value level

  • 这种策略一直多单,空单。这可以看出,因为现金水平永远不会回到价值水平
  • Side note: even for a dumb idea … (and without commission) the strategy hasn’t lost money …

  • 附注:即使是一个愚蠢的想法(并且没有佣金),这个策略也不会赔钱……

Second run: long only

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

 The output

To notice:

注意

  • Here the cash level goes back to be the value level after each sell, which means the strategy is out of the market

  • 在这里,现金水平回到每次出售后的价值水平,这意味着该策略已退出市场
  • Side note: Again no money has been lost …

  • 旁注:有一次赚钱了

 

Third run: short only

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

 The output

 

To notice:

  • The 1st operation is a sell as expected and takes place later than the 1st operation in the 2 examples above. Not until the close is below the SMA and the simple substraction yields a minus

  • 第一个预期的卖出操作,它发生的时间比上面两个示例中的第一个操作晚。直到收盘时低于SMA指数,简单的减法就会得到一个负数
  • Here the cash level goes back to be the value level after each buy, which means the strategy is out of the market

  • 在这里,每次买入后,现金水平回到价值水平,这意味着策略退出了市场
  • Side note: Finally the system loses money

  • 旁注:最后系统赔钱了

Fourth run: long + longexit

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

 The output

To notice:

  • Many of the trades are the same, but some are interrupted earlier because the fast moving average in the exit signal crosses the slow moving average to the downside

  • 许多交易都是相同的,但有些交易中断得更早,因为出口信号中的快速移动平均线穿过缓慢移动平均线下行
  • The system shows its longonly property with the cash becoming the value at the end of each trade

  • 该系统显示其长期属性,现金成为每笔交易结束时的价值
  • Side note: Again money is made … even with some modified trades

  • 边注:即使是修改了一些交易,仍然可以赚钱

这其实就是给了一个独立的退出策略而已.

Usage

$ ./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)

 

The code

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()

 

posted @ 2020-08-24 11:30  就是想学习  阅读(621)  评论(0编辑  收藏  举报