A股股票选股模板策略
由small_q创建,最终由small_q 被浏览 1782 用户
在平台策略编写文件导航器中,有近20个模板策略,可供大家借鉴学习,本文进行简要介绍。
编写策略里的策略模板
在编写策略界面下,我们可以找到模板策略文件夹,存放了一些常用的策略/功能实现模板。
进入模板策略文件夹,可以看到股票和期货两个文件夹,分别存放了股票策略模板和期货策略模板。
以股票文件夹为例,进入文件夹后可以看到如下策略。
我们对部分策略/功能进行初步介绍:
AI选股策略 ——综合过滤
在实盘操作中,往往有很多其他因素。综合过滤模板展示了如何过滤掉ST股票、 选取指定概念板块股、过滤退市股等操作。
多头排列回踩均线选股策略
- 买入条件:满足条件 1)5日均线大于10日均线,10日均线大于20日均线,20日均线大于40日均线,40日均线大于120日均线;2)今日最低价小于10日收盘价均线 的股票,次日以开盘价买入;
- 买入后,如果5日均线小于40日均线,则次日以开盘价卖出。
- 允许最多同时持有20只股票
固定止盈止损功能
- 本例中,止盈条件为固定点位止盈:赚3元就卖出,并使用 stopwin_stock 用来记录止盈的股票列表;
- 止损条件为固定百分比止损:亏10%就卖出,并使用 stoploss_stock 用来记录止损的股票列表。
等资金权重配置
查看回测/模拟模块属性栏中的“初始化函数”框体内的代码,将其中的:
每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]
context.stock_weights = T.norm([1 / math.log(i + 2) for i in range(0, stock_count)])
改为等权重配置:
context.stock_weights = [1 / stock_count for i in range(0, stock_count)]
移动止损功能
在开发策略时,经常使用个股的固定点位/百分比止盈止损功能。
本策略以买入后最高价下跌10%止损为例,介绍移动止损功能的实现步骤
海龟策略
- 当今天的收盘价大于过去20个交易日中的最高价时,以收盘价买入;
- 买入后,当收盘价小于过去10个交易日中的最低价时,以收盘价卖出;
多条件选股策略
- 买入条件:满足 1)今日开盘价大于昨日收盘价;2)5日收盘价均线大于10日收盘价均线的股票按PE升序排名取前十名,次日以开盘价买入;
- 买入后,如果5日收盘价均线小于10日收盘价均线,则次日以开盘价卖出。
股票事件驱动策略
- 由于财务公告通常在晚上发布,在财务报表公告的第二日开盘买入归属母公司股东的净利润同比增长率百分比大于30%的且降序排名靠前股票(总持仓量不超过50只);
- 买入并持有40个交易日后,以第二日开盘价卖出;
双均线策略
- 当收盘价5日均线大于10日均线时,以第二日开盘价买入;
- 买入后,当收盘价的5日均线小于10日均线时,以第二日开盘价卖出;
大盘风控功能
- 在开发策略时,经常需要根据大盘指数计算相关技术指标来进行仓位风险控制。
- 本策略以上证指数5日累计涨幅作为风险控制指标,如果5日累计涨幅小于-4%,则执行清仓止损。
指定验证集概念板块
- 在预测集流程中的缺失数据处理模块m14前加入模块“选取指定概念板块股”m27(从“用户模块”——“共享模块”中找到并拖入画布)
- 连接模块m18和模块m27即可实现指定验证集股票范围为指定的概念板块。
GBDT多因子选股策略
本例使用GBDT算法进行模型训练和数据预测
- 新建可视化AI模板策略
- 在左侧模块导航栏“机器学习”中拖出“GBDT训练”和“GBDT预测”模块替换原有的 StockRanker 训练模块和 StockRanker 预测模块
TALIB指标选股策略
- 买入条件:满足 1)今日开盘价大于昨日收盘价;2)5日收盘价均线大于10日收盘价均线的股票按PE升序排名取前十名,次日以开盘价买入;
- 买入后,如果5日收盘价均线小于10日收盘价均线,则次日以开盘价卖出。
配对交易策略
- 对于股价有长期协整关系的两只股票X和Y, 可以通过历史数据回归计算两只股票的股价关系,即 Y = a*X + b, 得到相关系数a和残差项b;
- 如果两个股票所属同一行业,我们可以认为两者的股价未来应该保持上述关系,即序列 zscore=(b-mean(b,N))/std(b,N) 存在比较稳定的均值回归特性,保持在-1和1之间往复震荡;
- 当zscore小于-1时,Y股票低估,此时卖出X, 全仓买入Y;
- 当zscore大于 1时,X股票低估,此时卖出Y, 全仓买入X;
价值选股策略
- 每隔30个交易日,以开盘价买入当日0<PB<1.5且0<PE<15且有成交量的股票;
- 每隔30个交易日,将不符合上述标准的持仓股票在第二天以收盘价卖出。
期货日线MACD策略
- 相关指标定义如下: DIF=EMA(close,12)−EMA(close,26) DEM=EMA(DIF,9)
- DIF从下而上穿过DEA,买入开仓;
- DIF从上往下穿过DEA,卖出开仓;
期货AR分钟策略
-
当收盘价 ≥ 最近60日收盘价最高值时,平空开多;
-
当收盘价 ≤ 最近60日收盘价最低值时,平多开空;
期货日线布林带策略
- 相关定义如下: 中轨 = N时间段的close价格简单移动平均线 上轨 = 中轨 + K × N时间段的标准差 下轨 = 中轨 − K × N时间段的标准差 当前价格相对位置 percent_b = (close - lower)/(upper - lower) 本例中N取20,K取2
- 当percent_b ≥ 1时,以第二日开盘价平空开多;
- 当percent_b ≤ 0时,以第二日开盘价平多卖空;
深度学习预测股票价格
- 买入条件:预测的上涨概率>0.5,则买入或保持已有持仓。
- 卖出条件 :预测的上涨概率<0.5,则卖出已有股票。
深度学习DNN构建选股模型
该模型来自社区帖子基于DNN模型的智能选股策略
止盈止损
鉴于很多用户询问止赢止损、大盘风控和ST股退市股卖出过滤的混合问题,这里进行一个模版说明。
首先,所有的测试集数据在预测之前过滤,可能会导致持仓后的股票丢失排序而无法卖出或排序混乱。 因此我们尽量使用数据合并功能,把股票名称、大盘风控等指标合并到预测结果中,保证输入给回测模块的股票数据的完整性。 我们尽量在回测模块外通过数据源等模块进行数据抽取。这里介绍常用的几种数据读取,包括:
- 从 instruments_CN_STOCK_A表中抽取股票名称列name;
- 从 指数特征抽取模块中抽取指数的相关风控条件因子(例如过去五日涨幅<-0.05就判定为有风险,否则为无风险)
- 从预测分支抽取所需的其他数据比如涨跌停状态price_limit_status_0等。这里使用了一个技巧即:用来后续过滤使用的因子/指标单独放在一个证券代码列表模块中而不作为模型因子参与模型的训练。注意连接个算法模块的仅是第一个输入特征列表的输出结果!
通过数据合并后,我们的预测数据在原来的date\instrument\score\position列的基础上,增加了每日所需的大盘风控条件bm_0列、股票名称name列和涨跌停状态price_limit_status_0,以便于我们后续在回测模块中条件判断和使用。最后利用排序模块 按照每天的股票预测得分或排名进行重新排序。这里要特别注意预测结果的每日顺序是否正确!
随后我们通过初始化函数读入的数据中就包含了所需要的数据列,我们直接在主函数中引用这些列名就可以实现每日的数据获取了。
today = data.current_dt.strftime('%Y-%m-%d')
# 按日期过滤得到今日的预测数据
ranker_prediction = context.ranker_prediction[
context.ranker_prediction.date == today]
然后我们利用每日的数据ranker_prediction的各列进行条件判断:
1.大盘风控代码
#大盘风控模块,读取风控数据
benckmark_risk=ranker_prediction['bm_0'].values[0]
#当risk为1时,市场有风险,全部平仓,不再执行其它操作
if benckmark_risk > 0:
for instrument in positions.keys():
context.order_target(context.symbol(instrument), 0)
print(today,'大盘风控止损触发,全仓卖出')
return
2.资金分配并定义卖出列表
# 1. 资金分配
# 平均持仓时间是hold_days,每日都将买入股票,每日预期使用 1/hold_days 的资金
# 实际操作中,会存在一定的买入误差,所以在前hold_days天,等量使用资金;之后,尽量使用剩余资金(这里设置最多用等量的1.5倍)
is_staging = context.trading_day_index < context.options['hold_days'] # 是否在建仓期间(前 hold_days 天)
cash_avg = context.portfolio.portfolio_value / context.options['hold_days']
cash_for_buy = min(context.portfolio.cash, (1 if is_staging else 1.5) * cash_avg)
cash_for_sell = cash_avg - (context.portfolio.cash - cash_for_buy)
stock_sold = [] # 记录卖出的股票,防止多次卖出出现空单
3. 止赢止损模块
#------------------------START:止赢止损模块(含建仓期)---------------
current_stopwin_stock=[]
current_stoploss_stock = []
positions_cost={e.symbol:p.cost_basis for e,p in context.portfolio.positions.items()}
if len(positions)>0:
for instrument in positions.keys():
stock_cost=positions_cost[instrument]
stock_market_price=data.current(context.symbol(instrument),'price')
# 赚9%且为可交易状态就止盈
if stock_market_price/stock_cost-1>=0.09 and data.can_trade(context.symbol(instrument)):
context.order_target_percent(context.symbol(instrument),0)
cash_for_sell -= positions[instrument]
current_stopwin_stock.append(instrument)
# 亏5%并且为可交易状态就止损
if stock_market_price/stock_cost-1 <= -0.05 and data.can_trade(context.symbol(instrument)):
context.order_target_percent(context.symbol(instrument),0)
cash_for_sell -= positions[instrument]
current_stoploss_stock.append(instrument)
if len(current_stopwin_stock)>0:
print(today,'止盈股票列表',current_stopwin_stock)
stock_sold += current_stopwin_stock # 注意此处卖出的股票要记录到列表中
if len(current_stoploss_stock)>0:
print(today,'止损股票列表',current_stoploss_stock)
stock_sold += current_stoploss_stock # 注意此处卖出的股票要记录到列表中
#--------------------------END: 止赢止损模块--------------------------
4.固定天数卖出逻辑
#--------------------------START:持有固定天数卖出(不含建仓期)-----------
current_stopdays_stock = []
positions_lastdate = {e.symbol:p.last_sale_date for e,p in context.portfolio.positions.items()}
# 不是建仓期(在前hold_days属于建仓期)
if not is_staging:
for instrument in positions.keys():
#如果上面的止盈止损已经卖出过了,就不要重复卖出以防止产生空单
if instrument in stock_sold:
continue
# 今天和上次交易的时间相隔hold_days就全部卖出 datetime.timedelta(context.options['hold_days'])也可以换成自己需要的天数,比如datetime.timedelta(5)
if data.current_dt - positions_lastdate[instrument]>=datetime.timedelta(5) and data.can_trade(context.symbol(instrument)):
context.order_target_percent(context.symbol(instrument), 0)
current_stopdays_stock.append(instrument)
cash_for_sell -= positions[instrument]
if len(current_stopdays_stock)>0:
print(today,'固定天数卖出列表',current_stopdays_stock)
stock_sold += current_stopdays_stock # 注意此处卖出的股票要记录到列表中
#------------------------- END:持有固定天数卖出-----------------------
5. 持仓ST股和退市股卖出逻辑
#-------------------------- START: ST和退市股卖出 ---------------------
st_stock_list = []
for instrument in positions.keys():
try:
instrument_name = ranker_prediction[ranker_prediction.instrument==instrument].name.values[0]
# 如果股票状态变为了st或者退市 则卖出
if 'ST' in instrument_name or '退' in instrument_name:
if instrument in stock_sold:
continue
if data.can_trade(context.symbol(instrument)):
context.order_target(context.symbol(instrument), 0)
st_stock_list.append(instrument)
cash_for_sell -= positions[instrument]
except:
continue
if st_stock_list!=[]:
print(today,'持仓出现st股/退市股',st_stock_list,'进行卖出处理')
stock_sold += st_stock_list # 注意此处卖出的股票要记录到列表中
#-------------------------- END: ST和退市股卖出 ---------------------
6. 轮仓卖出逻辑
# 3. 生成轮仓卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰
if not is_staging and cash_for_sell > 0:
instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
lambda x: x in positions)])))
for instrument in instruments:
# 如果资金够了就不卖出了
if cash_for_sell <= 0:
break
#防止多个止损条件同时满足,出现多次卖出产生空单
if instrument in stock_sold:
continue
context.order_target(context.symbol(instrument), 0)
cash_for_sell -= positions[instrument]
stock_sold.append(instrument) # 轮仓卖出的股票可以选择添加到卖出列表中
7.轮仓买入逻辑
# 4. 生成轮仓买入订单:按机器学习算法预测的排序,买入前面的stock_count只股票
# 计算今日跌停的股票
dt_list = list(ranker_prediction[ranker_prediction.price_limit_status_0==1].instrument)
# 计算今日ST/退市的股票
st_list = list(ranker_prediction[ranker_prediction.name.str.contains('ST')|ranker_prediction.name.str.contains('退')].instrument)
# 计算所有禁止买入的股票池banned_list,包括已卖出的股票和跌停的股票以及ST/退市股票
banned_list = stock_sold+dt_list+st_list
buy_cash_weights = context.stock_weights
buy_instruments=[k for k in list(ranker_prediction.instrument) if k not in banned_list][:len(buy_cash_weights)]
max_cash_per_instrument = context.portfolio.portfolio_value * context.max_cash_per_instrument
for i, instrument in enumerate(buy_instruments):
cash = cash_for_buy * buy_cash_weights[i]
if cash > max_cash_per_instrument - positions.get(instrument, 0):
# 确保股票持仓量不会超过每次股票最大的占用资金量
cash = max_cash_per_instrument - positions.get(instrument, 0)
if cash > 0:
context.order_value(context.symbol(instrument), cash)
\