操作 backtrader 也是可能的,而無需編寫策略。雖然這是首選方式,但由於構成機器的對象層次結構,使用信號也是可能的。
快速摘要:
-
而不是編寫策略類,實例化指標,編寫買入/賣出邏輯...
-
最終使用者添加信號(無論如何指標),其餘部分在後台完成
簡單範例:
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當 高於dataSMA -
問題 a
short indication當data低於SMA
注意
當 沒有為data 指示特定價格欄位時 close ,價格就是參考價格。
信號類型
下面指示的常量(如上面的示例所示)可直接從主 backtrader 模塊獲得,如下所示:
import backtrader as bt bt.SIGNAL_LONG
有5種類型的信號,分為2組。
主要組:
-
LONGSHORT:兩者都long從short這個信號中獲取指示 -
LONG:-
long適應症需要做多 -
short指示用於 close 多頭頭寸。但: -
如果系統中存在
LONGEXIT(見下文)信號,它將用於退出多頭 -
如果信號
SHORT可用而無LONGEXIT可用,它將用於在打開 a 之前closelongshort
-
-
SHORT:-
short適應症被採取短路 -
long指示用於 close 空頭頭寸。但: -
如果系統中存在
SHORTEXIT(見下文)信號,它將用於退出空頭 -
如果信號
LONG可用而無SHORTEXIT可用,它將用於在打開之前closeshortlong
-
離開群組:
這 2 個信號旨在覆蓋其他信號,併為退出 along / short 倉位提供標準
-
LONGEXIT:short指示用於退出long倉位 -
SHORTEXIT:long指示用於退出short倉位
累積和訂單併發
上面顯示的示例信號將持續發出長短指示,因為它只是從價格中SMAclose減去值,這將始終是和> 0 < 0 (0在數學上是可能的,但不太可能真正發生)
這將導致連續生成訂單,從而產生 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
輸出
要注意:
-
第 1次 操作是預期的賣出,並且比上述 2 個示例中的第 1次 操作晚。直到低於
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()