Backtrader分數大小

  |  

首先,讓我們用兩總結反向交易方法的工作原理:

  • 它就像一個帶有基本構建塊 ( Cerebro ) 的建築套件,可以插入許多不同的部件

  • 基本分佈包含許多部分,如指標、分析器觀察器、 Sizer 、過濾器、數據饋送、經紀人、佣金/資產信息方案,...

  • 可以從頭開始或基於現有的構建塊輕鬆構建新的構建塊

  • 基本構建塊 ( Cerebro ) 已經做了一些自動“插入”,以便更輕鬆地使用框架,而不必擔心所有細節。

因此,該框架已預先配置為提供具有默認值的行為,例如:

  • 使用單個/主要數據饋送
  • 1-day時間框架/壓縮組合
  • 10,000 單位貨幣
  • 股票交易

這可能並不適合所有人,但重要的是:它可以根據每個交易者/程序員的個人需求進行定制

交易股票:整數

如上所述,默認配置是用於股票交易,當交易股票時,購買/出售完整股(即:1、2 ... 50 ... 1000,而不是像1.51001.7589股那樣的數量。

這意味著當用戶在默認配置中執行以下操作時:

    def next(self):
        # Apply 50% of the portfolio to buy the main asset
        self.order_target_percent(target=0.5)

會發生以下情況:

  • 系統計算需要多少股資產,使給定資產在投資組合中的價值盡可能接近50%

  • 但是因為默認配置是使用共享,所以生成的共享數將是一個整數,即:整數

筆記

請注意,默認配置是使用單個/主要數據提要,這就是為什麼在對order_percent_target的調用中未指定實際數據的原因。在使用多個數據源進行操作時,必須指定要獲取/出售哪些數據(除非是指主要數據)

交易加密貨幣:分數

很明顯,在交易加密貨幣時,即使是小數點後 20 位,也可以買到“半個比特幣”。

好消息是人們實際上可以更改與資產有關的信息。這是通過CommissionInfo系列可插拔部件實現的。

一些文檔: Docs - Commission Schemes - https://www. backtrader .com/docu/commission-schemes/commission-schemes/

筆記

不得不承認,這個名字是不幸的,因為這些計劃不僅包含有關佣金的信息,還包含其他信息。

在分數場景中,感興趣的是該方案的這種方法: getsize(price, cash) ,它具有以下文檔字符串

Returns the needed size to meet a cash operation at a given price

這些方案與代理密切相關,通過代理API,可以將方案添加到系統中。

代理文檔位於: Docs - Broker - https://www。 backtrader .com/docu/broker/

相關的方法是: addcommissioninfo(comminfo, name=None) 。除了添加適用於所有資產的方案(當nameNone時)之外,還可以設置僅適用於具有特定名稱的資產的方案。

實施分數方案

這可以通過擴展名為CommissionInfo的現有基礎方案輕鬆實現。

class CommInfoFractional(bt.CommissionInfo):
    def getsize(self, price, cash):
        '''Returns fractional size for cash operation @price'''
        return self.p.leverage * (cash / price)

同上並完成。繼承CommissionInfo並編寫一行方法,目的就達到了。因為原始方案定義支持leverage ,所以在計算中考慮到了這一點,以防萬一可以用槓桿購買加密貨幣(默認值為1.0 ,即:無槓桿)

稍後在代碼中,將像這樣添加方案(通過命令參數控制)

    if args.fractional:  # use the fractional scheme if requested
        cerebro.broker.addcommissioninfo(CommInfoFractional())

即:添加子類方案的實例(注意()實例化)。如上所述, name參數未設置,這意味著它將應用於系統中的所有資產。

測試野獸

下面提供了實現多頭/空頭頭寸的簡單移動平均線交叉的完整腳本,可以直接在 shell 中使用。測試的默認數據饋送是來自backtrader存儲庫的數據饋送之一。

整數運行:沒有分數 - 沒有樂趣

$ ./fractional-sizes.py --plot
2005-02-14,3079.93,3083.38,3065.27,3075.76,0.00
2005-02-15,3075.20,3091.64,3071.08,3086.95,0.00
...
2005-03-21,3052.39,3059.18,3037.80,3038.14,0.00
2005-03-21,Enter Short
2005-03-22,Sell Order Completed - Size: -16 @Price: 3040.55 Value: -48648.80 Comm: 0.00
2005-03-22,Trade Opened  - Size -16 @Price 3040.55
2005-03-22,3040.55,3053.18,3021.66,3050.44,0.00
...

一個規模為16單位的空頭交易已開啟。整個日誌(由於明顯原因未顯示)包含許多其他操作,所有操作都包含完整大小的交易。

分數運行

在對分數進行硬子類化和單線工作之後......

$ ./fractional-sizes.py --fractional --plot
2005-02-14,3079.93,3083.38,3065.27,3075.76,0.00
2005-02-15,3075.20,3091.64,3071.08,3086.95,0.00
...
2005-03-21,3052.39,3059.18,3037.80,3038.14,0.00
2005-03-21,Enter Short
2005-03-22,Sell Order Completed - Size: -16.457437774427774 @Price: 3040.55 Value: -50039.66 Comm: 0.00
2005-03-22,Trade Opened  - Size -16.457437774427774 @Price 3040.55
2005-03-22,3040.55,3053.18,3021.66,3050.44,0.00
...

V代表勝利。空頭交易以相同的交叉開盤,但這次的小數大小為-16.457437774427774

請注意,圖表中的最終投資組合價值不同,這是因為實際交易規模不同。

是的,反向交易者可以。使用可插入/可擴展的構建工具包方法,可以很容易地根據交易者程序員的特定需求定制行為。

#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
# Copyright (C) 2019 Daniel Rodriguez - MIT License
#  - https://opensource.org/licenses/MIT
#  - https://en.wikipedia.org/wiki/MIT_License
###############################################################################
import argparse
import logging
import sys

import backtrader as bt

# This defines not only the commission info, but some other aspects
# of a given data asset like the "getsize" information from below
# params = dict(stocklike=True)  # No margin, no multiplier


class CommInfoFractional(bt.CommissionInfo):
    def getsize(self, price, cash):
        '''Returns fractional size for cash operation @price'''
        return self.p.leverage * (cash / price)


class St(bt.Strategy):
    params = dict(
        p1=10, p2=30,  # periods for crossover
        ma=bt.ind.SMA,  # moving average to use
        target=0.5,  # percentage of value to use
    )

    def __init__(self):
        ma1, ma2 = [self.p.ma(period=p) for p in (self.p.p1, self.p.p2)]
        self.cross = bt.ind.CrossOver(ma1, ma2)

    def next(self):
        self.logdata()
        if self.cross > 0:
            self.loginfo('Enter Long')
            self.order_target_percent(target=self.p.target)
        elif self.cross < 0:
            self.loginfo('Enter Short')
            self.order_target_percent(target=-self.p.target)

    def notify_trade(self, trade):
        if trade.justopened:
            self.loginfo('Trade Opened  - Size {} @Price {}',
                         trade.size, trade.price)
        elif trade.isclosed:
            self.loginfo('Trade Closed  - Profit {}', trade.pnlcomm)

        else:  # trade updated
            self.loginfo('Trade Updated - Size {} @Price {}',
                         trade.size, trade.price)

    def notify_order(self, order):
        if order.alive():
            return

        otypetxt = 'Buy ' if order.isbuy() else 'Sell'
        if order.status == order.Completed:
            self.loginfo(
                ('{} Order Completed - '
                 'Size: {} @Price: {} '
                 'Value: {:.2f} Comm: {:.2f}'),
                otypetxt, order.executed.size, order.executed.price,
                order.executed.value, order.executed.comm
            )
        else:
            self.loginfo('{} Order rejected', otypetxt)

    def loginfo(self, txt, *args):
        out = [self.datetime.date().isoformat(), txt.format(*args)]
        logging.info(','.join(out))

    def logerror(self, txt, *args):
        out = [self.datetime.date().isoformat(), txt.format(*args)]
        logging.error(','.join(out))

    def logdebug(self, txt, *args):
        out = [self.datetime.date().isoformat(), txt.format(*args)]
        logging.debug(','.join(out))

    def logdata(self):
        txt = []
        txt += ['{:.2f}'.format(self.data.open[0])]
        txt += ['{:.2f}'.format(self.data.high[0])]
        txt += ['{:.2f}'.format(self.data.low[0])]
        txt += ['{:.2f}'.format(self.data.close[0])]
        txt += ['{:.2f}'.format(self.data.volume[0])]
        self.loginfo(','.join(txt))


def run(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    data = bt.feeds.BacktraderCSVData(dataname=args.data)
    cerebro.adddata(data)  # create and add data feed

    cerebro.addstrategy(St)  # add the strategy

    cerebro.broker.set_cash(args.cash)  # set broker cash

    if args.fractional:  # use the fractional scheme if requested
        cerebro.broker.addcommissioninfo(CommInfoFractional())

    cerebro.run()  # execute

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))


def logconfig(pargs):
    if pargs.quiet:
        verbose_level = logging.ERROR
    else:
        verbose_level = logging.INFO - pargs.verbose * 10  # -> DEBUG

    logger = logging.getLogger()
    for h in logger.handlers:  # Remove all loggers from root
        logger.removeHandler(h)

    stream = sys.stdout if not pargs.stderr else sys.stderr  # choose stream

    logging.basicConfig(
        stream=stream,
        format="%(message)s",  # format="%(levelname)s: %(message)s",
        level=verbose_level,
    )


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Fractional Sizes with CommInfo',
    )

    pgroup = parser.add_argument_group('Data Options')
    parser.add_argument('--data', default='../../datas/2005-2006-day-001.txt',
                        help='Data to read in')

    pgroup = parser.add_argument_group(title='Broker Arguments')
    pgroup.add_argument('--cash', default=100000.0, type=float,
                        help='Starting cash to use')

    pgroup.add_argument('--fractional', action='store_true',
                        help='Use fractional commission info')

    pgroup = parser.add_argument_group(title='Plotting Arguments')
    pgroup.add_argument('--plot', default='', nargs='?', const='{}',
                        metavar='kwargs', help='kwargs: "k1=v1,k2=v2,..."')

    pgroup = parser.add_argument_group('Verbosity Options')
    pgroup.add_argument('--stderr', action='store_true',
                        help='Log to stderr, else to stdout')
    pgroup = pgroup.add_mutually_exclusive_group()
    pgroup.add_argument('--quiet', '-q', action='store_true',
                        help='Silent (errors will be reported)')
    pgroup.add_argument('--verbose', '-v', action='store_true',
                        help='Increase verbosity level')

    # Parse and process some args
    pargs = parser.parse_args(pargs)
    logconfig(pargs)  # config logging
    return pargs


if __name__ == '__main__':
    run()

推薦閱讀

相關文章

Backtrader教程:觀察者 - 參考

基準 backtrader類 .observers.基準() 此 observer 存儲策略的回報和參考資產的回報,參考資產是傳遞到系統的數據之一。

Backtrader做空現金

從一開始,反向交易者就可以做空任何東西,包括類似股票和類似期貨的工具。當做空時,現金減少,被賣空資產的價值用於總淨清算價值。從一側移除並添加到另一側可以保持平衡。人們似乎更喜歡增加現金,這可能會增加支出。在1.9.7.105版本中,經紀人已將默認行為更改為添加現金和移除價值。

Backtrader教程:倉位

資產的頭寸通常從策略中檢查: position (財產)或 getposition(data=None, broker=None) 這將返回策略在默認broker狀態下datas[0]的位置,由cerebro 倉位只是指示: 資產被持有size 平均價格price 它作為一種狀態,

Backtrader數據同步

在最新版本中,次要編號已從 8 移至 9,以指示即使已考慮相容性,也可能會對行為產生一些影響。 在 1.9.0.99 版中,使用 datetime 同步多個數據的整個機制已經重新設計(適用於下一個和一次模式)。

Backtrader教程:Cerebro - 優化 - 改進

backtrader版本1.8.12.99改進了在多處理過程中管理data feeds和結果的方式。

Backtrader教程:繪圖 - 日期範圍

該版本1.9.31.x 增加了製作部分繪圖的功能。 使用策略實例中保存的完整時間戳陣列的索引 或者使用實際datetime.date 或 datetime.datetime 實例來限制必須繪製的內容。 一切都超過標準cerebro.plot。

Backtrader交叉回溯測試陷阱

在backtrader 社區中 ,傾向於重複的事情是,用戶解釋了複製在例如 TradingView 中獲得的回溯測試結果的意願,這些天非常流行,或者其他一些回溯測試平臺。

Backtrader節省記憶體

1.3.1.92版本已經重新設計並完全實現了以前到位的記憶體節省方案,儘管沒有太多的吹捧和使用。

Backtrader教程:數據饋送 - 熊貓

注意 pandas 並且必須安裝其依賴項 支援Pandas Dataframes似乎受到很多人的關注,他們依賴於已經可用的解析代碼來分析不同的數據源(包括CSV)和Pandas提供的其他功能。 數據饋送的重要聲明。 注意 這些只是 聲明。不要盲目複製此代碼。

Backtrader熊貓數據饋送

在一些小的增強功能和一些OrderDict調整中,以獲得更好的Python 2.6支援, backtrader 的最新版本增加了對分析Pandas Dataframe或Time Series數據的支援。