QMT自带多因子策略解析

QMT自带了一些优秀策略,可以给新接触QMT的朋友练手。但是,面对百行左右的代码量,新手看了容易知难而退。其实,将策略的基本逻辑弄明白,再一句一句翻译代码,还是挺容易看懂的。

今天,我们分析一下QMT自带的《多因子选股回测示例》,逻辑在原策略中已经说明了,绿色字体的就是:

QMT自带多因子策略解析

原策略中的代码注释有限,本文几乎将原策略的每一行上面都做了注释,可以降低读者阅读代码的难度。

因为原策略形成的时间较早,所以有些函数现在几乎不用了,如set_universe和get_history_data。作为一篇分析代码的文章,本文对原策略不做改动,方便读者跟原策略对照。

由于时间仓促,可能对代码的解析过程中会有疏漏和谬误,如有读者发现,请留言指出,不吝赐教!

#coding:gbk"""回测模型示例(非实盘交易策略)#HS300日线下运行,20个交易日进行 一次调仓,每次买入在买入备选中因子评分前10的股票,每支股票各分配当前可用资金的10%(权重可调整)#扩展数据需要在补完HS300成分股数据之后生成,本模型中扩展数据暂时使用VBA指标ATR和ADTM生成,命名为atr和adtm"""#导入科学计算库import pandas as pdimport numpy as np#导入时间和日期库import timeimport datetime#系统初始化函数,只在策略启动时运行一次def init(ContextInfo):    #获取沪深300成分股    ContextInfo.s = ContextInfo.get_sector('000300.SH')    #将上一行获取的沪深300成分股作为股票池    ContextInfo.set_universe(ContextInfo.s)    #天数记数器    ContextInfo.day = 0    #新建一个字典,键为股票代码    ContextInfo.holdings = {i:0 for i in ContextInfo.s}    #各支股票权重均为10%    ContextInfo.weight = [0.1]*10         #设置资金分配权重    #建立一个空字典,后面将用来记录股票成交价    ContextInfo.buypoint = {}    #设置ContextInfo.money变量,来获取账户现金额    ContextInfo.money = ContextInfo.capital    #初始化ContextInfo.profit变量,用来记录利润值    ContextInfo.profit = 0    #设置模拟账号    ContextInfo.accountID='testS'#系统函数,每根K线运行一次def handlebar(ContextInfo):    #字典,用来记录股票的第一项打分情况    rank1 = {}    #字典,用来记录股票的第二项打分情况    rank2 = {}    #字典,用来记录股票的总分情况    rank_total = {}    #临时股票字典    tmp_stock = {}    #记录K线的编号,从0开始    d = ContextInfo.barpos    #获取前一个交易日的开盘价(等比向前复权)    price = ContextInfo.get_history_data(1,'1d','open',3)    #每月一调仓    if d > 60 and d % 20 == 0:        #获取当前K线的日期        nowDate = timetag_to_datetime(ContextInfo.get_bar_timetag(d),'%Y%m%d')        #打印当前K线的日期        print(nowDate)        #从自定义的signal函数中获取要买入和卖出的股票及持仓量的字典        buys, sells = signal(ContextInfo)        #        order = {}        #遍历带买入记号的股票列表        for k in list(buys.keys()):            #如果买入记号为1            if buys[k] == 1:                #记录股票的atr值,作为第一项的打分                rank1[k] = ext_data_rank('atr',k[-2:]+k[0:6],0,ContextInfo)                #记录股票的adtm值,作为第二项的打分                rank2[k] = ext_data_rank('adtm',k[-2:]+k[0:6],0,ContextInfo)                #股票的总分为第一项的打分                rank_total[k] = 1.0 * rank1[k]                          #因子的权重需要人为设置,此处取了0.5和-0.5                #打印投票第一项的打分                print (1111111, rank1[k])        #将股票按总分升序排列        tmp = sorted(list(rank_total.items()), key = lambda item:item[1])        #如果股票总数大于等于10        if len(tmp) >= 10:            #选择10支股票            tmp_stock = {i[0] for i in tmp[:10]}        #如果股票总数小于10        else:            #则选择所有的股票            tmp_stock = {i[0] for i in tmp}                              #买入备选中若超过10只股票则选10支,不足10支则全选        #遍历带买入股票记号字典的键(股票代码)        for k in list(buys.keys()):            #如果股票代码未在分数合格的股票之中            if k not in tmp_stock:                #标记这支股票的买入记号为0                buys[k] = 0        #如果打分的股票数量大于0        if tmp_stock:            #打印:股票池:打分的股票            print('stock pool:',tmp_stock)            #遍历沪深300成分股            for k in ContextInfo.s:                #如果股票有持仓,并且带有卖出标记                if ContextInfo.holdings[k] > 0 and sells[k] == 1:                    #打印:准备卖出                    print('ready to sell')                    #卖出股票                    order_shares(k,-ContextInfo.holdings[k]*100,'fix',price[k][-1],ContextInfo,ContextInfo.accountID)                    #更新账户资产额                    ContextInfo.money += price[k][-1] * ContextInfo.holdings[k] * 100 - 0.0003*ContextInfo.holdings[k]*100*price[k][-1]                  #手续费按万三设定                    #更新账户利润额                    ContextInfo.profit += (price[k][-1]-ContextInfo.buypoint[k]) * ContextInfo.holdings[k] * 100 - 0.0003*ContextInfo.holdings[k]*100*price[k][-1]                    #打印售出的股票代码                    print(k)                    #将股票代码字典相应的股票的数量更新为0                    ContextInfo.holdings[k] = 0            #新建变量,保存给要买入的各支股票分配的资金            ContextInfo.money_distribution = {k:i*ContextInfo.money for (k,i) in zip(tmp_stock,ContextInfo.weight)}            #遍历准备买入的股票字典            for k in tmp_stock:                #如果股票不在持仓中,并且带有买入记号                if ContextInfo.holdings[k] == 0 and buys[k] == 1:                    #打印:准备买入                    print('ready to buy')                    #计算出要买入的股票的手数                    order[k] = int(ContextInfo.money_distribution[k]/(price[k][-1]))/100                    #下限价单                    order_shares(k,order[k]*100,'fix',price[k][-1],ContextInfo,ContextInfo.accountID)                    #记录下单的价格                    ContextInfo.buypoint[k] = price[k][-1]                    #更新账户现金:原账户现金-下单用去的现金                    ContextInfo.money -= price[k][-1] * order[k] * 100 - 0.0003*order[k]*100*price[k][-1]                    #更新账户利润:原账户利润-交易手续费                    ContextInfo.profit -= 0.0003*order[k]*100*price[k][-1]                    #打印买入的股票代码                    print(k)                    #将买入的股票数量更新到股票代码字典中                    ContextInfo.holdings[k] = order[k]            #打印账户现金、账户利润、账户现金初始金额            print(ContextInfo.money,ContextInfo.profit,ContextInfo.capital)    #收益率 = 利润/账户初始金额    profit = ContextInfo.profit/ContextInfo.capital    #如果不是回测状态    if not ContextInfo.do_back_test:        #画出收益率变动曲线        ContextInfo.paint('profit_ratio', profit, -1, 0)        #自定义函数,用来发出买卖信号def signal(ContextInfo):    #买入信号字典,键为沪深300成分股列表,值为0    buy = {i:0 for i in ContextInfo.s}    #卖出信号字典,键为沪深300成分股列表,值为0    sell = {i:0 for i in ContextInfo.s}    #取前22个交易日的K线最高价,(等比向前复权)    data_high = ContextInfo.get_history_data(22,'1d','high',3)    #取前2个交易日的K线最高价,(等比向前复权)    data_high_pre = ContextInfo.get_history_data(2,'1d','high',3)    #取前62个交易日的K线收盘价,(等比向前复权)    data_close60 = ContextInfo.get_history_data(62,'1d','close',3)    #遍历沪深300成分股列表    for k in ContextInfo.s:        #如果有前62个交易日发收盘数据        if k in data_close60:            #如果前面的数据中,每一天的数据都有            if len(data_high_pre[k]) == 2 and len(data_high[k]) == 22 and len(data_close60[k]) == 62:                #如果第前2个交易日的最高价超过了前面20个交易日的最高价                if data_high_pre[k][-2] > max(data_high[k][:-2]):                    #给这支股票标记上买入信号                    buy[k] = 1           #超过20日最高价,加入买入备选                #如果第前2个交易日的最高价未超过前面20个交易日的最高价,并且低于60日均线                elif data_high_pre[k][-2] < np.mean(data_close60[k][:-2]):                    #给这支股票标记上卖出信号                    sell[k] = 1           #低于60日均线,加入卖出备选    #买入卖出备选字典返回    return buy,sell

收益情况:

QMT自带多因子策略解析

QMT自带多因子策略解析

策略中的一些知识点,本文限于篇幅就不在此讲解了,待优化完代码,另行发文时,再加入知识点讲解部分。

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/129436
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 4小时前
下一篇 4小时前

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注