Backtrader基金追踪

  |  

已經有一段時間了, backtrader已經在使用,可以說,專業,除了backtrader一些銀行和貿易公司的已知用途,用於Backtrader基金。

歷史

一群志同道合且相識已久的人決定走上開設(對沖)基金的道路,並以反向交易者為交易理念的基石。有一件事是不能放棄的:它必須受到 100% 的監管(但不是在開曼群島或類似的地方)

位置、遺產和網絡首先將重點放在歐盟,然後是西班牙,那裡(與其他一些地方一樣)立法允許 Umbrella Funds 託管子基金,從而能夠以更少的資金和數量創建一個完全受監管的基金的參與者。

並且……該基金已獲得西班牙監管機構 CNMV (Comisión Nacional del Mercado de Valores) 的批准,其 ISIN 為: ES0131444038 。鏈接:

對於那些能夠閱讀西班牙語的人, backtrader的使用記錄在官方基金傳單中。

對於那些可能在某個時候決定繼續前進的人來說,最重要的事情是:

  • 官僚作風緩慢,沿途會有很多問題

  • 跟踪一切(執行的操作、現金/淨資產價值水平、頭寸、槓桿)

  • 必須向監管機構報告(因此需要收集並妥善組織上述信息)

  • 保持在定義的風險/波動水平內不僅僅是一個指導方針

  • 管理 OPM(別人的錢)是一個真正的心理負擔。會有損失,也會有問題。無論這些問題的意圖和天真程度如何:它們都會產生影響。

backtrader是交易理念的基礎,它發現了一個新的應用領域:報告。用於控制風險/波動性的自定義分析器和指標減輕了管理負擔。

可能是因為我們老了(而且過時了),我們仍然更喜歡手動執行(自動執行將在未來的某個時候接管)

下面描述的功能是為了幫助管理基金和回測資金進出和績效不再是跟踪資產淨值的問題。

在版本1.9.52.121中, backtrader中的經紀人不僅以現金/價值形式跟踪會計,而且還像在基金中一樣,即:

  • 基金價值(實際基金份額)

  • 股份數量

有了這個,人們實際上可以模擬現金存款和現金提款,同時仍然跟踪實際業績,在常規會計中,這會被現金流入/流出所扭曲。

除了經紀商的變化之外,分析器觀察器(那些對資產淨值做一些事情的人)也進行了調整,以支持fund參數,以決定實際應該跟踪什麼。例如TimeReturn

...
cerebro.addanalyzer(bt.analyzers.TimeReturn)  # auto-detect broker mode
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=True)  # track fund value
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=False)  # track net asset value
...

這個基金追踪的是什麼?

想像一個用例(稍後在示例中),某人以1000貨幣單位開始,並在每個月的 15增加100貨幣單位。 12 個月後,帳戶中的總數為2200 。根據最初持倉計算的回報

以通常的方式計算回報,這意味著在沒有執行單一操作的情況下,年度回報的表現是: 120% 。當然,這是不對的。

為了緩解這個問題,無論賬戶的初始值如何,基金份額的價值(fundvalue)都設置為100.0 。有了這個和初始資產淨值( 1000貨幣單位),可以計算出基金份額的數量為

  • fundshares = net-asset-value / fundvalue

在這種情況下是1000 / 100.0 = 10 shares

每次增加現金,我們都會增加股票數量:

  • new_fund_shares = cash_addition / fundvalue

因為我們添加了100.0個貨幣單位並且沒有執行任何操作:

- ``100.0 / 100.0 = 1 share``

請注意,基金價值保持不變。快速轉發到年底,我們有以下內容:

  • 起始淨資產值: 1000

  • 最終資產淨值: 2200

  • 起始基金價值 = 100

  • 最終基金價值 = 100

  • 起始股數: 10

  • 最終股數: 22

現在,如果我們使用開始和結束基金價值來計算回報,並且因為它們是相同的,我們有一個: 0% ,這與現實相符。因為現金增加沒有改變

backtrader中使用資金跟踪

添加現金

首先,經紀人獲得了一種向系統規範添加現金的方法:

add_cash(cash)

例如在策略中使用它:

def next(self):

    if whatever:
        self.broker.add_cash(1000.0)

此方法必須用於跟踪現金進入和退出系統並正確跟踪基金價值。

自動的

在代理中激活它:

...
cerebro.broker.set_fundmode(True)
...

同時更改默認基金起始值:

...
cerebro.broker.set_fundmode(True, 10.0)  # the default is 100
...

或在獨立通話中:

...
cerebro.broker.set_fundmode(True)
cerebro.broker.set_fundstartval(10.0)  # the default is 100
...

激活默認模式並從上面返回TimeReturn分析器示例後:

...
# 1
cerebro.addanalyzer(bt.analyzers.TimeReturn)  # auto-detect broker mode
# 2
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=True)  # track fund value
# 3
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=False)  # track net asset value
...

12是等價的。但是應該選擇1 。如果希望進行比較,可以始終強制TimeReturn分析器不使用基金價值,而是跟踪淨資產價值

一個例子值一千字。在示例中,我們將使用如上所述的操作,但有一些額外的現金(資產的每股價值超過3000 )。初始現金水平為10000 ,這是backtrader的默認值,每月 15,將添加1000額外貨幣單位(使用循環Timer )。這將是 24 個月(這是backtrader中使用的標準數據樣本的大小)

無需任何操作

$ ./fund-tracker.py --broker fundmode=True --strat cash2add=1000 --cerebro writer=True --plot

圖形視圖

和文本輸出(為可讀性而設):

- timereturn:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: None
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 0.0
    - 2006-12-31: 0.0
.......................................................................
- timereturn1:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Params:
    ...
    - fund: True
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 0.0
    - 2006-12-31: 0.0
.......................................................................
- timereturn2:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: False
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 1.2
    - 2006-12-31: 0.545454545455

添加了 3 個TimeReturn分析器

  • 第一個有fund=None (默認),這意味著跟踪fundmode 在這種情況下為True

    它說年度回報是0.00.0 。由於我們沒有進行任何操作:好的

  • 第二個有fund= True ,這意味著總是使用fundvalue

    它說年度回報是0.00.0 。由於我們沒有進行任何操作:好的

  • 第三個有fund= False ,這意味著總是使用淨資產值

    它表示年回報率為1.2 (120%) 和0.54 (54%)。由於我們沒有進行任何操作:這顯然是錯誤的

該圖還包含 2 個新的觀察者FundValueFundShares ),它們可以查看即使資產淨值隨著每月現金的增加而增長,基金價值如何保持恆定為100.0 。同時,股票隨著每次現金增加而增長。

我們做個交易吧

與上述相同,但有些交易使用標準移動平均線交叉

$ ./fund-tracker.py --broker fundmode=True --strat cash2add=1000,trade=True --cerebro writer=True --plot

圖形視圖

和文本輸出(為可讀性而設):

- timereturn:
    ...
    - fund: None
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: -0.00642229824537
    - 2006-12-31: 7.78998679263e-05
.......................................................................
- timereturn1:
    ...
    - fund: True
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: -0.00642229824537
    - 2006-12-31: 7.78998679263e-05
.......................................................................
- timereturn2:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: False
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 1.19378185337
    - 2006-12-31: 0.546479045423

與之前相同的三個TimeReturn分析器。使用fund=Nonefund= True的結果合理,而使用fund= False的結果則以119%54%明顯不在圖表之列,這顯然不是移動平均線交叉提供的回報。

手動的

在這種情況下(這是經紀商的默認設置,即使經紀商正在跟踪基金的價值,也只有fund= True分析器才會使用該值。

僅使用文本輸出的快速運行:

$ ./fund-tracker.py --strat cash2add=1000,trade=True --cerebro writer=True

輸出:

- timereturn:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: None
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 1.19378185337
    - 2006-12-31: 0.546479045423
.......................................................................
- timereturn1:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: True
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: -0.00642229824537
    - 2006-12-31: 7.78998679263e-05
.......................................................................
- timereturn2:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
    - fund: False
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  - Analysis:
    - 2005-12-31: 1.19378185337
    - 2006-12-31: 0.546479045423

現在只有fund= TrueTimeReturn提供合理的結果。

結論

在經紀人中實施的新fundmode模式可以(自動/手動)在分析器中使用,允許使用反向交易者來模擬真實基金的內部運作或用例,例如在給定的時間間隔內持續投資資金。

示例使用

$ ./fund-tracker.py --help
usage: fund-tracker.py [-h] [--data0 DATA0] [--fromdate FROMDATE]
                       [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
                       [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]

Fund Tracking Sample

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: )

示例代碼

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt


class St(bt.SignalStrategy):
    params = dict(
        cash2add=None,
        cashonday=15,
        pfast=10,
        pslow=30,
        trade=False,
    )

    def __init__(self):
        self.add_timer(when=bt.Timer.SESSION_END, monthdays=[self.p.cashonday])

        sma1 = bt.ind.SMA(period=self.p.pfast)
        sma2 = bt.ind.SMA(period=self.p.pslow)
        signal = bt.ind.CrossOver(sma1, sma2)
        if self.p.trade:
            self.signal_add(bt.SIGNAL_LONGSHORT, signal)

    def notify_timer(self, timer, when, *args, **kwargs):
        # no need to check the timer, there is only one
        if self.p.cash2add is not None:
            self.broker.add_cash(self.p.cash2add)

    def next(self):
        pass


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

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.PercentSizer,
                     **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    cerebro.addobserver(bt.observers.FundValue)
    cerebro.addobserver(bt.observers.FundShares)

    ankwargs = dict(timeframe=bt.TimeFrame.Years)
    cerebro.addanalyzer(bt.analyzers.TimeReturn, **ankwargs)
    cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=True, **ankwargs)
    cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=False, **ankwargs)

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

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


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Fund Tracking Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)


if __name__ == '__main__':
    runstrat()

推薦閱讀

相關文章

Backtrader教程:分析儀

無論是回溯測試還是交易,能夠分析交易系統的性能是瞭解是否不僅獲得了利潤,而且是否在風險太大的情況下實現了利潤,或者與參考資產(或無風險資產)相比,是否真的值得付出努力的關鍵。 這就是物件家族的用武之Analyzer 地:提供對所發生事件甚至實際發生的事情的分析。

Backtrader教程:日誌記錄 - 編寫器

將以下內容寫出到流中: csv 流,

Backtrader教程:篩檢程式

此功能是 backtrader 的相對較新的補充,必須安裝到已經存在的內部結構中。這使得它不像希望的那樣靈活且100%功能齊全,但在許多情況下它仍然可以達到目的。 儘管該實現試圖允許隨插即用的篩檢程式連結,但預先存在的內部結構使得很難確保始終可以實現。因此,某些篩選器可能是連結的,而其他一些篩選器可能不是。

Backtrader教程:繪圖 - 日期範圍

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

Backtrader Python隐藏的细节

只有當遇到 backtrader 的真實使用者時,人們才能意識到平臺中使用的抽象和Python功能是否有意義。 在不撇開python的座右銘的情況下, backtrader 試圖為使用者提供盡可能多的控制權,同時通過將Python提供的隱藏功能付諸行動來簡化使用。 第一個示例是系列文章的第一篇。

Backtrader遞歸指標

backtrader的最初目標之一是:能夠快速製作指標原型以測試新想法它不一定是一個完美的指標,但能夠快速輕鬆地開發它們確實會有所幫助。為了確認設計是正確的,反向交易者標準庫中的第一個指標是指數移動平均線(又名 EMA),其定義為:遞歸。

Backtrader傭金計劃

發佈 backtrader 使用示例使我對缺失的東西有了深刻的瞭解。

Backtrader現實咬合

上一篇文章設法複製了該BTFD 策略,發現真正的收益 16x 而不是 31x。 但正如複製期間所指出的: 不收取傭金 使用2x 槓桿不收取利息 這就提出了一個顯而易見的問題: 當收取傭金和利息時,這16x中有多少會存在? 幸運的是,前面的示例足夠靈活,可以對其進行試驗。

Backtrader熊貓數據饋送

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

Backtrader教程:數據饋送 - 多個時間幀

有時投資決策是使用不同的時間框架做出的: 每周評估趨勢 每天執行條目 或者5分鐘對60分鐘。 這意味著需要組合多個時間幀的數據backtrader 來支援這種組合。 對它的本機支持已經內置。