Backtrader唐鐘斯10天連勝

  |  

它已成為新聞。DJI正在創下歷史新高,已經連續10個上漲日和9個歷史高點。例如,請參閱:

當然,許多人已經注意到道瓊斯指數處於這樣的狀態,這篇文章只是告訴我們它正在成為主流。但出現了一些問題:

  • 這是正常的還是非凡的?

  • 之後會發生什麼?

讓我們通過製作一個分析器來啟動 backtrader 來回答問題:分析情況並回答問題(請參閱下面的代碼)

我們的範例數據包含5923 交易時段。讓我們看看當前的連勝適合在哪裡。

這是正常的還是非凡的?

執行我們的代碼表明,10 連續幾天至少是非凡的,如果不是非凡的話:

$ ./updaystreaks.py --data 099I-DJI --upstreak hilo=True
            count  rank  upstreak       upleg   upleg %  drawdown  rel drawdown
1987-01-02      1     1        13  219.069946  0.116193  0.017616      0.171407
2017-02-09      2     2        12  822.109375  0.041074  0.001875      0.047548
1970-11-19      3     2        12   66.900024  0.088986  0.010321      0.127055
1929-06-20      4     2        12   32.000000  0.101716  0.031134      0.340625
1991-12-18      5     3        11  315.100098  0.109167  0.011113      0.113614
1955-01-18      6     3        11   22.200012  0.057290  0.014334      0.265765
2017-07-25      7     4        10  622.289062  0.028949       NaN           NaN
2013-03-01      8     4        10  488.959961  0.034801  0.008102      0.240919
1996-11-04      9     4        10  348.839844  0.058148  0.004792      0.087605
1973-07-16     10     4        10   53.600037  0.060695  0.095935      1.686565
1959-11-17     11     4        10   31.599976  0.049945  0.011216      0.237342
1959-06-24     12     4        10   36.200012  0.057680  0.020649      0.381215
1955-08-23     13     4        10   25.400024  0.056344  0.008772      0.165353
1933-03-03     14     4        10   12.600002  0.250497  0.142415      0.730158
1920-12-29     15     4        10    8.099998  0.119118  0.022339      0.209876
2016-07-08     16     5         9  778.378906  0.043688  0.016552      0.396003
1996-05-08     17     5         9  334.369629  0.061755  0.002442      0.041990
1989-07-03     18     5         9  141.890137  0.058804  0.007179      0.129677
1968-04-23     19     5         9   38.000000  0.043123  0.070535      1.736842
1967-04-13     20     5         9   49.700012  0.059061  0.006593      0.118713
1967-01-03     21     5         9   55.799988  0.071603  0.006321      0.094982
1965-01-22     22     5         9   18.500000  0.020838  0.031326      1.540541
1964-03-06     23     5         9   19.600037  0.024506  0.016127      0.678570
1955-06-15     24     5         9   12.399994  0.028343  0.005537      0.201613
1955-04-05     25     5         9   16.299988  0.039553  0.010465      0.276074
1954-09-01     26     5         9   18.599976  0.055822  0.009325      0.177419
1945-04-06     27     5         9    9.000000  0.058140  0.008526      0.155555
1929-02-18     28     5         9   21.800018  0.072812  0.086005      1.279815
1921-10-18     29     5         9    4.300003  0.061871  0.008130      0.139536

目前的連勝尚未結束,排名(並列)在第 4位。要注意:

  • 當連勝為 10 天或更長時間時,以 % 為單位的上挽是最小的

  • 連續9天上升的三(3)天在百分比上略小,可追溯到1955年,1964年和1965年

  • 今年又一次連續12天排名第二

之後會發生什麼?

即使表格已經顯示了連勝結束后的回撤和相對回撤(從連勝開始,因此可以>100%),這個問題在視覺上得到更好的回答。

圖表很快顯示:

  • 如此長的連勝似乎表明實力,沒有大的回撤,這是真正的預期,因為反應

但是等等!!!

排名第一,也排名第二,我們有非凡的日期:

            count  rank  upstreak       upleg   upleg %  drawdown  rel drawdown
1987-01-02      1     1        13  219.069946  0.116193  0.017616      0.171407
2017-02-09      2     2        12  822.109375  0.041074  0.001875      0.047548
1970-11-19      3     2        12   66.900024  0.088986  0.010321      0.127055
1929-06-20      4     2        12   32.000000  0.101716  0.031134      0.340625
...

的確,因為19871929後來真的有非常大的熊腿。但不是像統計數據所示的連勝之後立即:相對回撤沒有超過100%,因此在這些連勝結束后出現了新的高點。

代碼

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

import argparse
import collections
import datetime
import itertools

import matplotlib.pyplot as plt
import pandas as pd

import backtrader as bt


class UpStreak(bt.Analyzer):
    params = dict(
        sep=',',
        hilo=False,
    )

    def __init__(self):
        self.upday = bt.ind.UpDayBool()
        self.curdt = None  # streak start date

        self.incs = dict()  # upleg in points
        self.pincs = dict()  # upleg in percentage
        self.close0 = dict()  # starting price for upleg
        self.peaks = collections.deque()  # endng price for upleg
        self.ddown = dict()  # absolute drawdowns
        self.ddownrel = dict()  # relative drawdown (% of upleg retraced)

        self.rets = collections.defaultdict(int)  # holds main results

    def next(self):
        curclose = self.data.close[0]
        lastclose = self.data.close[-1]

        self.peaks.append((None, None))
        while True:
            dt, peak = self.peaks.popleft()
            if dt is None:
                break  # all elements seen

            if peak > curclose:  # peak not overdone, update drawdown
                ddown = 1.0 - curclose / peak
                self.ddown[dt] = max(self.ddown[dt], ddown)
                self.peaks.append((dt, peak))  # not done yet

                inc = self.incs[dt]
                fall = peak - curclose
                ddownrel = fall / inc
                self.ddownrel[dt] = max(self.ddownrel[dt], ddownrel)

        if self.upday:
            if self.curdt is None:  # streak begins
                self.curdt = self.strategy.datetime.date()
                if self.p.hilo:
                    lastclose = self.data.low[-1]
                self.close0[self.curdt] = lastclose

            self.incs[self.curdt] = inc = curclose - self.close0[self.curdt]
            self.pincs[self.curdt] = inc / self.close0[self.curdt]
            self.rets[self.curdt] += 1  # update current streak
        else:
            if self.curdt is not None:  # streak ends
                if self.p.hilo:
                    lastclose = self.data.high[-1]

                inc = self.incs[self.curdt]
                fall = lastclose - curclose
                self.ddownrel[self.curdt] = fall / inc
                self.ddown[self.curdt] = 1.0 - curclose / lastclose
                self.peaks.append((self.curdt, lastclose))

                self.curdt = None

    def stop(self):
        s = sorted(
            self.rets.items(),
            reverse=True,
            key=lambda item: (item[1], item[0])
        )
        # keep it in dict format
        self.rets = collections.OrderedDict(s)

        self.s = collections.OrderedDict(s)

        self.headers = [
            'date',
            'count', 'rank', 'upstreak',
            'upleg', 'upleg %',
            'drawdown', 'rel drawdown',
        ]

        i = 0
        count = itertools.count(1)
        last = float('inf')
        for dt, streak in self.s.items():
            if streak < last:
                i += 1
                last = streak
            ddown = self.ddown.get(dt, None)
            ddownrel = self.ddownrel.get(dt, None)
            inc = self.incs.get(dt, None)
            pinc = self.pincs.get(dt, None)

            self.s[dt] = [
                next(count), i,
                streak,
                inc, pinc,
                ddown, ddownrel
            ]

    def get_dataframe(self):
        return pd.DataFrame.from_items(
            self.s.items(),
            orient='index',
            columns=self.headers[1:],  # skip index
        )

    def print_ranking(self):
        i = 0
        last = float('inf')
        print(self.p.sep.join(self.headers))

        for dt, items in self.s.items():
            print(
                self.p.sep.join(
                    str(x) for x in itertools.chain([dt], items)
                )
            )


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

    cerebro = bt.Cerebro()

    kwargs = dict()  # Data feed kwargs

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

    fromdate = kwargs.get('fromdate', datetime.date.min)

    store = bt.stores.VChartFile()
    data = store.getdata(dataname=args.data, **kwargs)
    cerebro.adddata(data)

    cerebro.addanalyzer(UpStreak, **eval('dict(' + args.upstreak + ')'))
    result = cerebro.run()
    st0 = result[0]

    a = st0.analyzers.upstreak

    # Plot some things
    # pd.set_option('display.max_columns', 500)
    pd.set_option('display.expand_frame_repr', False)
    df = a.get_dataframe()
    up = df['upstreak']

    up9 = df[up >= 9]
    print(up9)

    up7 = df[up >= 7]
    x = up7['upstreak']
    y = up7['rel drawdown'] * 100.0

    plt.scatter(x, y)
    plt.ylabel('% Relative Drawdown')
    plt.xlabel('Updays streak')
    plt.title('DJI Relative Drawdown after N consecutive UpDays')
    plt.show()

    # Plot some things
    y = up7['drawdown'] * 100.0
    plt.ylabel('% Absolute Drawdown')
    plt.xlabel('Updays streak')
    plt.title('DJI Drawdown after N consecutive UpDays')
    plt.scatter(x, y)
    plt.show()


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

    parser.add_argument('--data', default='', required=True,
                        help='Data Ticker')

    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('--upstreak', 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細分傭金計劃

不久前,委員會計劃的實施進行了重新設計。最重要的是:涉及的部分返工: 保留原始的傭金資訊類和行為 打開大門,輕鬆創建使用者定義的傭金 將格式 xx% 作為新傭金方案的預設值,而不是 0.xx(只是一個品味問題),保持行為可配置 擴展委員會概述了基本要素。

Backtrader按日線交易

似乎在世界某個地方有一種权益(Interest)可以總結如下: 使用每日柱線引入訂單,但使用開盤價 這來自工單#105订单执行逻辑与当前数据和#101动态投注计算中的對話 backtrader 嘗試盡可能保持現實,並且在處理每日柱線時適用以下前提: 當每日柱被評估時,柱線已經結束 這是有道理的,

Backtrader教程:經紀人 - 開倉作弊

“發佈”1.9.44.116 添加了對 Cheat-On-Open的支援。這似乎是那些全力以赴的人的需求功能,他們在酒吧 close 后進行了計算,但希望與 open 價格相匹配。 當開盤價跳空(上漲或下跌,取決於是否buysell有效)並且現金不足以進行全面運營時,這樣的用例就會失敗。這將強制代理拒絕該操作。

Backtrader教程:經紀人 - 體積灌裝 - 灌裝機

backtrader經紀商類比在使用volume執行訂單時具有預設策略: 忽略 volume 這是基於2個前提: 在市場中交易的流動性足以一次性完全吸收買入/賣出訂單 真正的 volume 匹配需要真正的狼 一個簡單的例子是Fill or Kill 訂單。

Backtrader教程:倉位

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

Backtrader教程:數據饋送 - 擴展 (Extending DataFeed)

GitHub 中的問題實際上是在推動文檔部分的完成,或者説明我瞭解我是否backtrader 具有我從一開始就設想的易用性和靈活性以及在此過程中做出的決定。 在本例中為問題 #9。

Backtrader教程:觀察者 - 基準測試

工單 #89 是關於針對資產添加基準測試的。明智的是,人們實際上可能有一個策略,即使積極,也低於簡單地跟蹤資產所能提供的策略。

Backtrader教程:Cerebro - 優化 - 改進

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

Backtrader教程:數據饋送 - 雅虎

2017年5月,雅虎停止了現有的CSV格式歷史數據下載API。 一個新的API(這裡命名v7)很快被標準化並已實現。 這也帶來了實際CSV下載格式的變化。 使用 v7 API/格式 從版本1.9.49.116 開始,這是預設行為。

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

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