BackTrader 中文文档(十五)(3)

简介: BackTrader 中文文档(十五)

BackTrader 中文文档(十五)(2)https://developer.aliyun.com/article/1505364

该代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import datetime
import backtrader as bt
class BaseStrategy(bt.Strategy):
    params = dict(
        fast_ma=10,
        slow_ma=20,
    )
    def __init__(self):
        # omitting a data implies self.datas[0] (aka self.data and self.data0)
        fast_ma = bt.ind.EMA(period=self.p.fast_ma)
        slow_ma = bt.ind.EMA(period=self.p.slow_ma)
        # our entry point
        self.crossup = bt.ind.CrossUp(fast_ma, slow_ma)
class ManualStopOrStopTrail(BaseStrategy):
    params = dict(
        stop_loss=0.02,  # price is 2% less than the entry point
        trail=False,
    )
    def notify_order(self, order):
        if not order.status == order.Completed:
            return  # discard any other notification
        if not self.position:  # we left the market
            print('SELL@price: {:.2f}'.format(order.executed.price))
            return
        # We have entered the market
        print('BUY @price: {:.2f}'.format(order.executed.price))
        if not self.p.trail:
            stop_price = order.executed.price * (1.0 - self.p.stop_loss)
            self.sell(exectype=bt.Order.Stop, price=stop_price)
        else:
            self.sell(exectype=bt.Order.StopTrail, trailamount=self.p.trail)
    def next(self):
        if not self.position and self.crossup > 0:
            # not in the market and signal triggered
            self.buy()
class ManualStopOrStopTrailCheat(BaseStrategy):
    params = dict(
        stop_loss=0.02,  # price is 2% less than the entry point
        trail=False,
    )
    def __init__(self):
        super().__init__()
        self.broker.set_coc(True)
    def notify_order(self, order):
        if not order.status == order.Completed:
            return  # discard any other notification
        if not self.position:  # we left the market
            print('SELL@price: {:.2f}'.format(order.executed.price))
            return
        # We have entered the market
        print('BUY @price: {:.2f}'.format(order.executed.price))
    def next(self):
        if not self.position and self.crossup > 0:
            # not in the market and signal triggered
            self.buy()
            if not self.p.trail:
                stop_price = self.data.close[0] * (1.0 - self.p.stop_loss)
                self.sell(exectype=bt.Order.Stop, price=stop_price)
            else:
                self.sell(exectype=bt.Order.StopTrail,
                          trailamount=self.p.trail)
class AutoStopOrStopTrail(BaseStrategy):
    params = dict(
        stop_loss=0.02,  # price is 2% less than the entry point
        trail=False,
        buy_limit=False,
    )
    buy_order = None  # default value for a potential buy_order
    def notify_order(self, order):
        if order.status == order.Cancelled:
            print('CANCEL@price: {:.2f} {}'.format(
                order.executed.price, 'buy' if order.isbuy() else 'sell'))
            return
        if not order.status == order.Completed:
            return  # discard any other notification
        if not self.position:  # we left the market
            print('SELL@price: {:.2f}'.format(order.executed.price))
            return
        # We have entered the market
        print('BUY @price: {:.2f}'.format(order.executed.price))
    def next(self):
        if not self.position and self.crossup > 0:
            if self.buy_order:  # something was pending
                self.cancel(self.buy_order)
            # not in the market and signal triggered
            if not self.p.buy_limit:
                self.buy_order = self.buy(transmit=False)
            else:
                price = self.data.close[0] * (1.0 - self.p.buy_limit)
                # transmit = False ... await child order before transmission
                self.buy_order = self.buy(price=price, exectype=bt.Order.Limit,
                                          transmit=False)
            # Setting parent=buy_order ... sends both together
            if not self.p.trail:
                stop_price = self.data.close[0] * (1.0 - self.p.stop_loss)
                self.sell(exectype=bt.Order.Stop, price=stop_price,
                          parent=self.buy_order)
            else:
                self.sell(exectype=bt.Order.StopTrail,
                          trailamount=self.p.trail,
                          parent=self.buy_order)
APPROACHES = dict(
    manual=ManualStopOrStopTrail,
    manualcheat=ManualStopOrStopTrailCheat,
    auto=AutoStopOrStopTrail,
)
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.FixedSize, **eval('dict(' + args.sizer + ')'))
    # Strategy
    StClass = APPROACHES[args.approach]
    cerebro.addstrategy(StClass, **eval('dict(' + args.strat + ')'))
    # 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=(
            'Stop-Loss Approaches'
        )
    )
    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')
    # Strategy to choose
    parser.add_argument('approach', choices=APPROACHES.keys(),
                        help='Stop approach to use')
    # 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()

开发递归指标(带种子)

原文:www.backtrader.com/blog/posts/2018-01-27-recursive-indicators/recursive-indicator/

backtrader 的最初目标之一是:

  • 能够快速原型化指标以测试新想法

它不必是一个完美的指标,但能够快速轻松地开发它们确实有所帮助。为了确认设计是正确的,backtrader标准武器库中的第一个指标之一是指数移动平均线(又称EMA),根据定义是:递归

注意

小知识:您可能会想象第一个指标是简单移动平均线

由于在backtrader 社区中发布了如何开发递归指标的问题,让我们快速开发一个ExponentialMovingAverage指标。

像这样的递归指标

  • 它使用先前的值来计算当前值

您可以在Wikipedia - 指数移动平均线中看到数学示例

如果您足够勇敢读完所有内容,您会发现期间用于计算指数平滑。我们将使用它。

为了解决计算第一个值的难题,行业决定使用前period个值的简单平均值。

作为杠杆,我们将使用bt.indicators.PeriodN,它:

  • 已经定义了一个period参数
  • 通知框架关于最终用户使用的实际period

在此处查看其定义:文档 - 指标参考

然后让我们开发我们的EMA

import backtrader as bt
class EMA(bt.indicators.PeriodN):
    params = {'period': 30}  # even if defined, we can redefine the default value
    lines = ('ema',)  # our output line
    def __init__(self):
        self.alpha = 2.0 / (1.0 + self.p.period)  # period -> exp smoothing factor
    def nextstart(self):  # calculate here the seed value
        self.lines.ema[0] = sum(self.data.get(size=self.p.period)) / self.p.period
    def next(self):
        ema1 = self.lines.ema[-1]  # previous EMA value
        self.lines.ema[0] = ema1 * (1.0 - self.alpha) + self.data[0] * self.alpha

几乎比说更容易。关键在于在nextstart中提供种子值,其中

  • 当指标的最小预热期满足时将被调用一次。
    next相反,它将为系统传递的每个新数据值调用

nextstart的默认实现只是将工作委托给next,对于大多数指标(例如简单移动平均线)来说,这是正确的做法。但在这种情况下,重写并提供种子值是关键。

将其与数据一起绘制

作为一个移动平均线,如果指标绘制在计算平均值的数据的同一轴上会很好。因为我们从PeriodN继承了绘图的默认值(在文档中查看):

subplot=True

这当然意味着我们的指标将创建一个subplot(图表上的另一个轴)。这可以很容易地被覆盖。

import backtrader as bt
class EMA(bt.indicators.PeriodN):
    plot = dict(subplot=False)

完成。如果您想控制更多绘图选项,请查看文档 - 绘图

祝你好运!

2017

道琼斯工业平均指数 10 天连涨

原文:www.backtrader.com/blog/posts/2017-08-08-dow-10-day-streak/dow-10-day-streak/

这已经成为新闻了。道琼斯工业平均指数创下了历史新高,已经连续上涨了 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 天的日子在百分比方面略小,分别是 1955 年、1964 年和 1965 年
  • 今年有另一个长达 12 天的排名第二的连涨

之后会发生什么?

即使表格已经显示了持续上涨日结束后的回撤和相对回撤(从上涨日开始计算,因此可能 > 100%),但最好通过视觉方式来回答问题。

图表很快就会显示出来:

  • 这样的长连涨似乎表明了强劲,不会真的预期到有大的回撤作为反应

但是等等!!!

在排名第 1 和第 2 的 remarkable 日期中,我们有:

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 中文文档(十五)(4)https://developer.aliyun.com/article/1505366

相关文章
|
Unix 索引 Python
BackTrader 中文文档(一)(2)
BackTrader 中文文档(一)
818 0
|
索引
BackTrader 中文文档(六)(2)
BackTrader 中文文档(六)
299 0
conda常用操作和配置镜像源
conda常用操作和配置镜像源
32581 0
|
调度
BackTrader 中文文档(十五)(2)
BackTrader 中文文档(十五)
329 0
BackTrader 中文文档(一)(4)
BackTrader 中文文档(一)
279 0
|
机器学习/深度学习 算法 机器人
BackTrader 中文文档(一)(1)
BackTrader 中文文档(一)
1346 0
|
存储 算法 索引
BackTrader 中文文档(三)(1)
BackTrader 中文文档(三)
604 0
|
算法 索引 Python
BackTrader 中文文档(十五)(4)
BackTrader 中文文档(十五)
248 0
|
Docker 容器
docker设置国内镜像源
docker设置国内镜像源
41432 5
|
存储 缓存 Shell
BackTrader 中文文档(二)(3)
BackTrader 中文文档(二)
592 0

热门文章

最新文章