QMT是迅投开发的极速策略交易系统,是专为券商、期货公司等机构的高净值客户及专业投资者打造的综合性量化交易平台。它集行情显示、策略研究、产品交易及风险控制等功能于一体,旨在为用户提供高效、稳定、灵活的量化交易解决方案。
它支持多种交易策略的开发、测试和实盘交易,支持股票、期货、期权、ETF等多种市场和交易类型,支持Python和VBA两种语言编写策略,能利用历史数据回测评估策略有效性,还可自动执行策略,实现对冲下单等多种交易模式,支持多账户批量管理,其多层次并行风控可降低交易延迟,提升风控效率,同时具备数据导入导出、云配置等功能。
今天给大家分享一个简单的策略——QMT 多因子选股策略
代码是基于 QMT 平台开发的量化选股策略,主要针对沪深 300 成分股进行多因子选股和定期调仓。策略结合了技术指标 (ATR 和 ADTM) 进行选股评分,并通过因子加权方式确定买入标的。
#!/usr/bin/python# coding:gbk'''策略以HS300为基础股票池,在日线下运行,20个交易日进行一次调仓,每次买入在买入备选中因子评分前10的股票,每支股票各分配当前可用资金的10%(权重可调整)扩展数据需要在补完HS300成分股数据之后生成,本模型中扩展数据暂时使用VBA指标ATR和ADTM生成,命名为atr和adtm'''import pandas as pdimport numpy as npimport timeimport datetime#1. ===========================================初始化部分=============================================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}# 资金权重 ContextInfo.weight = [0.1] * 10 # 设置资金分配权重# 买入点 ContextInfo.buypoint = {}# 可用资金? ContextInfo.money = ContextInfo.capital# 策略盈利 ContextInfo.profit = 0# 设置交易账户 ContextInfo.accountID = 'testS'#1. ===========================================初始化部分=============================================#2. ===========================================周期循环部分===========================================def handlebar(ContextInfo): rank1 = {} rank2 = {} rank_total = {} tmp_stock = {}# 当前bar线索引号 d = ContextInfo.barpos# 获取过去1日的开盘价数据 price = ContextInfo.get_history_data(1, '1d', 'open', 3)# 策略运行天数大于60天且到达20天的调仓周期if d > 60 and d % 20 == 0:# 当前bar线日期 nowDate = timetag_to_datetime(ContextInfo.get_bar_timetag(d), '%Y%m%d')# print(nowDate)# 获取待买入、卖出股票池 buys, sells = signal(ContextInfo)# order = {}# 获取待买入for k in buys.keys():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)#print(rank1[k], rank2[k])# 人为设置因子的权重,此处取了0.5和-0.5 rank_total[k] = 0.5 * rank1[k] - 0.5 * rank2[k]# 对rank_total按照值value进行排序,并返回一个列表,列表里面的元素是形如(code,value)的元组 tmp = sorted(rank_total.items(), key=lambda item: item[1])# print(tmp)# 如果买入备选股票数大于10只,则选取因子排序最小的10只if len(tmp) >= 10: tmp_stock = {i[0] for i in tmp[:10]}# 如果买入备选股票数小于10只,则全选else: tmp_stock = {i[0] for i in tmp}for k in buys.keys():if k not in tmp_stock: buys[k] = 0if tmp_stock:print('买入备选股票列表:', tmp_stock)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(price[k][-1])# print(k)# print(ContextInfo.money) 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)#2. ===========================================周期循环部分===========================================#3. ===========================================初步删选股票池=========================================def signal(ContextInfo): buy = {i: 0 for i in ContextInfo.s} sell = {i: 0 for i in ContextInfo.s}# 获取基础股票池历史22天的日最高价 data_high = ContextInfo.get_history_data(22, '1d', 'high', 3)# 获取基础股票池历史2天的日最高价 data_high_pre = ContextInfo.get_history_data(2, '1d', 'high', 3)# 获取基础股票池历史62天的日收盘价 data_close60 = ContextInfo.get_history_data(62, '1d', 'close', 3)# print(data_high)# print(data_close)# print(data_close60)for k in ContextInfo.s:if k in data_close60.keys():# 过去62天未出现停牌,数据齐全if len(data_high_pre[k]) == 2 and len(data_high[k]) == 22 and len(data_close60[k]) == 62:# 超过20日最高价,加入买入备选if data_high_pre[k][-2] > max(data_high[k][:-2]): buy[k] = 1# 低于60日均线,加入卖出备选elif data_high_pre[k][-2] < np.mean(data_close60[k][:-2]): sell[k] = 1# print(buy)# print(sell)return buy, sell#3. ===========================================初步删选股票池=========================================
1、核心交易逻辑 (handlebar 函数)
策略每 20 个交易日调仓一次,主要步骤包括:
信号生成:通过signal函数筛选买入 / 卖出候选股票
因子评分:对买入候选股票计算 ATR 和 ADTM 排名并加权
股票选择:选择评分最高的 10 只股票作为买入标的
def handlebar(ContextInfo): if d > 60 and d % 20 == 0: # 每20个交易日调仓 buys, sells = signal(ContextInfo) # 获取买卖信号 # 对买入候选股票进行因子评分 for k in buys.keys(): if buys[k] == 1: rank1[k] = ext_data_rank('atr', ...) # ATR排名 rank2[k] = ext_data_rank('adtm', ...) # ADTM排名 rank_total[k] = 0.5 * rank1[k] - 0.5 * rank2[k] # 因子加权 # 选择评分最高的10只股票 tmp = sorted(rank_total.items(), key=lambda item: item[1]) tmp_stock = {i[0] for i in tmp[:10]} # 执行卖出操作 for k in ContextInfo.s: if ContextInfo.holdings[k] > 0 and sells[k] == 1: order_shares(k, -ContextInfo.holdings[k]*100, ...) ContextInfo.money += ... # 更新可用资金 ContextInfo.profit += ... # 更新策略盈利 # 执行买入操作 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: order[k] = int(ContextInfo.money_distribution[k] / price[k][-1]) / 100 order_shares(k, order[k]*100, ...) ContextInfo.money -= ... # 更新可用资金 ContextInfo.holdings[k] = order[k] # 更新持仓
2、股票筛选逻辑 (signal 函数)
该函数通过简单的技术指标生成买入和卖出信号:
def signal(ContextInfo): buy = {i: 0 for i in ContextInfo.s} sell = {i: 0 for i in ContextInfo.s} data_high = ContextInfo.get_history_data(22, '1d', 'high', 3) data_high_pre = ContextInfo.get_history_data(2, '1d', 'high', 3) data_close60 = ContextInfo.get_history_data(62, '1d', 'close', 3) for k in ContextInfo.s: if k in data_close60.keys(): if len(data_high_pre[k]) == 2 and len(data_high[k]) == 22 and len(data_close60[k]) == 62: # 买入信号:超过20日最高价 if data_high_pre[k][-2] > max(data_high[k][:-2]): buy[k] = 1 # 卖出信号:低于60日均线 elif data_high_pre[k][-2] < np.mean(data_close60[k][:-2]): sell[k] = 1 return buy, sell
总结:这个策略逻辑完整、风险控制到位,新手照着学能快速入门量化交易,老手拿过来改改因子和参数就能直接用!
- 股票无条件万1,根据资金量最低可以做到万0.854
3、福利领取