7. 回測您的第一個交易策略

qteasy是一個完全本地化部署和運行的量化交易分析工具包,具備以下功能:

  • 金融數據的獲取、清洗、存儲以及處理、可視化、使用

  • 量化交易策略的創建,並提供大量內置基本交易策略

  • 向量化的高速交易策略回測及交易結果評價

  • 交易策略參數的優化以及評價

  • 交易策略的部署、實盤運行

通過本系列教程,您將會通過一系列的實際示例,充分了解qteasy的主要功能以及使用方法。

7.1. 開始前的準備工作

在開始本節教程前,請先確保您已經掌握了下面的內容:

  • 完成qteasy的安裝並升級到最新版本,完成qteasy的初始化配置

  • 配置好本地數據源,掌握下載各種金融數據的方法,能夠將指數、股票的各種歷史價格數據、財務報表數據等下載到本地。

上一篇教程中,我介紹瞭如何配置本地數據源,查找、下載金融數據到本地,並從本地數據源中提取數據。如果還沒有完成這一步的朋友,請移步前一篇教程瞭解如何下載和操作數據。

7.2. 本節的目標

在本節中,我們將通過創建qteasy模塊來測試一個大小盤輪動交易策略,

大小盤輪動是一個非常基本而且常見的交易策略,這個交易策略抓住大盤股和小盤股往往上漲和下跌不同步的特點,在大盤股和小盤股之間輪流切換持有,以期望獲得更高的收益率。通過創建這個交易策略,可以非常方便地幫助我們瞭解如何使用qteasy創建交易策略,調用歷史價格回測交易策略,分析策略的表現並對策略進行改進。

在這裏,我們需要創建一個最簡單的輪動策略:在前面提到的兩個指數之間輪動,每天選擇未來可能的漲幅較大的指數持有:

  • 分別計算兩個指數在過去20天的漲幅,也就是今天的價格相對於20天前價格的漲幅

  • 選擇漲幅較大的那個指數,在第二天持有,同時賣掉漲幅較小的指數

\[::\]

7.3. 策略的實現

根據上述的策略思路,我們很容易在qteasy中實現這樣的輪動選股策略,因爲qteasy中已經內置了近70個交易策略,所有的內置策略都有獨特的名稱,直接引用名稱即可使用這些內置策略。qteasy中的所有交易策略都必須包含在一個名爲Operator(交易員)的對象中,交易員對象實際是一個策略的容器,可以理解爲一個交易員可以同時管理多個策略,並且同時運行這些策略來生成交易信號。

交易員對象可以直接通過qt.Operator()來創建,創建時傳遞strategies參數即可在創建時同時創建交易策略:

>>> import qteasy as qt
>>> op = qt.Operator(
...     strategies = 'ndayrate',  # 创建交易员对象时,同时创建一个交易策略“ndayrate”
...     run_freq='d',   # 交易策略的运行频率为每天运行一次
...     run_timing='close',  # 交易策略的运行时机为每天股票收盘时运行
... )

通過上面的代碼,我們已經在qteasy中創建了一個單因子選股策略(ndayrate),這個策略是一個內置選股策略,它根據“N日價格漲幅”來選股,它的選股邏輯是判斷股票池中所有股票的N日價格漲幅,並且根據價格漲幅選擇股票或資產(當然,選擇的方法是通過參數配置的,在下文中會提到)。

使用qt.built_ins()函數,可以查看內置策略的詳細介紹:

>>> qt.built_in_doc('ndayrate', print_out=True)

輸出如下:

以股票过去N天的价格或数据指标的变动比例作为选股因子选股
    基础选股策略:根据股票以前n天的股价变动比例作为选股因子

    策略参数:
        n: int, 股票历史数据的选择期
    信号类型:
        PT型: 百分比持仓比例信号
    信号规则:
        在每个选股周期使用过去N日内价格变动率作为选股因子进行选股
        通过以下策略属性控制选股方法:
        - max_sel_count:     float,  选股限额,表示最多选出的股票的数量,默认值: 0.5,表示选中50%的股票
        - condition:         str ,   确定股票的筛选条件,默认值any
            - any        :默认值,选择所有可用股票
            - greater    :筛选出因子大于ubound的股票
            - less       :筛选出因子小于lbound的股票
            - between    :筛选出因子介于lbound与ubound之间的股票
            - not_between:筛选出因子不在lbound与ubound之间的股票
        - lbound:            float,  执行条件筛选时的指标下界, 默认值np.-inf
        - ubound:            float,  执行条件筛选时的指标上界, 默认值np.inf
        - sort_ascending:    bool,   排序方法,默认值: False,
            - True: 优先选择因子最小的股票,
            - False, 优先选择因子最大的股票
        - weighting:         str ,   确定如何分配选中股票的权重,默认值: even
            - even       :所有被选中的股票都获得同样的权重
            - linear     :权重根据因子排序线性分配
            - distance   :股票的权重与他们的指标与最低之间的差值(距离)成比例
            - proportion :权重与股票的因子分值成正比
    策略属性缺省值:
        默认参数: (14,)
        数据类型: close 收盘价,单数据输入
        窗口长度: 150
        参数范围: [(2, 150)]
    策略不支持参考数据,不支持交易数据

至此,一個Operator對象和交易策略就已經創建好了。

我們可以使用Operator.info()來查看交易員對象和交易策略的詳細資訊,同時,通過Operator.strategies屬性可以訪問其中的所有交易策略。

>>> op.info()
>>> stg = op.strategies[0]  # 获取op的第一个策略,下面的几种方法是等效的
>>> # stg = op[0]
>>> # stg = op['ndayrate']
>>> # stg = op.get_strategy_by_id('ndayrate')

輸出如下:

==============================Operator Information==============================
Name:        None
Run Mode:    batch - All history operation signals are generated before back testing
Groups:      1 Strategy(s) in 1 Group(s)

------------------------------------Group_1-------------------------------------
Signal Type: pt - Position Target, signal represents position holdings in percentage of total value
Run Timing:  close @ d - days
Strategies (1): ['ndayrate']
Signal blenders: ndayrate

------------------------------Strategies in group-------------------------------
stg_id          name                    parameters                              
--------------------------------------------------------------------------------
ndayrate        N-DAY RATE              (14,)                                   
================================================================================

通過交易策略的info()方法也可以查看更詳細的策略參數和資訊:

>>> stg.info()

輸出如下

============================= Strategy: N-DAY RATE =============================
Strategy FACTOR(N-DAY RATE)
Parameters: ['n'] = (14,)                                           
Date Types: close_ANY_d x 150                                       
----------------------------- Selection Properties -----------------------------
Max select count        50.0%
Sort Ascending          False
Weighting               even                                                    
Filter Condition        any                                                     
Filter ubound           inf
Filter lbound           -inf

從上面的資訊中可以看到,ndayrate策略有許多的可配置參數,通過調整這些參數,我們可以調整策略的選股方式,從而調整交易策略的表現。

接下來,我們還需要做一些最基本的設定,確保這個選股策略能按照我們的想法選股。Operator對象中的所有參數都可以通過op.set_parameter()方法來實現。

>>> op.set_parameter(0,   # 指定需要设置参数的交易策略:即设置策略0的参数
...                  sort_ascending=False,  # 设置选择涨幅最大的指数
...                  max_sel_count=1,  # 设置选股数量,每次最多从投资池里选择一支股票
...                  par_values=(20, ),  # 策略参数N=20,比较20日涨幅
...                  data_types=[qt.StgData('close',  # 使用收盘价计算涨幅
                                            freq='d',  # 使用每日收盘价
                                            asset_type='ANY',  # 适用于任何类型的资产
                                            use_latest_data_cycle=True,  # 使用最新的数据周期,每次选股数据包括当天收盘价在内
                                            window_length=25,  # 数据窗口长度为25天
                                            )],  
... )  

在上面的代碼段中,我們通過幾個簡單的參數設置選股策略的基本行爲:

  • sort_ascending=False:排序方式:該策略的操作方式是將選股指標排序後取前幾位,因爲需要取最大漲幅,因此需要降序排列,如果要取最小漲幅,則需要設置sort_ascending=True

  • max_sel_count=1:選股數量:這個參數控制選股因爲從兩個指數中固定二選一,因此設置此參數爲1

  • par_values=(20, ):策略參數值:這個策略使用一個可調參數N,表示表示根據N日漲幅選股,設置爲20即根據20日股價漲幅作爲選股因子

  • data_types=[qt.StgData(...)]:數據類型:決定策略使用何種數據來計算選股因子,這裏使用了一個StgData對象,該對象包含下面參數:

    • freq='d': 這裏我們使用收盤價計算價格漲幅,窗口長度設置爲25,這樣每一次策略運行的時候,都會讀取當日以及

    • asset_type='ANY':數據類型:該策略適用於任何類型的資產,當然也可以設置爲特定的資產類型,例如股票、指數、基金等等

    • use_latest_data_cycle=True:數據週期:該參數控制策略每次運行時使用的數據週期,設置爲True代表使用最新的數據週期,也就是每次選股時都包括當天的收盤價在內,如果設置爲False,則每次策略運行看到的數據都不包括當天的收盤價。在實際運行過程中,我們實際上是不可能使用當天的準確收盤價來交易的,因爲收盤後就無法交易了,不過,我們可以在收盤前一分鐘使用收盤前一分鐘的實時價格來交易,通常這個價格與收盤價非常接近,在這裏設置使用當天的收盤價是一種簡化的回測處理方式。

    • window_length=25:數據窗口長度:qteasy確保策略每次運行時只能看到從運行當時回溯一段時間的數據,從而避免未來函數。設置爲25代表每次選股時都只能看到從25天前到當天的25個價格,這樣就可以保證在計算20日漲幅的時候有足夠的數據了

準備回測數據

配置好選股策略以後,需要通過回測檢驗策略的表現,也就是調用滬深300和創業板兩個指數的實際歷史數據,進行模擬交易,看看模擬交易的結果是否能夠跑贏大盤。在實際操作中,賣賣大盤指數不太容易,不過一般都可以很容易找到跟蹤大盤指數的ETF基金來代替大盤,在這裏爲了簡單起見,我們這裏就直接投資於2011年1月1日一直到2020年12月31日之間的滬深300和創業板指數,假設交易費率爲萬分之一,雙向收費,看看投資的結果如何。

前面我們已經瞭解過如何下載歷史數據了,這裏我們需要滬深300和創業板指數從2013年到2022年底之間的所有數據。

注意 在下載歷史數據用於回測的時候,下載的數據需要比回測日期起點更多一些,例如,回測從2013年1月1日開始,實際需要的數據更多一些,因此下載數據的起點應該從2012年9月開始。關於這一點的詳細分析,請參見參考文檔

使用下面的代碼下載相應的歷史數據:

>>> qt.refill_data_source(tables='index_daily', symbols='399006, 000300', start_date='20100901', end_date='20201231')

確認數據是否下載成功:

>>> qt.candle('000300.SH', start='20110101', end='20201231', asset_type='IDX', mav=[])
>>> qt.candle('399001.SZ', start='20110101', end='20201231', asset_type='IDX', mav=[])

在這裏插入圖片描述

在這裏插入圖片描述

配置回測參數

數據準備好之後,就可以開始配置回測參數並開始回測了。qteasy的策略回測完全是參數化的,在回測之前我們需要告訴系統所有的相關資訊,例如投資的產品品種、投入資金的數量、回測開始日期和結束日期、回測過程的交易費用計算方法、交易批量等。我們可以通過qt.configure()對回測參數進行基本配置:

>>> qt.configure(asset_pool=['000300.SH',
...                          '399006.SZ'],  # 投资股票池里包括沪深300和创业板指数两个指数,分别代表大盘和小盘股
...              invest_cash_amounts=[100000],  # 投入金额为十万元
...              asset_type='IDX',  # 为简单起见,直接投资于指数
...              cost_rate_buy=0.0001,  # 买入资产时交易费用万分之一
...              cost_rate_sell=0.0001,  # 卖出资产时的交易费用为万分之一
...              invest_start='20110101',  # 模拟交易开始日期
...              invest_end='20201231',  # 模拟交易结束日期
...              trade_batch_size=0.01,  # 买入资产时最小交易批量
...              sell_batch_size=0.01,  # 卖出资产时最小交易批量
... )

上面的配置含義如下

  • asset_pool=['000300.SH', '399006.SZ']:投資目標指數用列表形式給出,如果要投資其他的指數或ETF基金,直接傳入證券代碼即可,如果要從三個或更多的證券中選股,直接加入列表中即可

  • invest_amounts=100000: 投資金額爲十萬元,如果需要模擬多次分批投入,還可以傳入一個列表,不過需要分別指定每次投入的具體日期

  • asset_type='IDX': 投資標的類型:'E'代表股票, 'IDX'代表指數, 'FD'代表基金,'FT'代表期貨,'OPT'代表期權

  • cost_rate_buy=0.0001: 設置買入和賣出交易費用比例,qteasy還支持設置最低費用、固定費用等等,這裏只簡單設置費率即可

  • cost_rate_sell=0.0001

  • invest_start='20110101': 模擬交易開始日期

  • invest_end='20201231': 模擬交易結束日期

  • trade_batch_size=0.01: 買入資產時最小交易批量,最小允許0.01,1代表只能交易整數份,這裏可以輸入任意大於等於0.01的數

  • sell_batch_size=0.01: 賣出資產時最小交易批量,最小允許0.01

qteasy还有其他的配置参数,参见QTEASY文档

7.4. 策略的回測結果

qteasy的策略回測非常簡單,設置好所有的配置後,即可以開始回測了,我們可以調用qt.run()開始回測,回測的同時,我們開啓可視化圖表輸出,並且開啓交易明細記錄:

>>> res = qt.run(op, mode=1, visual=True, trade_log=True)  # 调用qteasy的run方法,启动回测交易

等待片刻後,回測完成,qteasy會將列印回測報告在終端列印出來

輸出如下:

在報告中直接可以看到我們投資的總回報率以及同期滬深300指數的回報率的對比:

====================================
|                                  |
|         BACKTEST REPORT          |
|                                  |
====================================
qteasy running mode: 1 - History back testing
... 内容略,详见下文报告解析
-------------operation summary:------------
... 内容略,详见下文报告解析

Total operation fee:      ¥   10,666.34 
total investment amount:  ¥  100,000.00  # 投入的总金额为十万元
final value:              ¥  611,231.20  # 回测结束时的资产总额
Total return:                   511.23%  # 投资的总收益率
Avg Yearly return:               19.86%  # 投资的年化收益率为19.86%
... 内容略,详见下文报告解析
Benchmark return:                60.32%  # 同期沪深300指数的总回报率为60.32%
Benchmark Yearly return:          4.84%  # 同期沪深300指数的年化收益率仅有4.84%

------strategy loop_results indicators------ 
... 内容略,详见下文报告解析

==================END OF REPORT===================

從回測的結果可以很容易看出,這個策略是跑贏了滬深300大盤指數的,在這十年間滬深300的年化收益率只有可憐的5%左右(4.84%),甚至比某些收益較高的定期產品都不如,而我們這個策略的投資年化收益率達到了19.86%,十年間總資產從十萬元達到了六十多萬元,翻了六倍多

7.5. 策略的進一步改進

我們的策略獲得了初步的成功,不過,光看總回報率還不能完全說明問題,策略在整個十年間的表現如何呢?這就需要進一步分析,看看能否進一步改進這個策略。這時我們需要進一步查看回測的結果,尤其是可視化結果和交易明細記錄,通過這些記錄和報告來找到策略的不足和改進點。

爲了進一步改進,我們需要更深入地分析策略的表現,找到策略的不足之處,並且找到改進的方向。爲此,qteasy提供了下面這些工具,用於幫助用戶分析回測結果:

  • 回測報告:需設置report=True,回測報告以文字的形式總結了回測的結果,便於快速掌握投資的回報率以及關鍵評價指標。

  • 可視化報告:需設置visual=True,可視化複合圖表以圖形的形式展示回測報告中的內容,並且提供了更多的細節資訊,便於用戶更加直觀地理解回測結果。

  • 交易日誌文件:需設置:trade_log=True,交易日誌文件以csv文件的形式記錄了回測過程中每一次策略運行的過程,記錄策略運行的關鍵變量以及每一次的選股結果,便於用戶分析策略的運行過程,找到策略的不足之處,並且找到改進的方向。

  • 交易明細報告:需設置:trade_log=True,交易明細報告以csv文件的形式記錄了每一筆交易的詳細資訊,便於用戶分析每一筆交易的細節,找到策略的不足之處,並且找到改進的方向。

下面我們將詳細瞭解每一項工具的內容和使用方法,幫助我們分析回測結果,找到策略的不足之處,並且找到改進的方向。

回測報告詳細解析:

qteasy的回測報告包含大量的回測結果統計資訊,我們來逐一詳細瞭解:

第1部份:回測基本資訊

報告的第一部份包含了回測的基本資訊,包括回測耗時、回測的起止日期、回測的總週期等等,這些資訊可以幫助我們瞭解回測的基本情況:

qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 123.2 ms  # 生成交易信号的时间
time consumption for operation back testing:  288.7 ms  # 回测交易的时间
investment starts on      2011-01-04 15:00:00           # 回测开始日期
ends on                   2020-12-30 15:00:00           # 回测结束日期
Total looped periods:     10.0 years.                   # 回测的总周期为十年

第2部份:交易操作統計

回測報告的第二部份是交易操作統計,以列表方式統計了每個投資標的的買入和賣出次數,以及持倉的時間佔比:

例如可以看出000300.SH(滬深300指數)在十年間被買入了75次,賣出了81次,持倉時間佔比爲47.7%,空倉時間佔比爲52.3%,而399006.SZ(創業板指數)在十年間被買入了105次,賣出了85次,持倉時間佔比爲47.9%,空倉時間佔比爲52.1%。

-------------operation summary:------------
Only non-empty shares are displayed, call 
"loop_result["oper_count"]" for complete operation summary
          Sell Cnt Buy Cnt Total Long pct Short pct Empty pct
000300.SH   101       97    198   50.4%     -0.0%     49.6%  
399006.SZ   102      101    203   50.2%     -0.0%     49.8% 

第3部份:回測結果統計

回測報告的第三部份是回測結果統計,包含了投資的總金額、最終的資產總額、總收益率、年化收益率、基準收益率等等重要的回測結果統計資訊:

Total operation fee:      ¥   10,666.34  # 投资过程中的总交易费用 
total investment amount:  ¥  100,000.00  # 投入的总金额
final value:              ¥  611,231.20  # 回测结束时的资产总额
Total return:                   511.23%  # 投资的总收益率
Avg Yearly return:               19.86%  # 投资的年化收益率
Skewness:                         -0.40  # 投资回报率的偏度,偏度表明投资回报率分布的偏斜程度,负偏度表明投资回报率分布的左尾较长,意味着投资回报率有较大的负面极端值的可能性
Kurtosis:                          2.79  # 投资回报率的峰度,峰度表明投资回报率分布的峰态程度,峰度越大,表明投资回报率分布的峰态越高,意味着投资回报率有较大的极端值的可能性
Benchmark return:                60.32%  # 基准收益率,这里是指投资标的的基准指数(默认沪深300指数)的收益率,该指数在同一时段内收益率只有60%
Benchmark Yearly return:          4.84%  # 基准年化收益率,这里是指投资标的的基准指数(默认沪深300指数)的年化收益率,该指数在同一时段内年化收益率只有4.84%

第4部份:策略表現指標

回測報告的第四部份是策略表現指標,包含了Alpha、Beta、Sharp Ratio、Info Ratio、250日波動率、最大回撤等等重要的策略表現指標

------strategy loop_results indicators------ 
alpha:                            0.219  # 投资策略的Alpha值,Alpha值表明投资策略相对于基准指数的超额收益率,Alpha值越大,表明投资策略相对于基准指数的表现越好
Beta:                             0.686  # 投资策略的Beta值,Beta值表明投资策略相对于基准指数的系统风险,Beta值越大,表明投资策略相对于基准指数的系统风险越大
Sharp ratio:                      0.880  # 投资策略的Sharp Ratio,Sharp Ratio表明投资策略的风险调整收益率,Sharp Ratio越大,表明投资策略的风险调整收益率越好
Info ratio:                       0.062  # 投资策略的Info Ratio,Info Ratio表明投资策略相对于基准指数的风险调整超额收益率,Info Ratio越大,表明投资策略相对于基准指数的风险调整超额收益率越好
250 day volatility:               0.265  # 投资策略的250日波动率,250日波动率表明投资策略在250个交易日内的收益率波动程度,250日波动率越大,表明投资策略的收益率波动程度越大
Max drawdown:                    48.25%  # 最大回撤,最大回撤表明投资策略在回测期间内的最大资产回撤程度,最大回撤越大,表明投资策略的风险越大
    peak / valley:        2015-06-03 / 2015-08-26  # 最大回撤从2015年6月3日的峰值开始,到2015年8月26日的2015年8月26日,投资收益达到回撤48.25%
    recovered on:         2020-02-21               # 从2015年8月26日开始反弹,一直到2020年2月21日才完全恢复到2015年6月3日的峰值水平,也就是说,最大回撤持续了近五年时间

可視化報告詳解

只要設置qteasy的環境配置變量visual=True,在回測報告的最後,就能看到運行結果的可視化圖表報告:

在這裏插入圖片描述

可視化圖表用複合圖表的方式將回測報告中的所有資訊以更加直觀的方式顯示出來。可以看到,這張圖表的主體部分包括六張統一時間軸的歷史曲線圖,下面還有三張並列的柱狀圖,分別統計了歷年和歷月的收益率。

總體上,這張圖表可以分爲四個部分,我們逐一來了解:

第1部分,基本資訊以及投資回報指標

在圖表的最上方,顯示了回測的基本資訊和投資回報指標,這些資訊與回測報告中的內容基本一致,包括投資的總回報率、年化收益率、基準指數的回報率等等重要的投資回報指標,方便用戶快速瞭解投資的表現。

最大一次回撤持續了近五年

第2部份,收益率曲線圖

回測的歷史回報率曲線圖包括三張圖表,顯示的內容都是歷史收益率,但是各有側重。

可視化圖表第二部份

三張圖表分別爲:

  • 收益率曲線圖:這個曲線圖以百分比爲單位,用紅、綠兩條線記錄投資回報和基準回報(默認情況下投資基準是滬深300指數,可以通過環境配置變量reference_asset來設置其他指數)的收益率歷史曲線。紅色曲線爲投資組合的收益率,而藍色曲線爲參考指數收益率。 這張曲線圖包含了一些額外資訊,可以通過環境變量來控制開啓或關閉:

    • 回撤指示器:在投資區間的最大一次回撤的峯值、谷值以及恢復日間使用黑色箭頭標註出來,方便用戶瞭解投資的最大回撤情況

    • 買賣指示器(設置buy_sell_points=True):在表示基準收益曲線的綠色線上,會在買入和賣出的時間點用紅色/綠色箭頭標註每一次買進和賣出,紅色表示

    • 倉位指示器(設置show_positions=True):圖面背景用淺色的豎向條紋標註了每個時段的持倉比例,綠色代表持多倉,紅色表示持空倉(上圖中只有多倉,沒有空倉),條紋顏色越深,代表持倉比例越高,反之則代表持倉比例較低

  • 對數收益率曲線圖:這個曲線圖以對數收益率爲單位,顯示投資回報和基準回報的歷史曲線。對數收益率曲線圖可以更好地顯示投資回報率的變化趨勢,尤其是在投資回報率較高或較低的時候,對數收益率曲線圖能夠更清晰地顯示出投資回報率的變化情況。

  • 收益率柱狀圖:這個圖用柱狀圖顯示了回測歷史區間內每天的收益金額。

從收益率曲線圖上可以看到,整體收益率雖然還算不錯,但是回撤較高,最大達到了45%。

大家可以設想,投資者如果從2015年6月3日開始投資,結果將是:到2019年1月3日虧損45.9%,然後一直到2020年後才能解套!

第3部分,評價指標曲線圖

圖表的第三部份是策略性能指標圖,主要用於評價策略的盈利能力和風險控制能力,同樣包括三張圖表:

可視化圖表第三部份

  • 盈利能力評價指標:第一張圖顯示了策略的盈利能力評價指標,顯示了夏普率和Alpha率兩個指標在歷史上的滾動變化值。這兩個指標都被廣泛用於評價交易策略產生超額利潤(即超出基準)的能力,圖表上顯示的兩條線上每一點都是以當日爲終點,過去250個交易日的夏普率和Alpha率的滾動計算結果,因此可以看到這兩條線在歷史上的變化情況,夏普率越高,表明策略的風險調整收益率越好,Alpha率越高,表明策略相對於基準指數的超額收益率越好。

  • 風險控制能力評價指標:第二張圖顯示了策略的風險控制能力評價指標,顯示了Beta率和波動率兩個指標在歷史上的滾動變化值。這兩個指標都評價策略能否控制風險,Beta率越低,表明策略相對於基準指數的系統風險越小,波動率越低,表明策略的收益率波動程度越小。

  • 回撤控制能力指標:第三張圖是回撤潛水圖,顯示了策略的回撤控制能力評價指標,顯示了投資回報率的歷史曲線以及投資回報率的歷史回撤曲線(也就是“underwater”曲線)。該曲線的每一次下探都代表着投資回報曲線相對前期高點出現了回撤,下探得越深就代表回撤越厲害,一次完整的下潛和上浮代表一次完整的回撤週期。同時在這張圖上用灰色柱標註了歷史上五次最深的回撤過程,灰色柱越寬表示回撤的持續時間越長。

從回撤潛水圖上可以看出,整個十年間除了短短一兩年以外,幾乎都處於潛水套牢狀態,而且潛水的深度最深達到了45%以上。在整個十年投資期間,總資產不斷地出現回撤,45%回撤是最大最深的一次,但前期還有26%、22%等多次回撤,而且長度都不短,整個投資就是“長期被套牢,偶爾能翻盤”的狀況,我相信,沒有幾個投資者能夠熬得住這樣的煎熬的,對吧?

第4部分,收益率統計圖

最下面還有並列的三張圖表,分別從不同角度統計歷年或歷月的收益率:

可視化圖表第三部份

  • 月度收益熱力圖:以填色方塊的形式展示歷年(縱座標)每個月(橫座標)的收益率,顏色越偏綠表示收益越高、越偏紅表示收益越低或虧損

  • 年度收益柱狀圖:直觀地顯示每年的收益率,綠色柱代表正收益、紅色柱表示虧損,柱子越高收益/虧損絕對值越高。

  • 月度收益直方圖:統計所有月份的收益率並繪製它們的概率分佈圖,總體上來說這個圖一般是呈類似正態分佈的鐘形曲線,但是根據策略的性能不同,鐘形曲線會整體上向右偏移,表示總體產生正收益的概率高,或者產生偏斜等,從而幫助用戶從統計上理解收益率的分佈情況。

通過上面三張圖,可以看到整個十年中有三年(2011年、2016年和2018年)的收益率是負的,其餘年份均實現了正收益,但是收益率波動很大,有些年份大賺,有些年份大虧,波動大表明策略風險高,風控能力較弱。

至此,通過分析圖表,我們現在對交易策略的總體收益有了非常直觀的瞭解:這個策略最大的問題是沒有控制好回撤。

因此,應該想辦法改進一下這個策略,看看如何能夠降低迴撤,提升策略的性能。爲此,我們需要仔細分析模擬交易回測過程中的每一筆交易,尋找降低迴撤的辦法。要查看回測交易的每一個細節,那就需要查看交易日誌文件。

下面我們來繼續詳解交易日誌文件。

交易日誌/交易報告詳解

只要我們設置環境變量trade_log=True,系統就會在每次回測後生成多份 CSV(至少包括交易日誌與交易明細報告;當前版本還會在啓用該選項時寫入淨值曲線 value_curve_* 等,以實際生成爲準)。這些文件的保存路徑由配置項trade_log_file_path控制,默認在QT_ROOT_PATH/tradelog/路徑下。該路徑支持相對路徑、絕對路徑以及以~開頭的家目錄路徑,且可通過qt.configure(trade_log_file_path='...')在運行時修改並立即生效(熱修改),無需重新導入。例如:

  • Windows 指定到其他盤目錄:qt.configure(trade_log_file_path='C:\\qt_trade_logs\\')

  • macOS/Linux 指定到家目錄:qt.configure(trade_log_file_path='~/qt_trade_logs/')

磁盤保留策略:配置項 trade_log_keep_days 默認爲 3,表示在 每次新進程導入 qteasy 之後 會按該天數清理 trade_log_file_path 目錄下過期的 trade_log_*trade_summary_*value_curve_* 等回測 CSV(不在每次回測寫文件前自動清理)。若需長期保留全部歷史文件,請將 trade_log_keep_days 設爲 None小於等於 0;也可隨時調用 qt.rotate_trade_logs(days=...) 手動清理。

如果要查看當前的日誌文件保存路徑,請使用:

import qteasy as qt
print(qt.QT_TRADE_LOG_PATH)

兩個交易明細報告

打開路徑文件夾,可以看到歷次回測的報告,所有報告都保存爲csv文件,便於用Excel打開,常見類型包括:

  • 交易日誌:以“trade_log”打頭是交易日誌文件,羅列了每一次策略運行的詳細資訊

  • 交易報告:以“trade_summary”打頭的是交易報告,裏面羅列了每一筆交易的資訊

  • 淨值曲線(若已生成):以“value_curve”打頭,記錄完整淨值序列等

上述文件都以回測運行的日期/時間結尾,以區分不同時間生成的日誌文件。我們分別解釋:

交易日誌解讀

使用Excel打開第一個文件可以看到交易日誌,交易日誌用表格記錄了每一次策略運行時的資金的變動,持股的變動、每種股票的交易明細等資訊,不管是否有交易或持股變動,每天都有記錄:

整個交易歷史在表格中是按行排列的,每八行爲一組,每一組記錄一次交易,從第一次到最後一次交易的資訊按順序從上到下記錄。

每一組記錄的八行分別記錄下面的內容:

  • 0, trade signal 交易信號: 記錄這次運行後產生的交易信號(交易信號是一組數字,經信號解析後變成交易訂單,詳見交易信號

  • 1, price 交易價格: 記錄本次運行時的各個股票的交易價格

  • 2, traded amounts交易數量:記錄本次運行後各個股票的實際交易數量,正數爲買入數量,負數爲賣出數量,0代表沒有交易

  • 3, cash changed現金變動:記錄本次運行後各個股票實際交易導致的現金變動金額,正數代表現金增加,負數代表現金減少,0代表沒有變動

  • 4, trade cost交易費用:記錄本次運行後各個股票實際交易產生的交易費用

  • 5, own amounts持倉數量: 記錄本次運行後持有的各個股票的數量,正數代表持多頭持倉數量,負數代表空頭數量,0代表沒有持股

  • 6, available amounts可用持倉:記錄本次運行後可用的各個股票的數量,正數代表可用的多頭持倉數量,負數代表可用的空頭持倉數量,0代表沒有可用持股

  • 7, summary合計:記錄本次運行後持有/可用現金總計、持股總價值以及投資總價值

在這裏插入圖片描述 仔細察看交易日誌,整張表格按列可分爲三組或四組,分別提供不同角度的資訊:

  • 表頭區域:表頭區域第一列記錄每一次運行的日期和時間,第二列記錄每一次運行的策略組名稱(詳情參見策略組),第三列標註每一行的名稱,

  • 持有現金記錄:在該區域的summary合計行,記錄每次運行前新增投入的現金、期末持有現金總額/可用現金總額以及投資總額(包括現金總額和股票總價值)

  • 策略運行變量追蹤:策略運行變量追蹤是可選的,允許用戶在策略中插入追蹤點來追蹤策略運行過程變量的值,這在策略調試過程中很有用,此處暫時掠過,詳見

  • 持股記錄:這個區域裏顯示投資股票池中每一個股票的持股數量、交易數量、交易價格等等資訊,每一列顯示一支股票的數據,列數與股票池中的股票數量相等

基於上面的理解,我們可以分析一下策略的運行過程,看看策略是如何進行選股和換股的

在這裏插入圖片描述

如上圖,我們可以以前四天的運行記錄深入理解策略的運行方式:

  • 第一天:初始資金投入10萬元,投資開始啓動,同時我們可以看到當天的滬深300和創業板指的漲幅分別爲0.76%和1.83%,根據策略規則,我們在收盤時買入86.56股創業板指,消耗資金99996.32元,交易後持倉86.56股創業板指;

  • 第二天:創業板指和滬深300指數漲幅分別爲-0.77%和-0.56%,兩相比較仍然是創業板指勝出,因此持倉不變;

  • 第三天:滬深300指數漲幅反超創業板指,此時交易信號出現反轉,同時產生了滬深300的買入信號和創業板的賣出信號,不過此時只有創業板指賣出信號得到執行,全倉賣出86.56股,但滬深300的買入信號無法執行,因爲此時持有現金餘額不足,只有3.68元可用。

  • 第四天:交易信號持續反轉,此時滬深300買入信號發揮作用,全倉買入31.01股滬深300指數

按照上面提到的方式,交易日誌文件完整地記錄了十年裏一共2431個交易日的全部交易資訊,從而使我們可以仔細分析每一筆交易的得失。

仔細分析上面的表格,會發現這個投資策略除了在換股的時候以外,都是滿倉持有的,在2015年中的股災期間也不例外,我們找到這段時間會發現,從2015年的6月18日開始,不管是滬深300指數還是創業板指數,他們的20日收益率都已經由正轉負,表明後市已經開始下跌了,然而此時策略仍然堅定地持有創業板指,這是因爲創業板指的跌幅要小於滬深300,也就是收益率大於滬深300:

在這裏插入圖片描述

所以其實這時候我們的策略仍然選擇了正確的指數,只不過因爲兩個指數都在跌,我們的策略選擇了跌的少的那一個持有,減少了我們的損失。

但是正是這一段時間的操作,造成了我們十年投資歷史中最大的回撤事件:從2025年6月一直持續到2020年,深度套牢長達五年!!

交易報告解讀

交易日誌雖然事無鉅細地包含了所有的資訊,但有時候我們並不需要察看那麼多的數據,這時就可以使用交易報告了。

交易報告是交易日誌文件的緊湊版本,它忽略了所有沒有實質性交易的策略運行記錄,只保留了所有的實質性交易記錄,包括交易日期、買賣方向、交易份額、價格、總金額、交易費率等等資訊,由於只記錄有交易的實際發生,因此資訊更加緊湊:

在這裏插入圖片描述

從上圖可以看到,交易報告的每一行記錄一條實質交易記錄,如果同一天發生多條交易記錄,那麼他們會被記錄在不同的行。

交易報告的每一行裏都記錄了交易信號、交易價格、交易數量、現金變動、交易費率、交易後持倉數量等等資訊,列名稱包含的資訊與交易日誌完全相同;

7.6. 策略的改進思路

綜合上面我們對交易策略的分析,我們發現,這個交易策略雖然達到了較好的投資效果,但是在控制回撤方面,由於沒有避開股災區間,導致深度套牢。

那麼,我們可否從這裏出發改進我們的策略呢?思路很簡單,我們可以加一條規則:

  • 每天計算兩個指數在過去20天的漲幅,也就是今天的價格相對於20天前價格的漲幅

  • 如果選股日兩個指數都小於0,那麼我們第二天就空倉,一個指數都不持有

  • 否則,選擇漲幅較大的那個指數,在第二天持有,同時賣掉漲幅較小的指數

我們在原來的簡單選股規則基礎上增加了一條“過濾條件”,將兩個指數都小於0的情況排除在外,好了,那麼在qteasy中如何調整,以反映這個新的修改呢?

改進後的策略設置

qteasy的內置選股策略提供了一個過濾條件condition屬性,默認條件下condition='any',代表沒有過濾條件,現在我們需要把小於0的收益率過濾掉,因此可以設置condition='greater'同時設置過濾範圍ubound=0即可:

>>> op.set_parameter(0,
...                  condition='greater',  # 新增过滤条件:20日涨幅大于等于
...                  ubound=0.0,  # 过滤条件值:0
...                  )  

上面的設置跟前一節基本相同,增加了兩個參數:

  • condition='greater':含義是增加過濾條件,N日漲幅必須大於等於某個值才能參加選股,這個值在ubound參數中設置。也就是說排除掉小於這個值的股票,讓其無法中選

  • ubound=0: 設置爲0,這樣只有漲幅大於等於0的指數才能被選中,當然還可以設置爲其他浮點數

改進後的結果

同樣按照前面的配置,直接執行qt.run()。這裏直接放結果:

>>> res=qt.run(op, visual=True, trade_log=True)

輸出如下:

====================================
|                                  |
|         BACKTEST REPORT          |
|                                  |
====================================
qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 146.7 ms
time consumption for operation back testing:  8.7 ms
investment starts on      2011-01-04 15:00:00
ends on                   2020-12-30 15:00:00
Total looped periods:     10.0 years.
-------------operation summary:------------
Only non-empty shares are displayed, call 
"loop_result["oper_count"]" for complete operation summary
          Sell Cnt Buy Cnt Total Long pct Short pct Empty pct
000300.SH   113       99    212   26.7%     -0.0%     73.3%  
399006.SZ    97      103    200   42.2%     -0.0%     57.8%  

Total operation fee:     ¥   14,906.10
total investment amount: ¥  100,000.00
final value:              ¥  906,783.37
Total return:                   806.78% 
Avg Yearly return:               24.68%
Skewness:                         -0.23
Kurtosis:                          4.86
Benchmark return:                60.32% 
Benchmark Yearly return:          4.84%

------strategy loop_results indicators------ 
alpha:                            0.297
Beta:                             0.619
Sharp ratio:                      1.030
Info ratio:                       0.054
250 day volatility:               0.213
Max drawdown:                    28.88% 
    peak / valley:        2018-01-24 / 2019-01-23
    recovered on:         2019-07-01


==================END OF REPORT===================

可視化圖表如下: png

從資產收益率圖上可以看到,原來一片綠色(全程持倉)變成了白綠相間(白色區間空倉持幣),資產回撤情況得到了大幅度優化:從原來的50%回撤降低到了20%左右。而且總回報率也大大提升:

  • 資產總額從改進前的五十多萬提高到九十多萬

  • 總收益率從300%提升到了806%

  • 年化收益率從17%提升到了24.68%

  • 最大回撤從50%降低到了28%

通過查看交易記錄可知,的確策略在2015年6月底的股災期間保持空倉,躲避了單邊下跌的行情。

7.7. 本篇回顧

通過本教程,我們通過一個大小盤輪動交易策略的創建、回測、修改熟悉瞭解了qteasy的交易策略,知道如何通過引用內置交易策略,創建一個單策略交易員對象,並使交易員運行策略獲得回測結果。從下一篇教程開始,我們將進一步詳細討論qteasy的內置交易策略,並且介紹組合策略的實現方式,在交易員對象中添加更多的策略並設定組合方式,通過策略組合實現更復雜的效果,並且瞭解更多策略控制和類型。