分享市盈率股息率选股+均线止盈交易策略
本策略得出的结论是稳定分红的高股息股票,跌破均值时,价值一定会回归。当然时间是1个月还是2年就不一定了,手动狗头
选股:
每个月选出一个股票池。选股的策略是市盈率10倍-30倍之间,股息率在4%以上,按股息率排序。
择时:
开盘时,买入股票池中,价格小于10日均线95%的股票。
当持仓个股盈利16%时卖出,亏损10%时补仓。
年化20%左右,最大回撤30%左右,胜率:1!
后续优化思路:
1 持仓个股盈利16%时其实不急着卖出,可以判断一下当前的市盈率,如果市盈率没怎么变,是每股收益增加引起的股价增长,可以继续持有。
2 选股可以再加上其他因子。
3 资金利用率比较低,如果改成轮动不知道会不会好一点
源码:
from jqdata import *
from kuanke.wizard import *
def initialize(context):
# 定义一个全局变量, 保存要操作的股票
# (股票:黄河旋风,曲美家具,浙江美大,再生科技)
g.mavalue = 10;
g.buy_value=10000
g.add_percent = -0.10
g.sell_percent = 0.16
# 初始化此策略
set_option('use_real_price', True)
choosePool(context);
run_monthly(choosePool, 1, time='09:00')
# 每个单位时间(如果按天回测,则每天调用一次,如果按分钟,则每分钟调用一次)调用一次
def handle_data(context, data):
# for i in 0,top:
for security in g.security:
judgeAndBuy2(context,data,security,0.95)
for security,v in context.portfolio.positions.items():
judgeAndBuy2(context,data,security,0.95);
#卖出
for security,v in context.portfolio.positions.items():
judgeAndSell2(context,data,security);
# 画出上一时间点价格
record(cash=context.portfolio.available_cash)
def judgeAndBuy2(context,data,security,percent=0.95):
current_data = get_current_data()
is_st = current_data[security].is_st
if is_st:
return;
# 取得过去五天的平均价格
average_price = data[security].mavg(g.mavalue, 'close')
ma10 = data[security].mavg(10, 'close')
# 取得上一时间点价格
current_price = data[security].close
#每次买卖数目,当天最多买入数目
buy_amount = 100
closeable_amount = context.portfolio.positions[security].closeable_amount
avg_cost = context.portfolio.positions[security].avg_cost
# log.info("沪深 %s baseStockMa5 %s" % (baseStockPrice,baseStockMa5));
if context.portfolio.positions[security].today_amount >= buy_amount:
return;
if closeable_amount==0:
if current_price < percent*average_price :
buyValue = g.buy_value
# if context.portfolio.cash < buyValue:
# judgeAndSellMaxTime(context,data,buyValue-context.portfolio.cash)
order_value(security, buyValue)
else:
if (current_price - avg_cost) / avg_cost < g.add_percent:
buyValue = g.buy_value
# buyValue = 10000
# if context.portfolio.cash < buyValue:
# judgeAndSellMaxTime(context,data,buyValue-context.portfolio.cash)
# order(security, closeable_amount)
order_value(security, buyValue)
def judgeAndSell2(context,data,security):
current_data = get_current_data()
if context.portfolio.positions[security].today_amount > 0:
return;
is_st = current_data[security].is_st
if is_st:
order_target(security, 0)
return;
# 取得过去五天的平均价格
average_price = data[security].mavg(10, 'close')
# 取得上一时间点价格
current_price = data[security].close
closeable_amount = context.portfolio.positions[security].closeable_amount
avg_cost = context.portfolio.positions[security].avg_cost
if closeable_amount <= 0:
return;
days = (context.current_dt - context.portfolio.positions[security].init_time).days
value = context.portfolio.positions[security].value
if (current_price - avg_cost) / avg_cost > g.sell_percent:
# 卖出股票
# order(security, -1 * closeable_amount)
order_target(security, 0)
log.info("卖出%s 持仓天数%s" % (security,days));
return;
def choosePool(context):
securitys = get_index_stocks('000001.XSHG')
date=context.previous_date
top = 10
df = DividendRatio2(date,top)
# df = DividendRatio(securitys,date,top)
# log.info("df!!!!!");
# log.info(df);
g.security = [];
for i in range(0,top):
current = df.loc[i];
if current['DividendRatio'] < 0.04:
continue;
# log.info("current:%s" % (current));
security = normalize_code(current['SecuCode']);
g.security.append(security)
# if len(g.security) > 3:
# break;
log.info("choosePool")
log.info(len(g.security));
log.info("df.loc");
first = df.loc[0];
log.info("first!!!");
log.info(first);
def DividendRatio2(end_date,top=50):
syldf = get_fundamentals(query(
valuation.code, valuation.pe_ratio
).filter(
valuation.pe_ratio < 25,
valuation.pe_ratio > 10
).order_by(
# 按市值降序排列
valuation.market_cap.desc()
).limit(
# 最多返回100个
100
), date=end_date)
security_list = [];
for i in range(0,len(syldf)):
current = syldf.loc[i];
# log.info("current:%s" % (current));
security_list.append(current['code'])
# security_list = financial_data_filter_dayu(security_list, indicator.inc_revenue_year_on_year, 0)
# security_list = financial_data_filter_dayu(security_list, indicator.inc_net_profit_year_on_year, 0)
# log.info(security_list)
return DividendRatio(security_list,end_date,top);
def DividendRatio(security_list,end_date,top=50):
'''查询股息率(日更新) 注意3000条限制,超限时获取的数据将不完整甚至报错
输入:股票池,截止日期,获取数量
输出:panel结构,单位:1'''
trade_days = get_trade_days(end_date=end_date,count = 1)
security_list.sort()
# log.info("security_list %s" % security_list);
secu_list = [x[:6] for x in security_list]
# log.info("secu_list %s" % secu_list);
code_df = jy.run_query(query(
jy.SecuMain.InnerCode,jy.SecuMain.SecuCode,
# jy.SecuMain.ChiName,jy.SecuMain.CompanyCode
).filter(jy.SecuMain.SecuCategory == 1,
jy.SecuMain.SecuCode.in_(secu_list)).order_by(jy.SecuMain.SecuCode))
# log.info(len(code_df))
# log.info(len(security_list))
# log.info(code_df)
# log.info(security_list)
# code_df['code'] = security_list
df = jy.run_query(query(
# jy.LC_DIndicesForValuation #得到整表
jy.LC_DIndicesForValuation.InnerCode,
jy.LC_DIndicesForValuation.TradingDay,
jy.LC_DIndicesForValuation.DividendRatio,
).filter(jy.LC_DIndicesForValuation.InnerCode.in_(code_df.InnerCode),
jy.LC_DIndicesForValuation.TradingDay.in_(trade_days)
))
# df = pd.concat([df,code_df],axis=1)
df = pd.merge(df, code_df, on = 'InnerCode')
df = df.sort(columns = ['DividendRatio'],axis = 0,ascending = False)
df.reset_index(inplace=True)
df = df.loc[0:top];
return df