从零实现”搭积木式实现策略“的回测系统 part III

之前的文章,介绍了回测系统的总体架构,以及DataFeed的核心代码。这篇文章重点讲目前这个回测框架最有意思的地方——策略模块化。就是我们不需要写大量的模板代码去实现一个策略,基本重点的模块都拆分成了小块,像搭积木一下,重新组合起来即可。

aogos.py

Algo是一个基类,只实现了一个抽象函数call

'''
算法基类,是一个可以复用的功能单元。
代码很简单,只一个name属性,以及一个需要子类实现的功能__call__。
'''
class Algo(object):
    def __init__(self, name=None):
        self._name = name

    @property
    def name(self):
        if self._name is None:
            self._name = self.__class__.__name__
        return self._name

    def __call__(self, context):
        raise NotImplementedError("%s 没有实现!" % self.name)

我们看一个最简单的Alog实现PrintBar——就是把当期context里的一些变量打印出来。模块化算法,最核心的就是把各块之间的传递,以及与回测框架环境的交互放在context里,context是一个dict类型。回测每走一步,会更新context里相应的数据,比如最新的idx,now等。

class PrintBar(Algo):
    def __call__(self, context):
        logger.info('当前索引:{},当前日期:{}'.format(context['idx'],context['now']))
        return True

再个一个选择集合里全部股票的Algo——SelectAll。 其实就是把context['LONG'] = context['universe'],就是生成LONG指令,目标是全体标的。

class SelectAll(Algo):
    def __init__(self):
        super(SelectAll, self).__init__()

    def __call__(self,context,direction='LONG'):
        context[direction] = context['universe']
        return True

SelectWhere是按信号选股,这个计算会复杂一些。 初始化传入的参数是signal,signal是dataframe类型。df:['AAPL':[1,0,-1],'AMZN':[…]],每列对应相应证券的交易信号,1为买入,0为不变,-1为平仓。根据信号计算结果,保存在context['LONG'],context['FLAT']里。

class SelectWhere(Algo):
    def __init__(self, signal):
        self.signal = signal #df:['AAPL':[1,0,-1],'AMZN':[...]]

    def __call__(self, context):

        #这里得到某一天的信号,是一个Series, index = ['AAPL'...]
        day_signal = self.signal.loc[context['now']]

        #LONG or FLAT
        day_signal_long = day_signal[day_signal==1]
        day_signal_flat = day_signal[day_signal == -1]

        #按方向过滤完信号后,取索引就是证券代码列表
        selected = day_signal.index
        context['LONG'] = list(day_signal_long.index)
        context['FLAT'] = list(day_signal_flat.index)

        return True

SelectByExpr在SelectWhere的基础上更进一步,它根据表达式生成交易信号。表达式示例:

表示5日与10日均线金叉时买入,死叉时卖出。

long_expr = 'cross_up(ma(close,5),ma(close,10))'
flat_expr = 'cross_down(ma(close,5),ma(close,10))'

算法会根据表达式,计算出signal,然后在算法内部调用SelectWhere生成交易指令。

class SelectByExpr(Algo):
    def __init__(self,long_expr,flat_expr):
        self.long_expr = long_expr
        self.flat_expr = flat_expr
        self.run_once = RunOnce()

    def __call__(self, context):
        if self.run_once(context) is True: #运行过了,会访问False表示不用继续,本算法返回True,continue
            codes = context['universe']
            all_close = context['all_close']
            all_data = context['all_data']
            # price_keys= ['open','high','low','close']
            sig = pd.DataFrame(index=all_close.index, columns=all_close.columns)
            for symbol in codes:
                df = all_data[symbol]
                close = df['Close']
                high = df['High']
                low = df['Low']
                open = df['Open']

                long_sig = eval(self.long_expr)  # eval('cross_up(ma(close,5),ma(close,10')
                flat_sig = eval(self.flat_expr)  # eval('cross_down(ma(close,5),ma(close,10)')
                sig[symbol] = long_sig + flat_sig
                context['sig'] = sig
            #print(sig[sig>0])
            return True

        SelectWhere(signal=context['sig'])(context)
        return True

WeighEqually是等权分配cash的算法,结果变量保存在context['weights']里。

class WeighEqually(Algo):
    def __init__(self):
        super(WeighEqually, self).__init__()

    def __call__(self, context):
        #FLAT不用权重,这个列表里的都平仓,rebalance会自动处理
        if 'LONG' in context.keys():
            selected = context['LONG']
            n = len(selected)

            if n == 0:
                context['weights'] = {}
            else:
                w = 1.0 / n
                context['weights'] = {x: w for x in selected}

        return True

有了上面列举的模块,我们就可以轻松实现经典的回测交易算法。

 buy_and_hold = Strategy([
        RunOnce(),
        PrintBar(),
        SelectAll(),
        WeighEqually(),
    ], name='买入并持有-基准策略')

    long_expr = 'cross_up(ma(close,5),ma(close,10))'
    flat_expr = 'cross_down(ma(close,5),ma(close,10))'
    ma_cross = Strategy([
        SelectByExpr(long_expr=long_expr, flat_expr=flat_expr),
        WeighEqually(),
    ], name='均线交叉策略')

明天我们继续讲解投资组合管理类——Portfolio。

代码在github上开源ailabx

image

转载于:https://my.oschina.net/u/3289203/blog/2251897

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值