1. 策略概述
这个策略可以理解为一种“挑选稳健股票”的方法。它主要通过筛选那些股息高、价格波动小、公司负债少的股票,来构建一个稳健的投资组合。简单来说,我们会优先选择那些每年分红较多的股票,然后在这些股票中,再进一步挑选出价格波动较小、公司负债较低的股票。这样,我们就能形成一个风险较小、收益稳定的投资组合。为了减少频繁买卖带来的成本和风险,这个策略不会频繁调整持仓,而是更注重组合的稳定性。
2. 策略逻辑
多因子选股:
首先,我们会挑选出那些股息率高的股票,股息率高意味着这些公司每年分红较多。接着,我们在这些股票中筛选出那些价格波动较小和公司负债率较低的股票。这样,最终得到的股票组合不仅有稳定的现金流(分红),而且价格波动不大,负债风险也较低。
持仓调整:
每周,我们都会根据最新的筛选结果,适当调整我们的持仓。如果发现有些股票不再符合条件,我们就会卖出这些股票,并买入新的符合条件的股票。这种调整频率不会太高,目的是保持组合的稳定性。
风险控制:
为了降低风险,我们会避免购买一些高风险的股票,比如已经涨停或跌停的股票,ST股(有退市风险的股票)、次新股(刚上市不久的股票)以及科创板股票(这些股票的价格波动较大)。通过这些筛选,我们可以让投资组合更加稳健。
调仓限制:
我们还会记录最近一段时间内持有过的股票,避免再次买入这些股票。这样做是为了减少频繁交易的风险,避免不必要的成本和损失。
3. 策略代码详解
3.1 初始化函数 initialize
在策略启动时,我们需要设置一些基本的配置,比如选择中证500指数作为基准,这样可以让我们知道自己的表现如何。我们还会启用真实价格进行交易,设定交易成本,避免未来数据干扰。这里还设置了最大持股数量和持仓天数限制,确保组合的多样性和稳定性。下面是代码的具体实现:
def initialize(context):
set_benchmark('000905.XSHG') # 中证500指数作为基准
set_option('use_real_price', True) # 使用真实价格进行交易
set_option("avoid_future_data", True) # 防止使用未来数据
set_slippage(FixedSlippage(0)) # 设置固定滑点为0
set_order_cost(OrderCost(open_tax=0, close_tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5), type='fund')
log.set_level('order', 'error') # 过滤日志,仅保留error级别以上的日志
g.stock_num = 10 # 最大持股数量
g.limit_days = 20 # 持仓天数限制
g.limit_up_list = [] # 涨停股票列表
g.hold_list = [] # 当前持仓列表
g.history_hold_list = [] # 历史持仓列表
g.not_buy_again_list = [] # 不再买入的股票列表
run_daily(prepare_stock_list, time='9:05', reference_security='000300.XSHG')
run_weekly(weekly_adjustment, weekday=1, time='9:30', reference_security='000300.XSHG')
run_daily(check_limit_up, time='14:00', reference_security='000300.XSHG')
run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')
功能解释:
这里的初始化函数主要是为策略设置基础配置,确保我们在正确的时间执行相应的操作,并且有明确的持仓数量和持仓时间限制。通过这些配置,我们能够有效地管理投资组合,避免不必要的风险。
3.2 获取股息率筛选列表 get_dividend_ratio_filter_list
我们会通过最近一年的分红情况来计算每只股票的股息率,并根据股息率筛选出最优质的股票。代码如下:
def get_dividend_ratio_filter_list(context, stock_list, sort, p1, p2):
time1 = context.previous_date
time0 = time1 - datetime.timedelta(days=365)
interval = 1000
list_len = len(stock_list)
q = query(finance.STK_XR_XD.code, finance.STK_XR_XD.a_registration_date, finance.STK_XR_XD.bonus_amount_rmb
).filter(finance.STK_XR_XD.a_registration_date >= time0,
finance.STK_XR_XD.a_registration_date <= time1,
finance.STK_XR_XD.code.in_(stock_list[:min(list_len, interval)]))
df = finance.run_query(q)
if list_len > interval:
for i in range(list_len // interval):
q = query(finance.STK_XR_XD.code, finance.STK_XR_XD.a_registration_date, finance.STK_XR_XD.bonus_amount_rmb
).filter(finance.STK_XR_XD.a_registration_date >= time0,
finance.STK_XR_XD.a_registration_date <= time1,
finance.STK_XR_XD.code.in_(stock_list[interval * (i + 1):min(list_len, interval * (i + 2))]))
temp_df = finance.run_query(q)
df = df.append(temp_df)
dividend = df.fillna(0).groupby('code').sum()
q = query(valuation.code, valuation.market_cap).filter(valuation.code.in_(dividend.index.tolist()))
cap = get_fundamentals(q, date=time1)
DR = pd.concat([dividend, cap.set_index('code')], axis=1, sort=False)
DR['dividend_ratio'] = (DR['bonus_amount_rmb'] / 10000) / DR['market_cap']
DR = DR.sort_values(by='dividend_ratio', ascending=sort)
final_list = list(DR.index)[int(p1 * len(DR)):int(p2 * len(DR))]
return final_list
功能解释:
这个函数通过计算每只股票的股息率(分红除以公司市值),筛选出那些股息率最高的股票。这些股票通常会提供较为稳定的现金流,对长期投资者非常有吸引力。
3.3 选股模块 get_stock_list
在筛选完股息率后,我们会进一步筛选出那些波动性较低和负债率较低的股票。代码如下:
def get_stock_list(context):
yesterday = context.previous_date
initial_list = get_all_securities().index.tolist()
initial_list = filter_kcbj_stock(initial_list)
initial_list = filter_new_stock(context, initial_list, 375)
initial_list = filter_st_stock(initial_list)
dr_list = get_dividend_ratio_filter_list(context, initial_list, False, 0, 0.5)
tv_list = get_factor_filter_list(context, dr_list, 'turnover_volatility', False, 0, 0.8)
lev_list = get_factor_filter_list(context, tv_list, 'MLEV', True, 0, 0.5)
q = query(valuation.code, valuation.circulating_market_cap).filter(valuation.code.in_(lev_list)).order_by(valuation.circulating_market_cap.asc())
df = get_fundamentals(q, date=yesterday)
final_list = list(df.code)[:15]
return final_list
功能解释:
这个选股模块进一步筛选出那些价格波动较小、负债率较低的股票,从而构建一个更加稳健的股票池。最终选出的股票不仅股息率高,而且财务状况良好,适合长期持有。
3.4 准备股票池 prepare_stock_list
每个交易日开始前,我们会更新当前持仓列表和最近持有过的股票列表,以确保我们在调仓时的信息准确。代码如下:
def prepare_stock_list(context):
g.hold_list = [position.security for position in context.portfolio.positions.values()]
g.history_hold_list.append(g.hold_list)
if len(g.history_hold_list) >= g.limit_days:
g.history_hold_list = g.history_hold_list[-g.limit_days:]
temp_set = set(stock for hold_list in g.history_hold_list for stock in hold_list)
g.not_buy_again_list = list(temp_set)
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.high_limit_list = list(df.code)
else:
g.high_limit_list = []
功能解释:
这个函数的作用是更新当前持仓的股票列表以及最近持有过的股票,避免在调仓时重复买入相同的股票,从而减少交易成本和风险。
3.5 整体调整持仓 weekly_adjustment
每周,我们会根据新的选股结果调整持仓,卖出不符合条件的股票,并买入新的高分股票。代码如下:
def weekly_adjustment(context):
target_list = get_stock_list(context)
target_list = filter_paused_stock(target_list)
target_list = filter_limitup_stock(context, target_list)
target_list = filter_limitdown_stock(context, target_list)
target_list = target_list[:min(g.stock_num, len(target_list))]
for stock in g.hold_list:
if stock not in target_list and stock not in g.high_limit_list:
log.info(f"卖出[{stock}]")
close_position(context.portfolio.positions[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
功能解释:
这个函数帮助我们每周调整持仓,确保我们的投资组合始终由符合条件的股票组成。通过这种定期调整,我们能够及时应对市场变化,保持投资组合的优质性和稳定性。
3.6 涨停股监控 check_limit_up
每天,我们都会监控那些前一天涨停的股票,看看它们今天的表现。如果涨停板被打开,我们会考虑卖出这些股票。代码如下:
def check_limit_up(context):
now_time = context.current_dt
if g.high_limit_list:
for stock in g.high_limit_list:
current_data = get_price(stock, end_date=now_time, frequency='1m', fields=['close', 'high_limit'], skip_paused=False, fq='pre', count=1, panel=False, fill_paused=True)
if current_data.iloc[0, 0] < current_data.iloc[0, 1]:
log.info(f"[{stock}] 涨停打开,卖出")
close_position(context.portfolio.positions[stock])
功能解释:
这个函数帮助我们在交易日内实时监控前一天涨停的股票,如果今天涨停被打开,我们会卖出这些股票以保护我们的收益。
4. 策略总结
“高股息低波动择优策略” 是一种结合了股息率、价格波动性和负债率的选股方法。通过定期调整持仓,并严格筛选符合条件的股票,这个策略能够在降低风险的同时,保持投资组合的稳定性,非常适合那些追求长期稳健收益的投资者。
长按扫描下方二维码获取本文完整源码下载方式:
点击阅读原文,加入「宽客邦量化俱乐部」