随机选股策略

1.随机50交易法介绍

  沪深300指数的成分股每年调整2次(每半年调整一次),一般是1月份和7月份进行调整,每次调整比例不超过10%。每日随机选取50只沪深300的成分股股票作为股票池等权重买入,在第二日重新随机选取50只沪深300成分股股票作为新的股票池。卖出不在新股票池内的股票,买入新股票池中的股票,若新股票池当中的股票已经买入则继续持有,保证每日固定持有50只股票。重复该过程。

2.策略实现

  • 参数说明

self.__capital__ = capital # 起始资金
self.__period__ = period # 交易k线的时间级,如5分钟,1天
self.__bar_cnt__ = barCnt # 拉取的bar的次数
self.__codes__ = codes # 所有沪深300成分股股票代码
self.__codes2__ = [] #每日50股票池

  若新股票池的股票已经买入,则继续持有,并对股票池进行更新。

  • 策略框架介绍


class BaseSTKStrategy:
    '''
    STK策略基础类,所有的策略都从该类派生\n
    包含了策略的基本开发框架
    '''
    def __init__(self, name):
        self.__name__ = name

    def on_init(self, context:STKContext):
        '''
        策略初始化,启动的时候调用\n
        用于加载自定义数据\n
        @context    策略运行上下文
        '''
        return
        
    def on_calculate(self, context:Context):
        '''
        策略主调函数,所有的计算逻辑都在这里完成
        '''
        return

策略实现具体思路:

第一步,由于沪深300成分股会发生变化,每日都要重新读取沪深300成分股的名单

第二步,生成50个不重复的随机数,确定当日的股票池,使用初始资金等权重买入

第三步,第二个交易日重新生成随机的新股票池,对股票池的持仓进行更新。保证每日持有50支股票。

  • 最终完整策略代码如下

from wtpy import BaseCtaStrategy
from wtpy import CtaContext
import random
import pandas as pd

class StraHushenStk(BaseCtaStrategy):
    def __init__(self, name:str, codes:list, capital:float, barCnt:int, period:str):
        BaseCtaStrategy.__init__(self, name)

		self.__capital__ = capital # 起始资金
		self.__period__ = period # 交易k线的时间级,如5分钟,1天
		self.__bar_cnt__ = barCnt # 拉取的bar的次数
		self.__codes__ = codes # 所有沪深300成分股股票代码
		self.__codes2__ = [] # 每日50股票池

    def on_init(self, context:CtaContext):
        codes = self.__codes__    # 所有沪深300成分股股票代码
        codes2 = self.__codes2__ # 每日50股票池

        for i in range(0,len(codes)):
            if i == 0:
                context.stra_get_bars(codes[i], self.__period__, self.__bar_cnt__, isMain=True) # 设置第一支股票为主要品种
            else:
                context.stra_get_bars(codes[i], self.__period__, self.__bar_cnt__, isMain=False)

        context.stra_log_text("Hushen inited")

    
    def on_calculate(self, context:CtaContext):
        codes = self.__codes__    # 所有沪深300成分股股票代码
        codes2 = self.__codes2__  # 每日50股票池
        capital = self.__capital__ # 初始资金
        date = context.stra_get_date() # 获取当前日期

        #读取当日对应的沪深300成分股代码
        stocks = pd.read_csv('E:/沪深300历年成分股/{}.csv'.format(date), index_col=0)['order_book_id'].values 

        nums = [] # 股票池的数量
        while len(nums) < 50: # 每日固定生成50个不重复的随机数
            num = random.randint(0, len(stocks)-1)
            if num in nums: # 若随机数重复,跳过
                pass
            elif num not in nums: # 确保保存的随机数不重复
                stock = stocks[num]
                if stock[-1] == 'E': # 将深市股票代码转化为wtpy识别的股票代码
                    code = "SZSE.STK.{}".format(stock[:6])
                    curPrice = context.stra_get_price(code)
                    if curPrice > 0: # 确保股票当日价格数据正常
                        codes2.append(code) # 将股票放入股票池
                        nums.append(num) # 将随机数放入nums
                elif stock[-1] == 'G': # 将沪市股票代码转化为wtpy识别的股票代码
                    code = "SSE.STK.{}".format(stock[:6])
                    curPrice = context.stra_get_price(code)
                    if curPrice > 0: # 确保股票当日价格数据正常
                        codes2.append(code) # 将股票放入股票池
                        nums.append(num) # 将随机数放入nums

        for code in list(context.stra_get_all_position().keys()): # 遍历当前的持仓
            curPos = context.stra_get_position(code) # 获取每只股票的持仓头寸
            if code not in codes2 and curPos > 0: # 若当前持有的股票不在新生成的股票池内
                context.stra_exit_long(code, curPos, 'exitlong') # 平仓

        for code in codes2: # 遍历股票池
            curPos = context.stra_get_position(code) # 获取当前仓位
            curPrice = context.stra_get_price(code) # 获取当前价格
            if curPos == 0: # 若尚未持仓,买入该股票
                unit = int((200000/curPrice)/100) # 确定买入的数量
                context.stra_enter_long(code, unit, 'enterlong') # 买入unit手code
            elif curPos > 0: # 若已经持有该股票,则继续保留
                pass
        self.__codes2__ = [] # 当天交易结束后,将股票池清空

  写好了策略主体,将策略主体代码命名为Hushen300.py放入wtpy/demos/cta_stk_bt/strategies目录下,然后我们还需要runBT.py来让策略跑起来,runBT.py的格式和代码如下:

from wtpy import WtBtEngine,EngineType
from Strategies.Hushen import StraHushenStk
from wtpy.apps import WtBtAnalyst
import os
path = "E:/wtpy/deploy/storage/hushen300/csv"
datanames = os.listdir(path)
files = [string.replace('.csv','') for string in datanames]
files = [string.replace('_d','') for string in files]

if __name__ == "__main__":
    #创建一个运行环境,并加入策略
    engine = WtBtEngine(EngineType.ET_CTA)
    engine.init(folder='../common/', cfgfile="configbt.yaml", commfile="stk_comms.json", contractfile="stocks.json")
    engine.configBacktest(201101010930,202210241500)
    engine.configBTStorage(mode="csv", path="../storage/hushen300")
    engine.commitBTConfig()
    straInfo = StraHushenStk(name='Hushen', codes=files, barCnt=10, period="d", capital=10000000)
    engine.set_cta_strategy(straInfo)

    engine.run_backtest()

    #绩效分析
    analyst = WtBtAnalyst()
    analyst.add_strategy("Hushen", folder="./outputs_bt", init_capital=10000000, rf=0.02, annual_trading_days=240)
    analyst.run()

    kw = input('press any key to exit\n')
    engine.release_backtest()

  将runBT.py放入wtpy/demos/cta_stk_bt运行即可,其中的参数可以自行修改替换。这次是使用的沪深300成分股数据来进行回测,回测需要的日线数据可以通过下面的链接获取,然后放入wtpy/demos/storage/hushen300/csv文件夹中。

https://pan.baidu.com/s/12WJd3tMRBdOgsDXiiBA_sg?pwd=wtpy

3.回测结果

  打开运行之后生成的xlsx文件,可以看到策略的绩效,如下图所示:

  • 回测绩效

../../_images/rand1.png