def prepare(context):
# 确定起始时间
start_date = context.start_date
# 确定结束时间
end_date = context.end_date
instruments = context.instruments
fields = ['fs_gross_profit_margin_0', 'fs_roe_0', 'fs_free_cash_flow_0', 'fs_net_profit_0']
raw_data = D.features(instruments, start_date, end_date, fields)
raw_data['cash_flow/profit'] = raw_data['fs_free_cash_flow_0'] / raw_data['fs_net_profit_0']
context.daily_buy_stock = pd.DataFrame(raw_data.groupby('date').apply(seek_stock))
def seek_stock(df):
ahead_f1 = set(df.sort_values('fs_roe_0',ascending=False)['instrument'][:500])
ahead_f2 = set(df.sort_values('fs_gross_profit_margin_0',ascending=False)['instrument'][:500])
ahead_f3 = set(df.sort_values('cash_flow/profit',ascending=False)['instrument'][:500])
return list(ahead_f1 & ahead_f2 & ahead_f3)
# 回测参数设置,initialize函数只运行一次
def initialize(context):
# 手续费设置
context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
# 调仓规则(每月的第一天调仓)
context.schedule_function(rebalance, date_rule=date_rules.month_start(days_offset=0))
# handle_data函数会每天运行一次
def handle_data(context,data):
pass
# 换仓函数
def rebalance(context, data):
# 当前的日期
date = data.current_dt.strftime('%Y-%m-%d')
# 根据日期获取调仓需要买入的股票的列表
stock_to_buy = list(context.daily_buy_stock.ix[date][0])
# 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表
stock_hold_now = [equity.symbol for equity in context.portfolio.positions]
# 继续持有的股票:调仓时,如果买入的股票已经存在于目前的持仓里,那么应继续持有
no_need_to_sell = [i for i in stock_hold_now if i in stock_to_buy]
# 需要卖出的股票
stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]
# 卖出
for stock in stock_to_sell:
# 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态
# 如果返回真值,则可以正常下单,否则会出错
# 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式
if data.can_trade(context.symbol(stock)):
# order_target_percent是平台的一个下单接口,表明下单使得该股票的权重为0,
# 即卖出全部股票,可参考回测文档
context.order_target_percent(context.symbol(stock), 0)
# 如果当天没有买入的股票,就返回
if len(stock_to_buy) == 0:
return
# 等权重买入
weight = 1 / len(stock_to_buy)
# 买入
for stock in stock_to_buy:
if data.can_trade(context.symbol(stock)):
# 下单使得某只股票的持仓权重达到weight,因为
# weight大于0,因此是等权重买入
context.order_target_percent(context.symbol(stock), weight)
# 策略运行调用函数
m=M.trade.v2(
instruments=D.instruments(market='CN_STOCK_A'),
start_date='2013-01-01',
end_date='2017-05-01',
prepare=prepare, # 在实盘或模拟交易,每天会更新数据,因此必须传入数据准备函数
# 必须传入initialize,只在第一天运行
initialize=initialize,
# 必须传入handle_data,每个交易日都会运行
handle_data=handle_data,
# 买入以开盘价成交
order_price_field_buy='open',
# 卖出也以开盘价成交
order_price_field_sell='open',
# 策略本金
capital_base=1000000,
# 比较基准:沪深300
benchmark='000300.INDX',
m_deps='quantamental'
)