操作 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á!.
当然,信号本身是缺失的。让我们定义一个非常愚蠢的信号,它产生:
-
Long
指示价格是否close
高于简单移动平均线 -
Short
指示价格是否close
低于简单移动平均线
定义:
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 将负责实例化一个特殊的策略实例,该实例知道如何处理信号。
初始常见问题
-
如何确定买入/卖出操作 volume ?
cerebro实例会自动
FixedSize
向策略添加sizer。最终用户可以更改sizer以更改策略cerebro.addsizer
-
订单如何运行?
运行类型为
Market
“有效”,直到“取消”为止
信号技术细节
从技术和理论的角度来看,可以这样描述:
-
一个可调用对象,在调用时返回另一个对象(仅一次)
在大多数情况下,这是类的实例化,但不得是
-
__getitem__
支持接口。唯一请求的键/索引将是0
从实际的角度来看,看看上面的例子,一个信号是:
-
来自backtrader生态系统的lines对象,主要是指针
这在使用其他指针时很有说明,例如在示例中使用简单移动平均线时。
信号指示
信号在查找signal[0]
时提供指示,含义为:
-
> 0
->long indication
-
´< 0
->short indication
-
´== 0
-> 无适应症
该范例使用self.data - SMA
和运行简单的算术运算:
-
问题 a
long indication
当 高于data
SMA
-
问题 a
short indication
当data
低于SMA
注意
当 没有为data
指示特定价格字段时 close
,价格就是参考价格。
信号类型
下面指示的常量,如上面的示例所示,可直接从主 bactrader 模块获得,如下所示:
import backtrader as bt bt.SIGNAL_LONG
有5种类型的信号,分为2组。
主要组:
-
LONGSHORT
:两者都long
从short
这个信号中获取指示 -
LONG
:long
适应症需要做多-
short
指示用于 close 多头头寸。但: -
如果系统中存在
LONGEXIT
(见下文)信号,它将用于退出多头 -
如果信号
SHORT
可用而无LONGEXIT
可用,它将用于在打开 a 之前closelong
short
-
SHORT
:short
适应症被采取短路-
long
指示用于 close 空头头寸。但: -
如果系统中存在
SHORTEXIT
(见下文)信号,它将用于退出空头 -
如果信号
LONG
可用而无SHORTEXIT
可用,它将用于在打开之前closeshort
long
离开群组:
这 2 个信号旨在覆盖其他信号,并为退出 along
/short
仓位提供标准
-
LONGEXIT
:short
指示用于退出long
仓位 -
SHORTEXIT
:long
指示用于退出short
仓位
累积和订单并发
上面显示的示例信号将持续发出长短指示,因为它只是从价格中SMA
减去值,这将始终是和> 0
< 0
(几次== 0
)close
这将导致连续生成订单,从而产生 2 种情况:
-
Accumulation
:即使已经在市场上,信号也会产生新的订单,这将增加市场的可能性 -
Concurrency
:新订单将在不等待其他订单运行的情况下生成
为避免这种情况,缺省行为为:
-
不累积
-
不允许并发
如果希望这两种行为中的任何一种,可以通过以下方式进行控制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
输出
要注意:
-
绘制信号。这是正常的,因为它只是一个指针,并且它的绘图规则适用
-
策略是真的
long
和short
.这可以看出,因为现金水平永远不会回到价值水准。 -
附注:即使是一个愚蠢的想法...(并且没有佣金)该策略没有损失金钱...
第二次运行:仅长
$ ./signals-strategy.py --plot --signal longonly
输出
要注意:
-
在这里,现金水准回到每次卖出后的价值水准,这意味着策略已经退出市场。
-
附注:再次没有钱损失...
第三次运行:仅短运行
$ ./signals-strategy.py --plot --signal shortonly
输出
要注意:
-
1st 操作是预期的卖出,并且比上述 2 个示例中的第 1个操作a 晚。直到低于
close
和SMA
简单减法 产生一个负数 -
在这里,现金水准回到每次买入后的价值水准,这意味着策略已经退出市场。
-
附注:最后系统赔钱
第四次运行:长+长输出
$ ./signals-strategy.py --plot --signal longonly --exitsignal longexit
输出
要注意:
-
许多交易是相同的,但有些交易被提前中断,因为退出信号中的快速移动平均线穿过慢速移动平均线到下行
-
该系统显示其长期属性,现金成为每笔交易结束时的价值
-
附注:再次金钱...即使有一些修改的交易
用法
$ ./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()