# 风险及免责提示:该策略由聚宽用户分享,仅供学习交流使用。
# 原文一般包含策略说明,如有疑问建议到原文和作者交流讨论。
# 克隆自聚宽文章:https://www.joinquant.com/post/29574
# 标题:精选价值策略
# 作者:BAFE
'''
投资程序:
霍华.罗斯曼强调其投资风格在于为投资大众建立均衡、且以成长为导向的投资组合。选股方式偏好大型股,
管理良好且为领导产业趋势,以及产生实际报酬率的公司;不仅重视公司产生现金的能力,也强调有稳定成长能力的重要。
总市值大于等于50亿美元。
良好的财务结构。
较高的股东权益报酬。
拥有良好且持续的自由现金流量。
稳定持续的营收成长率。
优于比较指数的盈余报酬率。
'''
import pandas as pd
import numpy as np
from jqdata import *
# 初始化函数,设定基准等等
def initialize(context):
# 设定沪深300
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 输出内容到日志 log.info()
log.info('初始函数开始运行且全局只运行一次')
# 过滤掉order系列API产生的比error级别低的log
# log.set_level('order', 'error')
#策略参数设置
#操作的股票列表
g.buy_list = []
# 设置滑点
set_slippage(FixedSlippage(0.0026))
### 股票相关设定 ###
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 个股最大持仓比重
g.security_max_proportion = 0.2
# 最大建仓数量
g.max_hold_stocknum = 10
# 单只最大买入股数或金额
g.max_buy_value = None
g.max_buy_amount = None
# 委托类型
g.order_style_str = 'by_cap_mean'
g.order_style_value = 100
# 每月第5个交易日进行操作
# 开盘前运行
run_monthly(before_market_open,10,time='before_open', reference_security='000300.XSHG')
# 开盘时运行
run_monthly(market_open,10,time='open', reference_security='000300.XSHG')
## 开盘前运行函数
def before_market_open(context):
# 获取要操作的股票列表,过滤st股和停牌股
temp_list = get_stock_list(context)
# 去除周期行业
g.industry_list = ["801010","801040","801050","801080","801110","801120","801130","801150","801160","801200","801230","801710","801720","801730","801740","801750","801760","801770","801780","801790"]
temp_list = industry_filter(context, temp_list, g.industry_list)
log.info('满足条件的股票有%s只'%len(temp_list))
#按市值进行排序
g.buy_list = get_check_stocks_sort(context,temp_list)
print(("the list consist of ",g.buy_list));
## 开盘时运行函数
def market_open(context):
#卖出不在买入列表中的股票
sell(context,g.buy_list)
#买入不在持仓中的股票,按要操作的股票平均资金
buy(context,g.buy_list)
#交易函数 - 买入
def buy(context, buy_lists):
# 获取最终的 buy_lists 列表
Num = g.max_hold_stocknum - len(context.portfolio.positions)
buy_lists = buy_lists[:Num]
# 买入股票
if len(buy_lists)>0:
# model1:
# cash=Allocation_of_funds(context,buy_lists,g.max_hold_stocknum,g.max_buy_value)
# for stock in buy_lists:
# order_target_value(stock,cash)
# 持仓再平衡策略
# model2
cash=Risk_rebalancing(context,buy_lists)
stock1=set(buy_lists)
stock2=set(context.portfolio.positions.keys())
buy_lists=stock1|stock2
for stock in buy_lists:
order_target_value(stock,cash)
return
# 交易函数 - 出场
def sell(context, buy_lists):
# 获取 sell_lists 列表
hold_stock = list(context.portfolio.positions.keys())
for s in hold_stock:
#卖出不在买入列表中的股票
if s not in buy_lists:
order_target_value(s,0)
#按流通市值进行排序
#从大到小
def get_check_stocks_sort(context,check_out_lists):
df = get_fundamentals(query(
valuation.circulating_market_cap,
valuation.pe_ratio,
valuation.code
).filter(
valuation.code.in_(check_out_lists)
).order_by(
# 按市值降序排列
valuation.circulating_market_cap.desc()
),date=context.previous_date)
out_lists = list(df['code'].values)
return out_lists
'''
1.总市值≧市场平均值*1.0。
2.最近一季流动比率≧市场平均值(流动资产合计/流动负债合计)。
3.近四季股东权益报酬率(roe)≧市场平均值。
4.近五年自由现金流量均为正值。(cash_flow.net_operate_cash_flow - cash_flow.net_invest_cash_flow)
5.近四季营收成长率介于6%至30%()。 'IRYOY':indicator.inc_revenue_year_on_year, # 营业收入同比增长率(%)
6.近四季盈余成长率介于8%至50%。(eps比值)
'''
def get_stock_list(context):
# 根据当前时间,获取四个报告期的报告时间
t1,t2,t3,t4=statDate_value(context)
# 获取当前市场非ST,未停牌个股,作为初始股池
temp_list = filter_st_paused(context)
# #获取多期财务数据
# panel = get_data(temp_list,4)
#1.总市值≧市场平均值*1.0。
df1=get_fundamentals(query(
valuation.code,
valuation.circulating_market_cap,
).filter(
valuation.code.in_(temp_list)
),date=context.previous_date)
dfx1=df1[df1['circulating_market_cap']>df1['circulating_market_cap'].mean()]
l1=set(dfx1.code.values)
#2.最近一季流动比率≧市场平均值(流动资产合计/流动负债合计)。
df2=get_fundamentals(query(
balance.total_current_assets,
balance.total_current_liability,
balance.code).filter(
valuation.code.in_(temp_list)
),statDate=t1)
# 剔除数据缺失行,以及流动性负债为0
df2.dropna(axis=0,inplace=True)
dfx2=df2[df2['total_current_liability'] != 0]
dfx2['cr'] = dfx2['total_current_assets']/dfx2['total_current_liability']
dfx2=dfx2[dfx2['cr']>dfx2['cr'].mean()]
l2=set(dfx2.code.values)
#3.近四季股东权益报酬率(roe)≧市场平均值。
def roe_value(i,date,l2):
df=get_fundamentals(query(
indicator.code,
indicator.roe
).filter(
indicator.code.in_(l2)
),statDate=date)
df.rename(columns={'roe':'roe'+str(i)},inplace=True)
return df
df31=roe_value(1,t1,temp_list)
df32=roe_value(2,t2,temp_list)
df33=roe_value(3,t3,temp_list)
df34=roe_value(4,t4,temp_list)
df3=df31.merge(df32,on='code')
df3=df3.merge(df33,on='code')
df3=df3.merge(df34,on='code')
# 连续四个季度ROE处于市场平均之上
dfx3=df3[(df3.roe1 >=df3.roe1.mean()) & (df3.roe2>=df3.roe2.mean()) & (df3.roe3>=df3.roe3.mean())&(df3.roe4>=df3.roe4.mean())]
l3=set(dfx3.code.values)
#4.近三年自由现金流量均为正值。(cash_flow.net_operate_cash_flow - cash_flow.net_invest_cash_flow)
# 实际年报数据需要在4月底才能公布完上一年的年报。
y =int(context.current_dt.year)
if context.current_dt.month>=5:
dt1,dt2,dt3=str(y-1),str(y-2),str(y-3)
else:
dt1,dt2,dt3=str(y-2),str(y-3),str(y-4)
def FCF(i,date,stocks):
df=get_fundamentals(query(
cash_flow.code,
cash_flow.net_operate_cash_flow,
cash_flow.net_invest_cash_flow
).filter(
valuation.code.in_(stocks)
),statDate=date)
df['FCF'+str(i)]=df['net_operate_cash_flow']+df['net_invest_cash_flow']
df.drop(['net_operate_cash_flow','net_invest_cash_flow'],axis=1,inplace=True)
return df
df41= FCF(1,dt1,temp_list)
df42= FCF(2,dt2,temp_list)
df43= FCF(3,dt3,temp_list)
df4=df41.merge(df42,on='code')
df4=df4.merge(df43,on='code')
dfx4=df4[pd.eval('(df4.FCF1 >0) & (df4.FCF2 >0) & (df4.FCF3 >0)')]
l4=set(dfx4.code.values)
#5.近四季营收成长率介于0%至50%()。 'IRYOY':indicator.inc_revenue_year_on_year, # 营业收入同比增长率(%)
def REVENUE_value(i,date,l2):
df=get_fundamentals(query(
indicator.code,
indicator.inc_revenue_year_on_year
).filter(
indicator.code.in_(l2),
indicator.inc_revenue_year_on_year>6,
indicator.inc_revenue_year_on_year<50
),statDate=date)
df.rename(columns={'inc_revenue_year_on_year':'inc_revenue_year_on_year'+str(i)},inplace=True)
return df
df51=REVENUE_value(1,t1,temp_list)
df52=REVENUE_value(2,t2,temp_list)
df53=REVENUE_value(3,t3,temp_list)
df54=REVENUE_value(4,t4,temp_list)
df5=df51.merge(df52,on='code')
df5=df5.merge(df53,on='code')
df5=df5.merge(df54,on='code')
l5=set(df5.code.values)
#6、扣非净利润近四季盈余成长率介于8%至50%
def profit_value(i,date,l2):
df=get_fundamentals(query(
indicator.code,
indicator.inc_net_profit_to_shareholders_year_on_year
).filter(
indicator.code.in_(l2),
indicator.inc_net_profit_to_shareholders_year_on_year>6,
indicator.inc_revenue_year_on_year<50
),statDate=date)
df.rename(columns={'inc_net_profit_to_shareholders_year_on_year':'inc_net_profit_to_shareholders_year_on_year'+str(i)},inplace=True)
return df
df61=profit_value(1,t1,temp_list)
df62=profit_value(2,t2,temp_list)
df63=profit_value(3,t3,temp_list)
df64=profit_value(4,t4,temp_list)
df6=df61.merge(df62,on='code')
df6=df5.merge(df63,on='code')
df6=df5.merge(df64,on='code')
l6=set(df6.code.values)
#6.近四季EPS在0.04至1
# def eps_value(i,date,l2):
# df=get_fundamentals(query(
# indicator.code,
# indicator.eps
# ).filter(
# indicator.code.in_(l2),
# indicator.eps>0.04,
# indicator.eps<1
# ),statDate=date)
# df.rename(columns={'eps':'epsr'+str(i)},inplace=True)
# return df
# df61=eps_value(1,t1,temp_list)
# df62=eps_value(2,t2,temp_list)
# df63=eps_value(3,t3,temp_list)
# df64=eps_value(4,t4,temp_list)
# df6=df61.merge(df62,on='code')
# df6=df6.merge(df63,on='code')
# df6=df6.merge(df64,on='code')
# l6=set(df6.code.values)
return list(l1 & l2 &l3 & l4 & l5 & l6)
#去极值(分位数法)
def winsorize(se):
q = se.quantile([0.025, 0.975])
if isinstance(q, pd.Series) and len(q) == 2:
se[se < q.iloc[0]] = q.iloc[0]
se[se > q.iloc[1]] = q.iloc[1]
return se
#获取报告季报时间,由于实际财报公布时间和当前日期不一致,为了防止未来函数,我们对取报告的时间进行规定。
def statDate_value(context):
dt=context.current_dt
if 9>dt.month>=5:
statDate1=str(dt.year)+'q1'
statDate2=str(dt.year-1)+'q4'
statDate3=str(dt.year-1)+'q3'
statDate4=str(dt.year-1)+'q2'
elif 11>dt.month>=9:
statDate1=str(dt.year)+'q2'
statDate2=str(dt.year)+'q1'
statDate3=str(dt.year-1)+'q4'
statDate4=str(dt.year-1)+'q3'
elif dt.month>=11:
statDate1=str(dt.year)+'q3'
statDate2=str(dt.year)+'q2'
statDate3=str(dt.year)+'q1'
statDate4=str(dt.year-1)+'q4'
else:
statDate1=str(dt.year-1)+'q4'
statDate2=str(dt.year-1)+'q3'
statDate3=str(dt.year-1)+'q2'
statDate4=str(dt.year-1)+'q1'
return statDate1,statDate2,statDate3,statDate4
# 行业过滤
def industry_filter(context, security_list, industry_list):
if len(industry_list) == 0:
# 返回股票列表
return security_list
else:
securities = []
for s in industry_list:
temp_securities = get_industry_stocks(s)
securities += temp_securities
security_list = [stock for stock in security_list if stock in securities]
# 返回股票列表
return security_list
# 剔除停牌、st股
def filter_st_paused(context):
temp_list = list(get_all_securities(types=['stock']).index)
#剔除停牌股
all_data = get_current_data()
temp_list = [stock for stock in temp_list if not all_data[stock].paused]
#剔除st股
temp_list = [stock for stock in temp_list if not all_data[stock].is_st]
return temp_list
# 资金分配
def Allocation_of_funds(context,buy_lists,max_hold_stocknum,max_buy_value):
current_position=len(context.portfolio.positions.keys())
if max_hold_stocknum-current_position>0:
num=min(max_hold_stocknum-current_position,len(buy_lists))
value=context.portfolio.available_cash/num
if not max_buy_value:
max_buy_value=context.portfolio.available_cash
cash=min(value,max_buy_value)
return cash
# 风险再平衡
def Risk_rebalancing(context,buy_lists):
tolal_value=context.portfolio.total_value
num1=len(context.portfolio.positions.keys())
num2=len(buy_lists)
if num1+num2>0:
cash=tolal_value/(num1+num2)
else:
cash=tolal_value
return cash
注册时间: