【python量化交易】—— 集合竞价选股 - Qteasy自定义交易策略【附源码】

45 篇文章 6 订阅
19 篇文章 1 订阅

使用qteasy自定义并回测一个集合竞价选股策略

我们今天使用qteasy来回测一个集合竞价选股交易策略,qteasy是一个功能全面且易用的量化交易策略框架,Github地址在这里。使用它,能轻松地获取历史数据,创建交易策略并完成回测和优化,还能实盘运行。项目文档在这里。

为了继续本章的内容,您需要安装qteasy【教程1】,并下载历史数据到本地【教程2、),详情可以参考更多教程【教程3】

建议您先按照前面教程的内容了解qteasy的使用方法,然后再参考这里的例子创建自己的交易策略。

策略思想

本策略通过获取SHSE.000300沪深300的成份股数据并统计其30天内开盘价大于前收盘价的天数,并在该天数大于阈值10的时候加入股票池,随后对不在股票池的股票平仓并等权配置股票池的标的,每次交易间隔1个月.

回测数据为:SHSE.000300沪深300指数成份股
回测时间为:2016-04-05 到 2021-02-01

import qteasy as qt
import pandas as pd
import numpy as np
htypes = 'open, close'
shares = qt.filter_stock_codes(index='000300.SH', date='20220131')
print(shares[0:10])
dt = qt.get_history_data(htypes, shares=shares, asset_type='any', freq='m')

one_share = shares[24]

df = dt[one_share]

['000001.SZ', '000002.SZ', '000063.SZ', '000066.SZ', '000069.SZ', '000100.SZ', '000157.SZ', '000166.SZ', '000301.SZ', '000333.SZ']

第一种自定义策略设置方法,使用持仓数据和选股数据直接生成比例交易信号PS信号:

使用GeneralStrategy策略类,计算选股因子后,去掉所有小于零的因子,排序后提取排名前三十的股票
按以下逻辑生成交易信号:
1,检查当前持仓,如果持仓的股票未被选中,则全数卖出
2,检查当前持仓,如果新选中的股票没有持仓,则等权买入新增选中的股票

设置交易信号类型为PS,生成交易信号
由于生成交易信号需要用到持仓数据,因此不能使用批量生成模式,只能使用stepwise模式


class GroupPS(qt.GeneralStg):
    
    def realize(self, h, r=None, t=None, pars=None):
        
        # 读取策略参数(开盘价大于收盘价的天数)
        if pars is None:
            n_day = self.pars[0]
        else:
            n_day = pars[0]

        # 从历史数据编码中读取四种历史数据的最新数值
        opens = h[:, -30:, 0]  # 从前一交易日起前30天内开盘价
        closes = h[:, -31:-1, 1]  # 从两个交易日前开始前30天内收盘价
        
        # 从持仓数据中读取当前的持仓数量,并找到持仓股序号
        own_amounts = t[:, 0]
        owned = np.where(own_amounts > 0)[0]  # 所有持仓股的序号
        not_owned = np.where(own_amounts == 0)[0]  # 所有未持仓的股票序号
        
        # 选股因子为开盘价大于收盘价的天数,使用astype将True/False结果改为1/0,便于加总
        factors = ((opens - closes) > 0).astype('float')
        # 所有开盘价-收盘价>0的结果会被转化为1,其余结果转化为0,因此可以用sum得到开盘价大于收盘价的天数
        factors = factors.sum(axis=1)
        # 选出开盘价大于收盘价天数大于十天的所有股票的序号
        all_args = np.arange(len(factors))
        selected = np.where(factors > n_day)[0]
        not_selected = np.setdiff1d(all_args, selected)
        # 计算选出的股票的数量
        selected_count = len(selected)
        
        # 开始生成交易信号
        signal = np.zeros_like(factors)
        # 如果持仓为正,且未被选中,生成全仓卖出交易信号
        own_but_not_selected = np.intersect1d(owned, not_selected)
        signal[own_but_not_selected] = -1  # 在PS信号模式下 -1 代表全仓卖出
        
        if selected_count == 0:
            # 如果选中的数量为0,则不需要生成买入信号,可以直接返回只有卖出的信号
            return signal
        
        # 如果持仓为零,且被选中,生成全仓买入交易信号
        selected_but_not_own = np.intersect1d(not_owned, selected)
        signal[selected_but_not_own] = 1. / selected_count  # 在PS信号模式下,+1 代表全仓买进 (如果多只股票均同时全仓买进,则会根据资金总量平均分配资金)

        return signal

创建一个Operator对象,并回测交易策略

alpha = GroupPS(pars=(10,),
                par_count=1,
                par_types=['int'],
                par_range=[(3, 25)],
                name='GroupPS',
                description='本策略每隔1个月定时触发, 从SHSE.000300成份股中选择过去30天内开盘价大于前收盘价的天数大于10天的股票买入',
                data_types='open, close',
                strategy_run_freq='m',
                data_freq='d',
                window_length=32)  
op = qt.Operator(alpha, signal_type='PS')
op.op_type = 'stepwise'
op.set_parameter(0, (20,))
op.run(mode=1,
       asset_type='E',
       asset_pool=shares,
       trade_batch_size=100,
       sell_batch_size=1,
       trade_log=True)
print()

运行结果如下:

     ====================================
     |                                  |
     |       BACK TESTING RESULT        |
     |                                  |
     ====================================

qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 0.0ms
time consumption for operation back looping:  12s 766.3ms

investment starts on      2016-04-05 00:00:00
ends on                   2021-02-01 00: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
000001.SZ    1        1      2     1.7%      0.0%     98.3%  
000069.SZ    2        2      4     3.4%      0.0%     96.6%  
000301.SZ    3        3      6    13.6%      0.0%     86.4%  
000333.SZ    1        1      2     1.7%      0.0%     98.3%  
000338.SZ    1        1      2     3.5%      0.0%     96.5%  
000596.SZ    1        1      2     1.8%      0.0%     98.2%  
000651.SZ    1        1      2     1.7%      0.0%     98.3%  
000776.SZ    1        1      2     1.7%      0.0%     98.3%  
000786.SZ    1        1      2     1.7%      0.0%     98.3%  
000800.SZ    2        2      4     5.4%      0.0%     94.6%  
...            ...     ...   ...      ...       ...       ...
603806.SH    2        2      4     3.6%      0.0%     96.4%  
603939.SH    0        1      1     3.7%      0.0%     96.3%  
688599.SH    0        1      1     3.7%      0.0%     96.3%  
000408.SZ    1        1      2     1.7%      0.0%     98.3%  
002648.SZ    2        2      4     3.4%      0.0%     96.6%  
300751.SZ    1        1      2     1.7%      0.0%     98.3%  
688065.SH    1        1      2     1.7%      0.0%     98.3%  
600674.SH    2        2      4     3.7%      0.0%     96.3%  
600803.SH    1        1      2     1.7%      0.0%     98.3%  
601615.SH    1        1      2     1.7%      0.0%     98.3%   

Total operation fee:     ¥    1,290.37
total investment amount: ¥  100,000.00
final value:              ¥  216,271.60
Total return:                   116.27% 
Avg Yearly return:               17.32%
Skewness:                          0.29
Kurtosis:                          7.52
Benchmark return:                65.96% 
Benchmark Yearly return:         11.06%

------strategy loop_results indicators------ 
alpha:                            0.115
Beta:                             0.525
Sharp ratio:                      0.956
Info ratio:                       0.017
250 day volatility:               0.149
Max drawdown:                    18.93% 
    peak / valley:        2018-05-25 / 2018-09-11
    recovered on:         2019-04-18

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

在这里插入图片描述

第二种自定义策略设置方,使用PT交易信号设置持仓目标:

在完成选股因子的计算之后,直接设置每个股票的持仓目标,这样就不需要使用知道持仓数据,直接输出持仓目标信号
,在回测过程中根据实际持仓量生成交易信号。


class GroupPT(qt.GeneralStg):
    
    def realize(self, h, r=None, t=None, pars=None):

        # 读取策略参数(开盘价大于收盘价的天数)
        if pars is None:
            n_day = self.pars[0]
        else:
            n_day = pars[0]
        
        # 从历史数据编码中读取四种历史数据的最新数值
        opens = h[:, -30:, 0]  # 从前一交易日起前30天内开盘价
        closes = h[:, -31:-1, 1]  # 从两个交易日前开始前30天内收盘价
        
        # 选股因子为开盘价大于收盘价的天数,使用astype将True/False结果改为1/0,便于加总
        factors = ((opens - closes) > 0).astype('float')
        # 所有开盘价-收盘价>0的结果会被转化为1,其余结果转化为0,因此可以用sum得到开盘价大于收盘价的天数
        factors = factors.sum(axis=1)
        
        # 选出开盘价大于收盘价天数大于十天的所有股票的序号
        all_args = np.arange(len(factors))
        selected = np.where(factors > n_day)[0]
        not_selected = np.setdiff1d(all_args, selected)
        # 计算选出的股票的数量
        selected_count = len(selected)
        
        # 开始生成交易信号
        signal = np.zeros_like(factors)
        if selected_count == 0:
            return signal
        # 所有被选中的股票均设置为正持仓目标
        signal[selected] = 1. / selected_count  
        # 未被选中的股票持仓目标被设置为0
        signal[not_selected] = 0
        
        return signal
    

创建一个Operator对象,开始回测交易策略

alpha = GroupPT(pars=(10,),
                par_count=1,
                par_types=['int'],
                par_range=[(3, 25)],
                name='GroupPS',
                description='本策略每隔1个月定时触发, 从SHSE.000300成份股中选择过去30天内开盘价大于前收盘价的天数大于10天的股票买入',
                data_types='open, close',
                strategy_run_freq='m',
                data_freq='d',
                window_length=32)  
op = qt.Operator(alpha, signal_type='PT')
op.op_type = 'batch'
op.set_parameter(0, (20,))
op.run(mode=1,
       asset_type='E',
       asset_pool=shares,
       trade_batch_size=100,
       sell_batch_size=1,
       trade_log=True)
print()

交易回测结果如下:

     ====================================
     |                                  |
     |       BACK TESTING RESULT        |
     |                                  |
     ====================================

qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 399.1ms
time consumption for operation back looping:  8s 621.5ms

investment starts on      2016-04-05 00:00:00
ends on                   2021-02-01 00: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
000001.SZ    1        1       2    1.7%      0.0%     98.3%  
000069.SZ    2        2       4    3.4%      0.0%     96.6%  
000301.SZ    6        5      11   13.6%      0.0%     86.4%  
000338.SZ    1        1       2    1.7%      0.0%     98.3%  
000596.SZ    1        1       2    1.8%      0.0%     98.2%  
000625.SZ    1        1       2    1.7%      0.0%     98.3%  
000661.SZ    1        1       2    1.7%      0.0%     98.3%  
000776.SZ    1        1       2    1.7%      0.0%     98.3%  
000786.SZ    1        1       2    1.7%      0.0%     98.3%  
000800.SZ    2        3       5    5.4%      0.0%     94.6%  
...            ...     ...   ...      ...       ...       ...
603806.SH    2        2       4    3.6%      0.0%     96.4%  
603939.SH    0        2       2    3.7%      0.0%     96.3%  
688599.SH    0        2       2    3.7%      0.0%     96.3%  
000408.SZ    1        1       2    1.7%      0.0%     98.3%  
002648.SZ    1        1       2    1.7%      0.0%     98.3%  
300751.SZ    1        1       2    1.7%      0.0%     98.3%  
688065.SH    1        1       2    1.7%      0.0%     98.3%  
600674.SH    2        2       4    3.7%      0.0%     96.3%  
600803.SH    1        1       2    1.7%      0.0%     98.3%  
601615.SH    1        1       2    1.7%      0.0%     98.3%   

Total operation fee:     ¥    1,375.35
total investment amount: ¥  100,000.00
final value:              ¥  216,215.03
Total return:                   116.22% 
Avg Yearly return:               17.31%
Skewness:                          0.23
Kurtosis:                          6.63
Benchmark return:                65.96% 
Benchmark Yearly return:         11.06%

------strategy loop_results indicators------ 
alpha:                            0.110
Beta:                             0.542
Sharp ratio:                      1.015
Info ratio:                       0.017
250 day volatility:               0.139
Max drawdown:                    18.58% 
    peak / valley:        2016-04-14 / 2017-01-16
    recovered on:         2017-09-27

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

在这里插入图片描述

  • 38
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
股票回测是量化交易中非常重要的一环,它可以通过历史数据对交易策略进行模拟和评估,从而评估策略的可行性和优劣性。在Python中,有很多开源的量化交易框架可以用来进行股票回测,如zipline、backtrader等。 下面是一个使用zipline框架进行简单交易策略回测的例子: 1. 安装zipline ```python pip install zipline ``` 2. 编写交易策略代码 ```python from zipline.api import order_target_percent, record, symbol def initialize(context): context.asset = symbol('AAPL') def handle_data(context, data): # 获取过去10天的收盘价 prices = data.history(context.asset, 'price', 10, '1d') # 计算平均价 mean_price = prices.mean() # 如果当前价格低于平均价,则买入 if data.current(context.asset, 'price') < mean_price: # 调整持仓比例至100% order_target_percent(context.asset, 1.0) # 否则卖出 else: # 调整持仓比例至0% order_target_percent(context.asset, 0.0) # 记录当前持仓比例 record(position=context.portfolio.positions[context.asset].amount) ``` 3. 运行回测 ```python from zipline import run_algorithm from zipline.api import symbol from datetime import datetime start = datetime(2016, 1, 1) end = datetime(2017, 1, 1) result = run_algorithm( start=start, end=end, initialize=initialize, capital_base=10000, handle_data=handle_data, bundle='quandl' ) ``` 在上述代码中,我们定义了一个简单的交易策略,即如果当前价格低于过去10天的平均价,则买入,否则卖出。然后我们使用zipline框架进行回测,设定回测开始和结束时间、初始资本、数据来源等参数,最终得到回测结果。 需要注意的是,这只是一个简单的例子,实际的交易策略可能会更加复杂,需要考虑更多的因素。另外,在进行股票回测时,也需要注意避免过度拟合或过度优化,以免出现回测虚高的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值