每一个季度进行调整。
选股思路为: 1.Stakeholder yield < 5%. Stakeholder yield = Cash from financing 12m / Market Cap 解释:因为一家稳定健康的公司,假设它自己的收入就足够运营了,那么cash from financing就会很小,这就是第一个限制的原因,某种程度上说明了公司的稳健程度。
2.投入资本回报率(ROIC) > 20% 解释:相对好理解,ROIC(资本回报率)是用来评估公司历史绩效的指标,资本回报率更高的公司,管理水平也更高。
3.经营活动现金流入(cash_from_operating_activities) > net income (net_profit) 解释:说明了公司在经营活动方面对于总收入的贡献很大且投资活动现金流量和筹资活动现金净流量可能还是负数(因为cash_from_operating_activities> net income ),经营活动中大量资金回笼,同时为了扩大业务,仅靠经营活动现金流量净额可能无法满足所需投资,需要筹集必要的外部资金作为补充。这一指标说明企业发展后劲比较强。
4.企业价值(ev)/自由现金流(fcff) < 15 (Value) 解释:说明股价是便宜的
5.过去六个月间股票和上证指数对比下的相对强度。 个股六个月回报率 / 大盘六个月回报率 解释:说明过去一段时间内股价表现优秀
@licco-li 补充解释一下~
第一点确实很有趣:因为一家稳定健康的公司,假设它自己的收入就足够运营了,那么cash from financing就会很小,这就是第一个限制的原因,某种程度上说明了公司的稳健程度。
第二点相对好理解,ROIC(资本回报率)是用来评估公司历史绩效的指标,资本回报率更高的公司,管理水平也更高。
第三点说明了公司在经营活动方面对于总收入的贡献很大且投资活动现金流量和筹资活动现金净流量可能还是负数(因为cash_from_operating_activities> net income ),经营活动中大量资金回笼,同时为了扩大业务,仅靠经营活动现金流量净额可能无法满足所需投资,需要筹集必要的外部资金作为补充。这一指标说明企业发展后劲比较强。
第四点说明股价是便宜的。
源代码
import pandas as pd
import numpy as np
def init(context):
context.s1 = '000001.XSHG'
context.max_num_stocks = 50
context.days = 0
context.quarter_days = 65
context.relative_strength_6m = {}
#跨季度
def quarter_passed(context):
return context.days % context.quarter_days == 0
def before_trading(context):
context.days += 1
if not quarter_passed(context):
return
do_screening(context)
update_universe(context.fundamental_df.columns.values)
def do_screening(context):
fundamental_df = get_fundamentals(
query(
fundamentals.cash_flow_statement.cash_flow_from_financing_activities,
fundamentals.eod_derivative_indicator.market_cap,
fundamentals.cash_flow_statement.cash_from_operating_activities,
fundamentals.financial_indicator.invested_capital,
fundamentals.financial_indicator.return_on_invested_capital,
fundamentals.balance_sheet.cash,
fundamentals.balance_sheet.cash_equivalent,
fundamentals.cash_flow_statement.cash_from_operating_activities,
fundamentals.income_statement.net_profit,
fundamentals.eod_derivative_indicator.market_cap,
fundamentals.financial_indicator.fcff
)
.filter(fundamentals.eod_derivative_indicator.market_cap > 0)
.filter(fundamentals.cash_flow_statement.cash_from_operating_activities > fundamentals.income_statement.net_profit)
.filter((fundamentals.eod_derivative_indicator.market_cap / fundamentals.financial_indicator.fcff) < 15)
.filter(fundamentals.financial_indicator.invested_capital > 0)
.filter((fundamentals.cash_flow_statement.cash_flow_from_financing_activities / fundamentals.eod_derivative_indicator.market_cap) < 0.05) #此项大幅提升了夏普比率
.filter(fundamentals.financial_indicator.return_on_invested_capital > 0.20)
#.filter(fundamentals.financial_indicator.fcff > 0) #OK but not good
#.filter(fundamentals.balance_sheet.cash + fundamentals.balance_sheet.cash_equivalent> 0)
#.filter(fundamentals.financial_indicator.invested_capital != fundamentals.balance_sheet.cash + fundamentals.balance_sheet.cash_equivalent)
.limit(context.max_num_stocks)
)
# Update context
context.stocks = [stock for stock in fundamental_df]
context.fundamental_df = fundamental_df
print(context.stocks)
def rebalance(context, bar_dict):
# Exit all positions before starting new ones
for stock in context.portfolio.positions:
if stock not in context.fundamental_df:
order_target_percent(stock, 0)
# 通过动量指标进一步过滤选股
# -0.6745 是一个参数,欢迎任意修改以fitting
#由于在某些回测区间缺少数据(例如股票当时尚未上市),导致prices.ix[19]的值为0,导致pct_change为inf,所以加入该指标小于9999的限制避免出现这种情况
context.stocks = [stock for stock in context.stocks
if stock in bar_dict and context.relative_strength_6m[stock] > -0.6745 and context.relative_strength_6m[stock] < 9999]
if len(context.stocks) == 0:
return
weight = 1.0/len(context.stocks)
for stock in context.stocks:
order_target_percent(stock, weight)
def handle_bar(context, bar_dict):
if not quarter_passed(context):
return
compute_relative_strength(context)
rebalance(context, bar_dict)
def compute_relative_strength(context):
prices = history (150, '1d', 'close')
# 过去六个月的价格变化率
pct_change = (prices.ix[149] - prices.ix[19]) / prices.ix[19]
print(prices.ix[19])
print(pct_change)
priceofbase = history (150, '1d', 'close')[context.s1]
pct_changeforbase = (priceofbase.ix[149] - priceofbase.ix[19]) / priceofbase.ix[19]
pct_change = pct_change - pct_changeforbase
if pct_changeforbase != 0:
pct_change = pct_change / abs(pct_changeforbase)
context.relative_strength_6m = pct_change