11. より複雑な戦略を構築する

qteasy は、定量的取引分析ツールキット Github アドレスはこちら の完全にローカライズされた展開と操作であり、pip 経由 でインストールできます。

$ pip install qteasy -U

qteasyには以下の機能があります。

  • 財務データの取得、クリーニング、保管、処理、可視化、および使用

  • クオンツ取引戦略を作成し、多数の組み込みの基本取引戦略を提供します

  • ベクトル化された高速取引戦略のバックテストと取引結果の評価

  • 取引戦略パラメーターの最適化と評価

  • 取引戦略の展開と実際の運用

これらのチュートリアルでは、一連の実践的な例を通じて、qteasy の主な機能と使用法を学びます。

11.1. 始める前に

このチュートリアルを開始する前に、次の内容を習得していることを確認してください。

  • qteasy のインストールと設定 —— QTEASY教程1

  • ローカル データ ソースをセットアップし、十分な履歴データをローカルにダウンロードしている ——QTEASY教程2

  • トレーダー オブジェクトの作成方法を学び、組み込みの取引戦略を使用します ——QTEASY教程3

  • ミキサーを使用して複数の単純な戦略を混合してより複雑な取引戦略を作成する方法を学びます ——QTEASY教程4

  • 取引戦略をカスタマイズする方法を理解する ——QTEASY教程5QTEASY教程6

QTEASY ドキュメント では、組み込み取引戦略の使用、カスタム戦略の作成などに関する詳細なコンテンツも見つけることができます。 qteasy の基本的な使用法にまだ慣れていない場合は、そこにアクセスしてさらに詳しい説明を参照してください。

qteasyのカーネルは、高速実行と十分な柔軟性を両立するフレームワークとして設計されています。理論的には、想像できるあらゆる種類の取引戦略を実装できます。

一方、qteasy のバックテスト フレームワークは、取引戦略に「将来の機能」を誤ってインポートすることを完全に回避するように特別に設計されており、バックテスト中に取引戦略が完全に過去のデータに基づいていることが保証されます。同時に、多くの前処理技術と JIT テクノロジを使用してカーネルの主要な関数をコンパイルし、C と同等の実行速度を達成しました。

ただし、理論上無限の可能なトレーディング戦略を実現するには、組み込みのトレーディング戦略と戦略の混合だけを使用するだけでは十分ではない可能性があります。一部の特定の取引戦略、または一部の特に複雑な取引戦略は、組み込み戦略を混合して作成することはできません。これには、qteasy が提供するStrategy 基本クラスを使用して、特定のルールに基づいてカスタム取引戦略を作成する必要があります。

11.2. このチュートリアルのターゲット

このチュートリアルでは、qteasy の取引戦略基本クラスを紹介し、これらの基本クラスに基づいて独自の取引戦略を作成する方法を具体的な例を通して詳しく説明します。説明するには

11.3. Strategy クラスを継承して、複雑な多要素の銘柄選択戦略を作成する

この例では、

>>> import qteasy as qt
>>> import numpy as np
>>> from qteasy import Parameter, StgData

>>> def market_value_weighted(stock_return, mv, mv_cat, bp_cat, mv_target, bp_target):
...    """ 根据mv_target和bp_target计算市值加权收益率,在策略中调用此函数计算加权收益率  """
...    sel = (mv_cat == mv_target) & (bp_cat == bp_target)
...    mv_total = np.nansum(mv[sel])
...    mv_weight = mv / mv_total
...    return_total = np.nansum(stock_return[sel] * mv_weight[sel])
...     return return_total

>>> class MultiFactors(qt.FactorSorter):
...     """ 开始定义交易策略 """
... 
...     def __init__(self, par_values: tuple = (0.5, 0.3, 0.7)):
...         """交易策略的初始化参数"""
...         super().__init__(
...                 pars=[Parameter((0.01, 0.99), par_type='float', name='size_gate', value=0.5),  # 参数1:大小市值分类界限
...                       Parameter((0.01, 0.49), par_type='float', name='bp_s_gate', value=0.3),  # 参数2:小/中bp分界线
...                       Parameter((0.50, 0.99), par_type='float', name='bp_l_gate', value=0.7)],  # 参数3,中/大bp分界线
...                 name='MultiFactor',
...                 description='根据Fama-French三因子回归模型估算HS300成分股的alpha值选股',
...                 data_types=[StgData('pb', freq='d', asset_type='E', window_length=20, use_latest_data_cycle=True),  # 执行选股需要用到的股票数据
...                              StgData('total_mv', freq='d', asset_type='E', window_length=20, use_latest_data_cycle=True),
...                              StgData('close', freq='d', asset_type='E', window_length=20, use_latest_data_cycle=True),
...                              StgData('close-000300.SH', freq='d', asset_type='IDX', window_length=20, use_latest_data_cycle=True)],  # 选股需要用到市场收益率,使用沪深300指数的收盘价计算
...                 max_sel_count=10,  # 最多选出10支股票
...                 sort_ascending=True,  # 选择因子最小的股票
...                 condition='less',  # 仅选择因子小于某个值的股票
...                 lbound=0,  # 仅选择因子小于0的股票
...                 ubound=0,  # 仅选择因子小于0的股票 
...         )
... 
...     def realize(self):
...         """ 策略的选股逻辑在realize()函数中定义
...         """
... 
...         size_gate_percentile, bp_small_percentile, bp_large_percentile = self.get_pars('size_gate', 'bp_s_gate', 'bp_l_gate')
...         # 读取投资组合的数据PB和total_MV的最新值
...         pb = self.get_data('pb_E_d')[-1]  # 当前所有股票的PB值
...         mv = self.get_data('total_mv_E_d')[-1]  # 当前所有股票的市值
...         pre_close = self.get_data('close_E_d')[-1]  # 当前所有股票的前收盘价
...         close = self.get_data('close_E_d')[-2]  # 当前所有股票的最新收盘价
...         
...         market_pre_close = self.get_data('close-000300.SH_IDX_d')[-2]  # HS300的昨收价
...         market_close = self.get_data('close-000300.SH_IDX_d')[-1]  # HS300的收盘价
...     
...         # 计算账面市值比,为pb的倒数
...         bp = pb ** -1
...         # 计算市值的50%的分位点,用于后面的分类
...         size_gate = np.nanquantile(mv, size_gate_percentile)
...         # 计算账面市值比的30%和70%分位点,用于后面的分类
...         bm_30_gate = np.nanquantile(bp, bp_small_percentile)
...         bm_70_gate = np.nanquantile(bp, bp_large_percentile)
...         # 计算每只股票的当日收益率
...         stock_return = pre_close / close - 1
...     
...         # 根据每只股票的账面市值比和市值,给它们分配bp分类和mv分类
...         # 市值小于size_gate的cat为1,否则为2
...         mv_cat = np.ones_like(mv)
...         mv_cat += (mv > size_gate).astype('float')
...         # bp小于30%的cat为1,30%~70%之间为2,大于70%为3
...         bp_cat = np.ones_like(bp)
...         bp_cat += (bp > bm_30_gate).astype('float')
...         bp_cat += (bp > bm_70_gate).astype('float')
...     
...         # 获取小市值组合的市值加权组合收益率
...         smb_s = (market_value_weighted(stock_return, mv, mv_cat, bp_cat, 1, 1) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 1, 2) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 1, 3)) / 3
...         # 获取大市值组合的市值加权组合收益率
...         smb_b = (market_value_weighted(stock_return, mv, mv_cat, bp_cat, 2, 1) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 2, 2) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 2, 3)) / 3
...         smb = smb_s - smb_b
...         # 获取大账面市值比组合的市值加权组合收益率
...         hml_b = (market_value_weighted(stock_return, mv, mv_cat, bp_cat, 1, 3) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 2, 3)) / 2
...         # 获取小账面市值比组合的市值加权组合收益率
...         hml_s = (market_value_weighted(stock_return, mv, mv_cat, bp_cat, 1, 1) +
...                  market_value_weighted(stock_return, mv, mv_cat, bp_cat, 2, 1)) / 2
...         hml = hml_b - hml_s
...     
...         # 计算市场收益率
...         market_return = market_pre_close / market_close - 1
...     
...         coff_pool = []
...         # 对每只股票进行回归获取其alpha值
...         for rtn in stock_return:
...             x = np.array([[market_return, smb, hml, 1.0]])
...             y = np.array([[rtn]])
...             # OLS估计系数
...             coff = np.linalg.lstsq(x, y)[0][3][0]
...             coff_pool.append(coff)
...     
...         # 以alpha值为股票组合的选股因子执行选股
...         factors = np.array(coff_pool)
...         
...         return factors

パラメータの設定とバックテスト

上記の戦略が定義されたら、バックテストを開始できます。 qteasy でトレーダー オブジェクトを作成し、前に作成した戦略を操作する必要があります。

shares = qt.filter_stock_codes(index='000300.SH', date='20190501')  # 选择股票池,包括2019年5月以来所有沪深300指数成分股
       
#  开始策略的回测

alpha = MultiFactors()  # 生成一个交易策略的实例,名为alpha
op = qt.Operator(alpha, signal_type='PT', 
                 run_timing='close',  # 在周期结束(收盘)时运行
                 run_freq='M',  # 每月执行一次选股(每周或每天都可以)
                )  # 生成交易员对象,操作alpha策略,交易信号的类型为‘PT',意思是生成的信号代表持仓比例,例如1代表100%持有股票,0.35表示持有股票占资产的35%
# 设置回测参数,并开始回测
>>> qt.run(op=op,
...        mode=1,  # 回测模式
...        invest_start='20160405',  # 回测开始日期
...        invest_end='20210201',  # 回测结束日期
...        asset_type='E',  # 投资品种为股票
...        asset_pool=shares,  # shares包含同期沪深300指数的成份股
...        trade_batch_size=100,  # 买入批量为100股
...        sell_batch_size=1,  # 卖出批量为整数股
...        trade_log=True,  # 生成交易记录
...       )  # 开始运行

結果は次のとおりです。

====================================
|                                  |
|         BACKTEST REPORT          |
|                                  |
====================================
qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 197.5 ms
time consumption for operation back testing:  315.2 ms
investment starts on      2016-04-05 15:00:00
ends on                   2021-01-29 15:00:00
Total looped periods:     4.8 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
000413.SZ    3        3       6    5.4%     -0.0%     94.6%  
000415.SZ    1        1       2    2.0%     -0.0%     98.0%  
000625.SZ    1        1       2    2.0%     -0.0%     98.0%  
000629.SZ    1        1       2    1.5%     -0.0%     98.5%  
000656.SZ    1        1       2    1.8%     -0.0%     98.2%  
000723.SZ    5        5      10    8.2%     -0.0%     91.8%  
000725.SZ    1        1       2    1.9%     -0.0%     98.1%  
000728.SZ    1        1       2    1.9%     -0.0%     98.1%  
000783.SZ    1        1       2    1.8%     -0.0%     98.2%  
000898.SZ    4        4       8    7.1%     -0.0%     92.9%  
...            ...     ...   ...      ...       ...       ...
001965.SZ    1        1       2    1.9%     -0.0%     98.1%  
300418.SZ    2        2       4    3.8%     -0.0%     96.2%  
300442.SZ    1        1       2    1.8%     -0.0%     98.2%  
600026.SH    1        1       2    1.8%     -0.0%     98.2%  
000975.SZ    1        1       2    1.8%     -0.0%     98.2%  
300394.SZ    1        1       2    1.5%     -0.0%     98.5%  
600160.SH    3        3       6    5.0%     -0.0%     95.0%  
601127.SH    1        2       3    1.7%     -0.0%     98.3%  
601058.SH    2        2       4    3.6%     -0.0%     96.4%  
302132.SZ    1        1       2    2.0%     -0.0%     98.0%  

Total operation fee:     ¥    3,227.95
total investment amount: ¥  100,000.00
final value:              ¥  153,592.75
Total return:                    53.59% 
Avg Yearly return:                9.31%
Skewness:                          0.09
Kurtosis:                          6.81
Benchmark return:                63.94% 
Benchmark Yearly return:         10.80%

------strategy loop_results indicators------ 
alpha:                            0.026
Beta:                             0.702
Sharp ratio:                      0.295
Info ratio:                      -0.008
250 day volatility:               0.161
Max drawdown:                    30.41% 
    peak / valley:        2016-11-22 / 2018-10-16
    recovered on:         2019-03-07

trade log is stored in: /Users/jackie/Projects/qteasy_logs/trade_log_none_20260309_152544.csv
trade summary is stored in: /Users/jackie/Projects/qteasy_logs/trade_summary_none_20260309_152544.csv
value curve (complete values) is stored in: /Users/jackie/Projects/qteasy_logs/value_curve_none_20260309_152544.csv

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

png

11.4. 要約

このチュートリアルでは、qteasyの取引戦略基本クラスに基づいて独自の取引戦略を作成する方法を、具体的な例を通して詳しく説明しました。この例を通して、qteasy の取引戦略基本クラスが、想像できるあらゆる種類の取引戦略を実装するのに十分な柔軟性を備えていることがわかります。

次のチュートリアルから、qteasy の取引戦略最適化手法を紹介し、さまざまな最適化アルゴリズムを通じて最適な取引戦略パラメータを見つけ、取引戦略のパフォーマンスを評価します。