求助闭包调用失败,请大家帮助

策略分享
标签: #<Tag:0x00007f61e7108b50>

(xiaoshitou) #1
克隆策略
In [15]:
# 获取股票代码
instruments = D.instruments()
# 确定起始时间
start_date = '2017-01-05' 
# 确定结束时间
end_date = '2017-06-01' 

def initialize():
    market_cap_data = D.history_data(instruments,start_date,end_date,
                  fields=['market_cap','amount','suspended'])

    # 根据是否停牌的字段确定每日选出来的股票
    daily_buy_stock = market_cap_data.groupby('date').apply(lambda df:df[(df['amount'] > 0) # 需要有成交量
                                                    & (df['suspended'] == False)  # 是否停牌
                                                    ].sort_values('market_cap')[:30]) # 前三十只

    # 回测参数设置,initialize函数只运行一次
    def initialize1(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)) 
        # 传入 整理好的调仓股票数据
        context.daily_buy_stock = daily_buy_stock
    
    return initialize1

# handle_data函数会每天运行一次
def handle_data(context,data):
    pass

# 换仓函数
def rebalance(context, data):
    # 当前的日期
    date = data.current_dt.strftime('%Y-%m-%d')
    # 根据日期获取调仓需要买入的股票的列表
    stock_to_buy = context.daily_buy_stock.ix[date].instrument # 一定要转化为列表
   
    # 通过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方法将其转化成平台可以接受的形式

        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.backtest.v5( 
    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=float("1.0e7") ,
    # 比较基准:沪深300
    benchmark='000300.INDX',
)
[2017-06-29 17:03:32.268619] INFO: bigquant: backtest.v5 start ..
[2017-06-29 17:03:52.997673] INFO: Performance: Simulated 96 trading days out of 96.
[2017-06-29 17:03:52.999331] INFO: Performance: first open: 2017-01-05 14:30:00+00:00
[2017-06-29 17:03:53.000392] INFO: Performance: last close: 2017-06-01 19:00:00+00:00
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-7d1b2cde65a8> in <module>()
     86     capital_base=float("1.0e7") ,
     87     # 比较基准:沪深300
---> 88     benchmark='000300.INDX',
     89 )

AttributeError: Can't pickle local object 'initialize.<locals>.initialize1'

(小Q) #2
  1. 错误是因为backtest.v5不支持闭包。我们已经升级到 trader.v1,这个没有这个限制
  2. 因为 trader 模块做了缓存支持,根据输入参数生成缓存key,你原来的代码有一部分没有进入缓存,可能导致代码修改了,但还是返回的缓存结果。建议的使用,见如下修改:
克隆策略
In [5]:
# 获取股票代码
instruments = D.instruments()
# 确定起始时间
start_date = '2017-01-05' 
# 确定结束时间
end_date = '2017-06-01' 

def prepare(context):
    market_cap_data = D.history_data(instruments,start_date,end_date,
                  fields=['market_cap','amount','suspended'])

    # 根据是否停牌的字段确定每日选出来的股票
    daily_buy_stock = market_cap_data.groupby('date').apply(lambda df:df[(df['amount'] > 0) # 需要有成交量
                                                    & (df['suspended'] == False)  # 是否停牌
                                                    ].sort_values('market_cap')[:30]) # 前三十只
    context.daily_buy_stock = daily_buy_stock


# 回测参数设置,initialize函数只运行一次
def initialize(context):
    # 手续费设置
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5)) 
    # 调仓规则(每月的第一天调仓)
    context.schedule_function(context.options['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 = context.daily_buy_stock.ix[date].instrument # 一定要转化为列表
   
    # 通过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方法将其转化成平台可以接受的形式

        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.v1( 
    instruments=instruments,
    start_date=start_date, 
    end_date=end_date,
    prepare=prepare,
    # 必须传入initialize,只在第一天运行
    initialize=initialize,
    #  必须传入handle_data,每个交易日都会运行
    handle_data=handle_data,
    # 买入以开盘价成交
    order_price_field_buy='open',
    # 卖出也以开盘价成交
    order_price_field_sell='open',
    # 策略本金
    capital_base=float("1.0e7") ,
    # 比较基准:沪深300
    benchmark='000300.INDX',
    options={'rebalance': rebalance}
)
[2017-06-29 17:18:17.102208] INFO: bigquant: backtest.v6 start ..
/var/app/enabled/pandas/tseries/index.py:817: PerformanceWarning: Non-vectorized DateOffset being applied to Series or DatetimeIndex
  "or DatetimeIndex", PerformanceWarning)
/var/app/enabled/empyrical/stats.py:534: RuntimeWarning: invalid value encountered in double_scalars
  sortino = mu / dsr
/var/app/enabled/empyrical/stats.py:534: RuntimeWarning: divide by zero encountered in double_scalars
  sortino = mu / dsr
[2017-06-29 17:18:45.115167] INFO: Performance: Simulated 96 trading days out of 96.
[2017-06-29 17:18:45.118817] INFO: Performance: first open: 2017-01-05 14:30:00+00:00
[2017-06-29 17:18:45.121271] INFO: Performance: last close: 2017-06-01 19:00:00+00:00
/var/app/enabled/pandas/core/generic.py:1138: PerformanceWarning: 
your performance may suffer as PyTables will pickle object types that it cannot
map directly to c-types [inferred_type->mixed,key->block4_values] [items->['LOG', 'POS_FAC', 'TRA_FAC', 'orders', 'period_label', 'positions', 'transactions']]

  return pytables.to_hdf(path_or_buf, key, self, **kwargs)
/var/app/enabled/pandas/core/indexing.py:141: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self._setitem_with_indexer(indexer, value)
  • 收益率-11.12%
  • 年化收益率-26.62%
  • 基准收益率3.84%
  • 阿尔法-0.33
  • 贝塔0.41
  • 夏普比率-3.23
  • 收益波动率9.58%
  • 信息比率-3.64
  • 最大回撤13.12%
[2017-06-29 17:18:45.737487] INFO: bigquant: backtest.v6 end [28.635254s].