今天,公众号将为全网读者带来掘金量化复现果仁策略系列!
公众号将为大家多维度、多策略、多场景来讲述掘金量化平台的实践应用。同时,我们对每段代码都做了解读说明,愿你在Quant的道路上学有所获!
此系列将由浅入深,每月1~2期,大家敬请期待!
今日复现策略:《小市值择时策略,模型Ⅰ--定期轮动》
果仁网策略定义
投资域:
股票池:全部股票
系统股票池:全部股票
指数:全部
板块:全部
行业标准:申万 2014
行业:全部
二级行业:全部
交易所:全部
地区省份:全部
企业性质:全部
融资融券:全部
ST:排除ST
科创板:排除科创板
过滤停牌股票:是
筛选条件:
去14月后半月10天大于0
排名条件:
总市值从小到大全部1
交易模型:
模型:I
调仓周期:1
调仓价格:收盘价
空闲资金配置:无
实时选股:未勾选
最大持仓股票数:5
备选买入股票数:5
个股最大买入仓位:100%
个股仓位权重:平权
大盘择时:
无
掘金复现过程
step 1:筛选条件
-
排除ST
-
排除科创板
-
过滤停牌股票
-
去14月后半月10天
定义:and( not( and ( MonthY()=4,dayM(0) >10) ), not( and ( MonthY()=1,dayM(0) >10) ) )
date = pd.Timestamp(date).replace(tzinfo=None)
# A股,剔除停牌和ST股票 https://www.myquant.cn/docs2/sdk/python/API 介绍/通用数据函数(免费).html#get-symbols-查询指定交易日多标的交易信息
stocks_info = get_symbols(sec_type1=1010, sec_type2=101001, skip_suspended=skip_suspended, skip_st=skip_st, trade_date=date.strftime('%Y-%m-%d'), df=True)
# 剔除次新股和退市股
stocks_info['listed_date'] = stocks_info['listed_date'].apply(lambda x:x.replace(tzinfo=None))
stocks_info['delisted_date'] = stocks_info['delisted_date'].apply(lambda x:x.replace(tzinfo=None))
stocks_info = stocks_info[(stocks_info['listed_date']<=date-datetime.timedelta(days=new_days))&(stocks_info['delisted_date']>date)]
# 排除科创板股票(代码以 SHSE.688 开头)
stocks_info = stocks_info[~stocks_info['symbol'].str.startswith('SHSE.688')]
# 一四月择时
stocks_info = pd.DataFrame(columns=['symbol']) if not (not ((context.current_month == 4 and context.current_day > 10) or (context.current_month == 1 and context.current_day > 10))) else stocks_info
step 2:排序条件
-
总市值从小到大全部1
# 配置排序条件(示例:A股总市值+A股流通市值)
indicators = [
{'field': 'tot_mv', 'direction': 'asc', 'weight': 1}, # A股总市值
# {'field': 'a_mv_ex_ltd', 'direction': 'asc', 'weight': 1} # A股流通市值
]
# 获取所有字段数据
fields = [ind['field'] for ind in indicators]
df = stk_get_daily_mktvalue_pt(symbols=stocks, trade_date=date,
fields=','.join(fields), df=True)
# 空值处理(空值排最后)
for ind in indicators:
field = ind['field']
if ind['direction'] == 'asc':
df[field] = df[field].fillna(float('inf')) # 升序时空值用极大值填充
else:
df[field] = df[field].fillna(float('-inf')) # 降序时用极小值填充
# 计算综合得分
total_score = pd.Series(0, index=df.index)
for ind in indicators:
field = ind['field']
weight = ind['weight']
# 计算排名(升序时直接排序,降序时取负数)
if ind['direction'] == 'asc':
sorted_col = df[field]
else:
sorted_col = -df[field]
# 计算排名分
ranks = sorted_col.rank(method='min', ascending=True)
score = ((len(df) - ranks + 1) / len(df) * 100).round(2)
df[f'{field}_score'] = score
total_score += score * weight
# 按综合得分排序
df['total_score'] = total_score
ranked = df.sort_values(by='total_score', ascending=False)
step 2:交易模型I
在每个调仓日,卖出不满足选股条件的股票,买进满足选股条件的股票, 所有仓内股票在调仓日重新平衡为等权重
# 筛选
all_stock,all_stock_str = filter_stocks(context,last_date)
# 排序
target_position = [] if not all_stock else rank_stocks(context, last_date, all_stock)
# 获取当前持仓
positions = get_position()
# 平不在标的池的股票(注:本策略交易以收盘价为交易价格,当调整定时任务时间时,需调整对应价格)
for position in positions:
symbol = position['symbol']
if symbol not in target_position:
new_price = \
history_n(symbol=symbol, frequency='1d', count=1, end_time=now_str, fields='close', adjust=ADJUST_PREV,
adjust_end_time=context.backtest_end_time, df=False)[0]['close']
order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Limit, position_side=PositionSide_Long,
price=new_price)
# 获取股票的权重(预留出1%资金,防止剩余资金不够手续费抵扣)
if len(target_position)>0:
percent = 0.99 / len(target_position)
# 买在标的池中的股票(注:本策略交易以收盘价为交易价格,当调整定时任务时间时,需调整对应价格)
for symbol in target_position:
# 收盘价(日频数据)
new_price = \
history_n(symbol=symbol, frequency='1d', count=1, end_time=now_str, fields='close', adjust=ADJUST_PREV,
adjust_end_time=context.backtest_end_time, df=False)[0]['close']
order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Limit,
position_side=PositionSide_Long, price=new_price)
复现结果验证
完整代码获取