最近几年,小市值策略一直都收益不错(当然,不包含17年和18年)。小市值因子对收益的影响是很大的。特别是行情不好的时候,大家都忙着炒作热点,那么这时候符合题材的小市值更加符合炒作标准了。
为什么说是魔改版的小市值策略呢,因为最终选取小市值的方法真的很玄学。
上收益效果图:
三年多,有7.45倍的收益,实属夸张了哈。有这个收益,还要什么自行车啊。
那么,这个策略玄学在哪里呢。
核心想法:
1、选取100个市值最小的股票,剔除ST股、次新股、科创板、北交所股票等等。
2、从剩下的股票中对证券代码进行排序,这就是最玄学的地方。然后选取排序靠前的5只股票。
3、每周第一个交易日早上进行轮换。
def monthly_filter(context):
today = context.current_dt
yesterday = today - datetime.timedelta(days=1)
start_day = today - datetime.timedelta(days=375)
yesterday = yesterday.strftime('%Y-%m-%d')
# 选出小市值的股票
q = query(
valuation.code,
valuation.circulating_market_cap
).filter(
valuation.circulating_market_cap.between(0,1000)
).order_by(
valuation.circulating_market_cap.asc()).limit(100)
codes = get_fundamentals(q).code.tolist()
# 筛选出主板、创业板股票
codes = [code for code in codes if code[:2] in ('60','00','30')]
# log.info("Top 10 小市值:" + str(codes[:10]))
# 过滤ST股票
df = get_extras('is_st', codes, end_date=yesterday,count=1)
df = df.T
df.columns = ['is_st']
df=df[df['is_st']==0]
codes = df.index.tolist()
# 过滤次新股
q = query(finance.STK_LIST.code).filter(
finance.STK_LIST.start_date <=start_day,
finance.STK_LIST.code.in_(codes)
)
codes = list(finance.run_query(q).code)
g.pools = set(codes)
def weekly_filter(context):
today = context.current_dt
yesterday = today - datetime.timedelta(days=1)
start_day = today - datetime.timedelta(days=375)
yesterday = yesterday.strftime('%Y-%m-%d')
codes = list(g.pools)
if codes != []:
# 过滤ST股票
df = get_extras('is_st', codes, end_date=yesterday,count=1)
df = df.T
df.columns = ['is_st']
df=df[df['is_st']==0]
codes = df.index.tolist()
# codes = filter_st_stock(codes)
g.pools = set(codes)
else:
# 选出小市值的股票
q = query(
valuation.code,
valuation.circulating_market_cap
).filter(
valuation.circulating_market_cap.between(0,1000)
).order_by(
valuation.circulating_market_cap.asc()).limit(100)
codes = get_fundamentals(q).code.tolist()
# 筛选出主板、创业板股票
codes = [code for code in codes if code[:2] in ('60','00','30')]
# 过滤ST股票
df = get_extras('is_st', codes, end_date=yesterday,count=1)
df = df.T
df.columns = ['is_st']
df=df[df['is_st']==0]
codes = df.index.tolist()
# 过滤次新股
q = query(finance.STK_LIST.code).filter(
finance.STK_LIST.start_date <= start_day,
finance.STK_LIST.code.in_(codes)
)
codes = list(finance.run_query(q).code)
g.pools = set(codes)
# log.info('每周五选定的股票:', g.pools)
#1-1 准备股票池
def prepare_stock_list(context):
#获取已持有列表
g.hold_list= list(context.portfolio.positions.keys())
#获取昨日涨停列表
if g.hold_list != []:
df = get_price(g.hold_list, end_date=context.previous_date, frequency='daily', fields=['close','high_limit'], count=1, panel=False, fill_paused=False)
df = df[df['close'] == df['high_limit']]
g.yesterday_HL_list = list(df.code)
else:
g.yesterday_HL_list = []
# 获取今天的日期
today = context.current_dt
today_Year = today.strftime('%Y')
NewYears = {'2010': '02-14', '2011': '02-03', '2012': '01-23', '2013': '02-10', '2014': '01-31', '2015': '02-19', '2016': '02-08',
'2017': '01-28', '2018': '02-16', '2019': '02-05', '2020': '01-25', '2021': '02-12', '2022': '02-01', '2023': '01-22',
'2024': '02-10', '2025': '01-29', '2026': '02-17', '2027': '02-06', '2028': '01-26', '2029': '02-13', '2030': '02-03'}
spring_festival_date = NewYears[today_Year]
#判断今天是否为账户资金再平衡的日期 或者 元旦到春节之间的日期
g.no_trading_today_signal = today_is_between(context, '04-05', '04-30') or today_is_between(context, '01-01', spring_festival_date)
def get_stock_list(context):
final_list = set()
initial_list = list(g.pools)
if len(initial_list) == 0:
return []
initial_list = filter_paused_stock(initial_list)
initial_list = filter_st_stock(initial_list)
today = context.current_dt
# 通过timedelta算出前一天的日期
delta = datetime.timedelta(days=1)
yesterday = today - delta
yesterday = yesterday.strftime('%Y-%m-%d')
q = query(valuation.code,
valuation.circulating_market_cap).filter(valuation.code.in_(initial_list)).limit(g.stock_num)
codes = get_fundamentals(q).code.tolist()
circulating_market_cap_list = get_fundamentals(q).circulating_market_cap.tolist()
log.info('数据库拉取的股票数量:{}'.format(len(codes)))
log.info('最小市值:{}, 最大市值:{}'.format(min(circulating_market_cap_list), max(circulating_market_cap_list)))
final_list = codes.copy()
print('final_list数量:{}'.format(len(final_list)))
final_list = filter_paused_stock(final_list)
print('过滤停牌后的股票数量:{}'.format(len(final_list)))
final_list = filter_st_stock(final_list)
log.info('每周一买入的股票数量:'.format(len(final_list)))
return final_list
#1-3 整体调整持仓
def weekly_adjustment(context):
if g.no_trading_today_signal:
return
#获取应买入列表
target_list = get_stock_list(context)
#调仓卖出
for stock in g.hold_list:
if (stock not in target_list) and (stock not in g.yesterday_HL_list):
log.info("卖出[%s]" % (stock))
order_target(stock, 0)
else:
log.info("已持有[%s]" % (stock))
#调仓买入
position_count = len(context.portfolio.positions)
target_num = len(target_list)
if target_num > position_count:
value = context.portfolio.cash / (target_num - position_count)
for stock in target_list:
if context.portfolio.positions[stock].total_amount == 0:
if open_position(stock, value):
if len(context.portfolio.positions) == target_num:
break