美文网首页
vector-bt 例程介绍

vector-bt 例程介绍

作者: dodorado | 来源:发表于2018-07-05 14:21 被阅读0次
    import vectorbt as vbt
    import numpy as np
    import pandas as pd
    
    import matplotlib
    from matplotlib import pyplot as plt
    matplotlib.rcParams['figure.figsize'] = (12, 5)
    

    数据

    从 cryptocurrency exchange Poloniex 处获取 OHLC 数据

    # period = 300, 900, 1800, 7200, 14400, or 86400
    ohlc_df = vbt.data.load_cryptopair('USDT_ETH', vbt.data.ago_dt(days=180), vbt.data.now_dt(), period=vbt.data.Period.D1)
    ohlc_df.head()
    

    done. 1.71s


    # No future data
    rate_sr = ohlc_df.O
    
    # Fees and slippage
    # 手续费和滑点
    investment = 1000
    fees = 0.0025
    slippage_factor = 0.25
    slippage = (ohlc_df['H'] - ohlc_df['L']) * slippage_factor / rate_sr
    
    vbt.graphics.plot_line(rate_sr)
    

    输出:

       count        mean         std    min  25%       50%        75%      max
    O  180.0  713.444128  233.235322  369.0  529.683513  681.0821  858.69125   1382.0  
    
    价格曲线

    indicators 指标

    计算均线 EMA 指标

    fast_ma_sr = vbt.indicators.EMA(rate_sr, span=5)
    slow_ma_sr = vbt.indicators.EMA(rate_sr, span=21)
    

    signals 交易信号

    根据条件计算交易信号

    1. 当快速均线上穿慢速均线时,做多,反之,做空
    ma_entries = vbt.signals.DMAC_entries(fast_ma_sr, slow_ma_sr)
    ma_exits = vbt.signals.DMAC_exits(fast_ma_sr, slow_ma_sr)
    

    多空向量都是0/1序列,方便进行快速向量运算。

    因为我们要找均线交叉点,所以我们只需要这两个向量0/1序列的第一个信号。

    ma_entries = vbt.bitvector.first(ma_entries)
    ma_exits = vbt.bitvector.first(ma_exits)
    
    1. 无论何时,如果价格下跌10%就做空。
    # trailstop_exits = vbt.signals.trailstop_exits(rate_sr, ma_entries, 0.1 * rate_sr)
    

    结合均线退出策略和跟踪止损策略(trailing stop)

    # ma_exits = vbt.bitvector.OR(ma_exits, trailstop_exits)
    # ma_exits = vbt.bitvector.first(ma_exits)
    

    如果还要应用其它的策略,生成你自己的 bit 向量然后使用 vector.AND/OR/XOR 进行结合。

    positions 头寸

    从多空策略向量生成头寸向量 Generete positions out of both vectors (merge and reduce).

    pos_sr = vbt.positions.from_signals(rate_sr, ma_entries, ma_exits)
    pos_sr.head()
    

    输出:

                    O
    date
    2018-01-05      1
    2018-01-18     -1
    2018-01-29      1
    2018-02-02     -1
    2018-02-18      1
    dtype: int32
    
    ff = pd.DataFrame(ma_entries).set_index(rate_sr.index)
    ff[ff==1].dropna()
    

    输出:

                    O
    date
    2018-01-05    1.0
    2018-01-29    1.0
    2018-02-18    1.0
    2018-02-20    1.0
    2018-04-14    1.0
    
    ff = pd.DataFrame(ma_exits).set_index(rate_sr.index)
    ff[ff==1].dropna()
    

    输出:

                    O
    date
    2018-01-18    1.0
    2018-02-02    1.0
    2018-02-19    1.0
    2018-02-21    1.0
    2018-05-23    1.0
    
    pos_sr
    
    date
    2018-01-05    1
    2018-01-18   -1
    2018-01-29    1
    2018-02-02   -1
    2018-02-18    1
    2018-02-19   -1
    2018-02-20    1
    2018-02-21   -1
    2018-04-14    1
    2018-05-23   -1
    dtype: int32
    

    头寸序列是一个二元序列,1表示买入,-1表示卖出。因为我们想更清楚地测评一个策略(只考虑策略逻辑对净值的影响,不考虑投资额度的大小和分配),所以头寸序列中的一行只有多或空,没有两个同时的头寸。

    买卖点的可视化

    vbt.positions.plot(rate_sr, pos_sr)
    

    输出:

       count       mean         std         min   25%    50%        75%  \
    0    5.0 -19.503679  137.281554 -205.209166 -61.0 -57.21  76.436671   
    
              max  
    0  149.464099  
    
    买卖点
    注:绿色三角为买,红色倒三角为卖

    equity 净值

    基于交易手续费和滑点,从头寸生成净值。

    equity_sr = vbt.equity.from_positions(rate_sr, pos_sr, investment, fees, slippage)
    equity_sr.head()
    

    输出:

    date
    2018-01-04            NaN
    2018-01-05     973.100223
    2018-01-06     999.309720
    2018-01-07    1040.616509
    2018-01-08    1155.616055
    dtype: float64
    

    在建立第一个头寸之前的净值都是 NaN。

    基准和报价净值(base and quote equities)的可视化

    vbt.equity.plot(rate_sr, equity_sr)
    

    输出:

    base: equity - hold
       count       mean         std         min       25%         50%         75%  \
    0  179.0  44.435796  169.491093 -305.699824 -73.27329  110.092595  153.878813   
    
              max  
    0  323.737977  
    
    净值曲线
    注:红色为跑输股价,绿色为跑赢股价
    quote: equity - hold
       count      mean       std      min       25%       50%       75%       max
    0  179.0  0.130039  0.277426 -0.33907 -0.077732  0.193859  0.267966  0.769767
    
    净值-报价 曲线

    returns 回报率

    生成回报率

    returns_sr = vbt.returns.from_equity(equity_sr)
    returns_sr.head()
    

    输出:

    date
    2018-01-04    0.000000
    2018-01-05    0.000000
    2018-01-06    0.026934
    2018-01-07    0.041335
    2018-01-08    0.110511
    dtype: float64
    

    回报率可视化

    vbt.returns.plot(vbt.returns.resample(returns_sr, 'W'))
    

    输出:

       count      mean       std       min  25%  50%  75%       max
    0   27.0 -0.003166  0.132375 -0.292969  0.0  0.0  0.0  0.374422
    
    回报率曲线

    performance 绩效

    显示多个基于回报的KPI总结。

    vbt.performance.summary(returns_sr)
    

    输出:

    distribution         count         180.000000
                         mean           -0.000616
                         std             0.038778
                         min            -0.197584
                         25%             0.000000
                         50%             0.000000
                         75%             0.000000
                         max             0.133985
    performance          profit         -0.219902
                         avggain         0.049820
                         avgloss         0.053396
                         winrate         0.500000
                         expectancy     -0.003547
                         maxdd           0.577767
    risk/return profile  sharpe         -0.015882
                         sortino        -0.014195
    dtype: float64
    

    optimizer.gridsearch 模型优化/参数搜索

    传统的的优化方法就是网格搜索(或者说穷举)。这种方法穷举生成所有可能的参数组合。

    这种方法的优点:

    • 容易实现
    • 二维组合可以通过热力图可视化
    • 可以用于发现未知的组合模式
    • 可以高度并行化处理

    但是也有一些不足之处:

    • 不够灵活,难以适应不断变化的金融市场
    • 易于“过拟合”
    • 没有中间回馈信号

    网格搜索包括 3-4 阶段:

    层级 目的 模块 结构
    1 计算头寸、净值、回报 srmap {param: pd.Series}
    2 计算 KPIs nummap pd.Series
    3 (可选) 组合多个KPI指标为一个分值并比较 scoremap pd.Series
    4 构造热力图以查看某些未知的模式 matrix pd.DataFrame

    最后我们就可以比较不同交易策略的绩效水平了。

    L1 第一阶段

    srmap 模块

    import vectorbt.optimizer.gridsearch as grids
    

    对一系列均线组合计算回报。

    1. 先计算所有的均线。
    
    # Init
    ma_func = lambda span: vbt.indicators.EMA(rate_sr, span=span)
    min_ma, max_ma, step = 1, 100, 1
    fees = 0.0025
    
    # Cache moving averages
    param_range = grids.params.range_params(min_ma, max_ma, step)
    ma_cache = dict(grids.srmap.from_func(ma_func, param_range))
    

    cores: 8
    processes: 1
    starmap: False
    calcs: 100 (~5.26s) ..
    done. 0.03s

    注:上面输出中的 calcs 的意思是需计算次数和预计总的计算时间
    而 done 后面是使用多核处理实际花费的时间,这里可以看到其加速了170倍!
    下面计算头寸需要5050次计算,其加速超过700倍!!!

    ma_cache Dict数据类型:

    • keys: 1..100

    • Values: key对应的日均线,Series类型数据,如 key=5:MA5

      1. 对每个均线组合,生成头寸向量。
    # Params
    ma_space = grids.params.combine_rep_params(min_ma, max_ma, step, 2)
    
    # Func
    def ma_positions_func(fast_ma, slow_ma):
        # Cache
        fast_ma_sr = ma_cache[fast_ma]
        slow_ma_sr = ma_cache[slow_ma]
        # Signals
        entries = vbt.signals.DMAC_entries(fast_ma_sr, slow_ma_sr)
        entries = vbt.bitvector.first(entries)
        exits = vbt.signals.DMAC_exits(fast_ma_sr, slow_ma_sr)
        exits = vbt.bitvector.first(exits)
        # Positions
        pos_sr = vbt.positions.from_signals(rate_sr, entries, exits)
        return pos_sr
    
    ma_positions_srmap = grids.srmap.from_func(ma_positions_func, ma_space)
    

    cores: 8
    processes: 1
    starmap: True
    calcs: 5050 (~1453.19s) ..
    done. 2.07s

    ma_positions_srmap 为Dict数据类型:

    • key: 均线组合 (fast, slow) tuple类型,如,(5, 21)为5日21日均线组合

    • value: 头寸向量,Series类型

      1. 对每个头寸向量,生成回报向量。

    我们需要分开计算头寸和回报,因为我们需要每个均线组合的头寸向量长度来进行随机映射。

    def ma_returns_func(fast_ma, slow_ma):
        # Equity
        pos_sr = ma_positions_srmap[(fast_ma, slow_ma)]
        equity_sr = vbt.equity.from_positions(rate_sr, pos_sr, investment, fees, slippage)
        # Returns
        returns_sr = vbt.returns.from_equity(equity_sr)
        return returns_sr
    
    ma_returns_srmap = grids.srmap.from_func(ma_returns_func, ma_space)
    

    cores: 8
    processes: 1
    starmap: True
    calcs: 5050 (~1451.34s) ..
    done. 5.68s

    ma_returns_srmap 为Dict数据类型:

    • key: 均线组合 (fast, slow) tuple类型,如,(5, 21)为5日21日均线组合
    • value: 回报向量,Series类型,这个Series是每天的回报,如0.012表示回报1.2%

    为每个均线组合生成同样长度的随机头寸向量和相应的回报向量。

    # Params
    random_space = [(fma, sma, len(np.flatnonzero(ma_positions_srmap[(fma, sma)].values))) for fma, sma in ma_space]
    
    # Func
    def random_returns_func(slow_ma, fast_ma, n):
        # Positions
        pos_sr = vbt.positions.random(rate_sr, n)
        # Equity
        equity_sr = vbt.equity.from_positions(rate_sr, pos_sr, investment, fees, slippage)
        # Returns
        returns_sr = vbt.returns.from_equity(equity_sr)
        return returns_sr
    
    random_returns_srmap = grids.srmap.from_func(random_returns_func, random_space)
    

    cores: 8
    processes: 1
    starmap: True
    calcs: 5050 (~1445.95s) ..
    done. 7.49s

    random_returns_srmap 为Dict数据结构:

    • keys tuple类型, (短线的天数,长线的天数,交易信号数量)
    • value Series类型,每天的收益率

    L2 第二阶段

    nummap 模块

    对每个回报向量,计算 KPI 指标。

    注:此处对回报进行了累积得到了累积回报

    if_i_hold = vbt.performance.profit(rate_sr.pct_change())
    profit = lambda r: vbt.performance.profit(r) - if_i_hold
    ma_profit_nummap = grids.nummap.from_srmap(ma_returns_srmap, vbt.performance.profit)
    

    cores: 8
    processes: 1
    starmap: False
    calcs: 5050 (~4.29s) ..
    done. 2.42s
    min (1, 7): -0.7055079244549305
    max (11, 13): 0.033964270132262

    sharpe = lambda r: vbt.performance.sharpe(r, nperiods=252)
    ma_sharpe_nummap = grids.nummap.from_srmap(ma_returns_srmap, sharpe)
    

    cores: 8
    processes: 1
    starmap: False
    calcs: 5050 (~1411.08s) ..
    done. 0.97s
    min (1, 7): -1.909136002820758
    max (11, 13): 0.3736374454176382

    random_profit_nummap = grids.nummap.from_srmap(random_returns_srmap, vbt.performance.profit)
    

    cores: 8
    processes: 1
    starmap: False
    calcs: 5050 (~4.86s) ..
    done. 2.38s
    min (2, 4, 31): -0.8685124600763209
    max (75, 85, 4): 1.6696239049805262

    比较EMA均线策略和随机策略的各个分位数分布情况。

    grids.nummap.compare_quantiles(ma_profit_nummap, random_profit_nummap)
    
               count      mean       std       min       25%       50%       75%  \
    nummap     5050.0 -0.204439  0.132031 -0.705508 -0.306606 -0.216076 -0.116936   
    benchmark  5050.0 -0.295012  0.305769 -0.868512 -0.521892 -0.350998 -0.122543   
                   max  
    nummap     0.033964  
    benchmark  1.669624  
    
    分位数分布情况
    注:X号标识了最好和最坏情况
    绿色表示均线跑赢随机,红色相反
    从这里看出,大约75%的情况均线策略是胜过随机策略的;随机策略可能出现比较极端的情况;最后,只做多头,在股价大幅下跌50%的情况下,两种策略都很少有取得正收益的参数配置 :-(

    比较 KPI 分布情况。

    cmap = plt.cm.rainbow_r
    norm = plt.Normalize()
    grids.nummap.compare_hists(ma_profit_nummap, random_profit_nummap, 50, cmap, norm)
    
               count      mean       std       min       25%       50%       75%  \
    nummap     5050.0 -0.204439  0.132031 -0.705508 -0.306606 -0.216076 -0.116936   
    benchmark  5050.0 -0.295012  0.305769 -0.868512 -0.521892 -0.350998 -0.122543   
                   max  
    nummap     0.033964  
    benchmark  1.669624  
    
    均线策略KPI分布 随机策略KPI分布

    L3 第三阶段

    scoremap 模块

    考虑 KPI 多个指标的权重,生成一个1-100的分数。

    ma_scoremap = grids.scoremap.from_nummaps([ma_profit_nummap, ma_sharpe_nummap], [2/3, 1/3], [False, False])
    

    done. 0.01s
    min (1, 7): 1.0
    max (11, 13): 99.99999999999999

    L4 第四阶段

    matrix 模块

    把二维参数网格映射到矩阵向量

    ma_matrix = grids.matrix.from_nummap(ma_profit_nummap, symmetric=True).fillna(0)
    

    done. 1.94s

    显示为热力图

    cmap = plt.cm.rainbow_r
    norm = plt.Normalize()
    matplotlib.rcParams['figure.figsize'] = (12, 9)
    grids.matrix.plot(ma_matrix, cmap, norm)
    matplotlib.rcParams['figure.figsize'] = (12, 5)
    
        count      mean       std       min       25%       50%       75%  \
    0  10000.0 -0.206483  0.131083 -0.705508 -0.306749 -0.223663 -0.116936   
           max  
    0  0.033964  
    
    各参数组合热力图

    相关文章

      网友评论

          本文标题:vector-bt 例程介绍

          本文链接:https://www.haomeiwen.com/subject/spfyuftx.html