克隆策略

1.获取数据

In [2]:
start_date = '2013-02-01' # 开始日期
end_date = '2017-05-07' # 结束日期
instruments = D.instruments()
# 获取市盈率、市净率、成交额数据
history_data = D.history_data(instruments, start_date=start_date,
               end_date= end_date, fields=[ 'pb_lf', 'pe_ttm','amount'])

2.整理换仓时买入股票列表

In [3]:
# 该函数的目的是通过history_data这个大的原始数据,获取每日满足价值投资股票列表
def seek_symbol(df):
    selected = df[(df['pb_lf'] < 1.5)
        & (df['pe_ttm'] < 15) 
        & (df['amount'] > 0) 
        & (df['pb_lf'] > 0)
        & (df['pe_ttm'] > 0 ) ]
                                    
    # 按pe_ttm和pb_lf 升序排列
    selected = selected.sort_values(['pe_ttm','pb_lf'])
    return list(selected.instrument)[:30] # 记得转化成list

daily_buy_stock = history_data.groupby('date').apply(seek_symbol)

3. 回测主体

In [9]:
def initialize(context):
    # 设置交易费用,买入是万三,卖出是千分之1.3,如果不足5元按5元算
    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)) 
    # 上面schedule_function函数的这句代码,其实可以写到一行,分两行是为了便于展示

def handle_data(context,data):
    pass

# 换仓
def rebalance(context, data):
    # 日期
    date = data.current_dt.strftime('%Y-%m-%d')
   
    # 买入股票列表
    stock_to_buy = daily_buy_stock.loc[date]
    # 目前持仓列表    
    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:
        if data.can_trade(context.symbol(stock)):
            context.order_target_percent(context.symbol(stock), 0)
    
    if len(stock_to_buy) == 0:
        return
    # 等权重
    weight = 1 / len(stock_to_buy)
    # 执行买入
    for  cp in stock_to_buy:
        if data.can_trade(context.symbol(cp)):
            context.order_target_percent(context.symbol(cp), weight)

4.回测接口

In [10]:
# 使用第四版的回测接口,需要传入多个策略参数
m=M.trade.v4( 
    instruments=instruments,
    start_date=start_date, 
    end_date=end_date,
    # 必须传入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',
)
  • 收益率130.15%
  • 年化收益率22.57%
  • 基准收益率25.89%
  • 阿尔法0.17
  • 贝塔0.91
  • 夏普比率0.79
  • 胜率0.44
  • 盈亏比2.74
  • 收益波动率26.51%
  • 信息比率0.08
  • 最大回撤35.35%
bigcharts-data-start/{"__type":"tabs","__id":"bigchart-b8350681f9ea4d11816b900301c33b13"}/bigcharts-data-end
In [15]:
results = m.raw_perf.read()
beta = results['beta'].tail(1)[0] # 系统自动计算的beta
print('beta:' ,beta)
risk_free = results['treasury_period_return'].tail(1)[0] # 无风险收益率
cum_algo_return = results['algorithm_period_return'].tail(1)[0] # 总收益
print('总收益:',cum_algo_return)
cum_benchmark_return = results['benchmark_period_return'].tail(1)[0] # 基准总收益
print('基准总收益:',cum_benchmark_return)
num_trading_days = results['trading_days'].tail(1)[0] # 天数
annualized_algo_return = (cum_algo_return + 1.0) ** (252.0 / num_trading_days) - 1.0
print('策略年化收益:', annualized_algo_return)
annualized_benchmark_return = (cum_benchmark_return + 1.0) **  (252.0 / num_trading_days) - 1.0
alpha_correct =annualized_algo_return - (risk_free + beta * (annualized_benchmark_return - risk_free) )  
print('alpha: ', alpha_correct)
beta: 0.9075410140424006
总收益: 1.3015125920920854
基准总收益: 0.25891014126269973
策略年化收益: 0.22574070902944965
alpha:  0.17325443151104797

日收益率计算

In [16]:
result = m.raw_perf.read_df() # 回测结果
algo_daily_returns =  (result.algorithm_period_return+1).pct_change().dropna() # 策略每日收益率
benchmark_daily_returns = (result.benchmark_period_return+1).pct_change().dropna() # 基准每日收益率
print('策略收益率与基准收益率的相关系数:',algo_daily_returns.corr(benchmark_daily_returns))
策略收益率与基准收益率的相关系数: 0.895005472485788

sharp计算

In [33]:
algo_volatility =  result['algo_volatility'].tail(1).values[0]
sharp_ratio = ((cum_algo_return +1)** (240.0 / num_trading_days) -1 - risk_free) / algo_volatility  
print('sharp_ratio: ', sharp_ratio)
sharp_ratio:  0.8069442432428029

信息比率计算

In [36]:
algo_daily_return =  (result.algorithm_period_return+1).pct_change().dropna() # 策略每日收益率
benchmark_daily_return = (result.benchmark_period_return+1).pct_change().dropna() # 基准每日收益率
diff = algo_daily_return - benchmark_daily_return
std = np.std(diff)
annualized_std = std*(240**0.5)
ir = (annualized_algo_return - annualized_benchmark_return) / annualized_std
print('信息比率' ,ir)
信息比率 1.4265404492786413