V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
raquant
V2EX  ›  Python

主流 Python 量化回测平台,回测速度客观评测

  •  
  •   raquant · 2017-03-11 01:41:03 +08:00 · 4501 次点击
    这是一个创建于 2619 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 python 的量化平台目前很多啊,现在就三家**ricequant,joinquant,raquant**做一下简单评测

    [分钟级别] 回测速度比较

    同样一段双均线( SMA 长短线)策略,虽然这个双均线,没必要每分钟都刷,毕竟作为超短期指标,双均线意义不大。

    所以这也是让策略开发者困扰的一点,有时候有些策略的逻辑在“被选择的频度”下面毫无意义。镭矿则没有这一困扰,镭矿策略代码里面规定是什么频度执行就是什么频度执行。

    言归正传,现在我们来用这个经典策略对比一下各个平台,分钟频度下一年半(2015-05-01 到 2017-01-01 )的回测速度。每分钟刷日线数据肯定是更没有意义,所以小编将 ricequant 和 joinquant 的获取历史数据的参数手动改为获取分钟数据

     策略 A:
     回测时间: 2015-05-01 至 2017-01-01
     使用分钟数据,短均线上穿长均线,则买入,反之,则清仓卖出
    

    对篇幅很恐惧的同学请先看一下这个表格:

    策略 A 的回测时间对比表格

    • 镭矿 raquant 17 秒
    • ricequant 3 到 4 分钟
    • joinquant 表现好的时候 6 分钟

    下面是镭矿 raquant的代码,耗时 17 秒

    def init(context):
        context.s1="sha-600000"
        sma12_factor=SMAFactor(12,"close")
        sma30_factor=SMAFactor(30,"close")
        reg_factor("sma12",sma12_factor)
        reg_factor("sma30",sma30_factor)
    def every_minute(context,data):
        stock=context.s1
        ma12=factor_output("sma12",context.s1,"m1")["sma12"]
        ma30=factor_output("sma30",context.s1,"m1")["sma30"]
        if ma12>ma30:
            order(stock,1000)
        elif ma12 < ma30 and context.portfolio.positions[stock].amount > 0:
            order_target_value(stock,0)
    

    下面是 joinquant 的代码,耗时 6 分钟

    # 初始化函数,设定要操作的股票、基准等等
    def initialize(context):
        # 定义一个全局变量, 保存要操作的股票
        # 000001(股票:平安银行)
        g.security = '000001.XSHE'
        # 设定沪深 300 作为基准
        set_benchmark('000300.XSHG')
     
    # 每个单位时间(如果按天回测,则每天调用一次,如果按分钟,则每分钟调用一次)调用一次
    def handle_data(context, data):
        security = g.security
        close_data = attribute_history(security, 10, '1m', ['close'],df=False)
        ma5 = close_data['close'][-5:].mean()
        ma10 = close_data['close'].mean()
        cash = context.portfolio.cash
     
        if ma5 > ma10:
            order_value(security, cash)
            log.info("Buying %s" % (security))
     
        elif ma5 < ma10 and context.portfolio.positions[security].closeable_amount> 0:
            order_target(security, 0)
            log.info("Selling %s" % (security))
     
        # 绘制五日均线价格
        record(ma5=ma5)
        # 绘制十日均线价格
        record(ma10=ma10)
    

    下面是 ricequant 的代码,耗时 3 到 4 分钟,貌似比 joinquant 的快,也有可能是因为这个例程的买入条件比较多?但是明显 ricequant 的回测准备时间较长。

    import talib 
    # 在这个方法中编写任何的初始化逻辑。 context 对象将会在你的算法策略的任何方法之间做传递。
    def init(context):
        context.s1 = "000001.XSHE"
     
        # 设置这个策略当中会用到的参数,在策略中可以随时调用,这个策略使用长短均线,我们在这里设定长线和短线的区间,在调试寻找最佳区间的时候只需要在这里进行数值改动
        context.SHORTPERIOD = 20
        context.LONGPERIOD = 120
    # 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
    def handle_bar(context, bar_dict):
        prices = history(context.LONGPERIOD+1, '1m', 'close')[context.s1].values 
        short_avg = talib.SMA(prices, context.SHORTPERIOD)
        long_avg = talib.SMA(prices, context.LONGPERIOD) 
        plot("short avg", short_avg[-1])
        plot("long avg", long_avg[-1])
        cur_position = context.portfolio.positions[context.s1].quantity
        # 计算现在 portfolio 中的现金可以购买多少股票
        shares = context.portfolio.cash/bar_dict[context.s1].close 
        # 如果短均线从上往下跌破长均线,也就是在目前的 bar 短线平均值低于长线平均值,而上一个 bar 的短线平均值高于长线平均值
        if short_avg[-1] - long_avg[-1] < 0 and short_avg[-2] - long_avg[-2] > 0 and cur_position > 0:
            # 进行清仓
            order_target_value(context.s1, 0) 
        # 如果短均线从下往上突破长均线,为入场信号
        if short_avg[-1] - long_avg[-1] > 0 and short_avg[-2] - long_avg[-2] < 0:
            # 满仓入股
            order_shares(context.s1, shares)
    

    [日线级别] 回测速度比较

    日线级别上,策略 A 回测时间对比表格(镭矿需要手动修改函数名 every_minute 为 every_day) 回测时间 镭矿 秒回(少于一秒) ricequant 5 秒后秒回。(感觉任何回测都需要准备 5 秒钟) joinquant 秒回(少于一秒)

    对比不明显,我们来升级一下策略逻辑。为了简便,我们仍然修改策略 A ,形成策略 B 。

    事实上这不是一个真正的策略,不产生任何交易。只不过我们这里为了尽快知道各个平台的回测速度罢了。

    策略 B
    每日轮询 50 只股票的 SMA 长短线, record 出符合 SMA 短线上穿长线的股票个数
    

    镭矿 raquant的代码

    def init(context):
        context.stocks=find_by_group('sz50')
        sma12_factor=SMAFactor(12,"close")
        sma30_factor=SMAFactor(30,"close")
        reg_factor("sma12",sma12_factor)
        reg_factor("sma30",sma30_factor)
    def every_day(context,data):
        cnt=0
        for stock in context.stocks:
            ma12=factor_output("sma12",stock)["sma12"]
            ma30=factor_output("sma30",stock)["sma30"]
            if ma12>ma30:
                cnt=cnt+1
        record("cnt",cnt)
    

    ricequant 代码

    import talib
    # 在这个方法中编写任何的初始化逻辑。 context 对象将会在你的算法策略的任何方法之间做传递。
    def init(context):
        context.stocks =concept('央企 50')
        context.SHORTPERIOD = 20
        context.LONGPERIOD = 120
    def handle_bar(context, bar_dict):    
        cnt=0
        for stock in context.stocks:
            prices = history(context.LONGPERIOD+1, '1d', 'close')[stock].values
            short_avg = talib.SMA(prices, context.SHORTPERIOD)
            long_avg = talib.SMA(prices, context.LONGPERIOD) 
            if short_avg[-1] - long_avg[-1] < 0 and short_avg[-2] - long_avg[-2] > 0:
                cnt=cnt+1
        plot("cnt",cnt)
    

    joinquant 的代码:

    # 初始化函数,设定要操作的股票、基准等等
    def initialize(context):
        # 定义一个全局变量, 保存要操作的股票
        # 000001(股票:平安银行)
        g.stocks =get_concept_stocks('GN177')
        set_benchmark('000300.XSHG')
     
    # 每个单位时间(如果按天回测,则每天调用一次,如果按分钟,则每分钟调用一次)调用一次
    def handle_data(context, data):
        cnt=0
        # 获取股票的收盘价
        for stock in g.stocks:
            close_data = attribute_history(stock, 10, '1d', ['close'],df=False)
        # 取得过去五天的平均价格
            ma5 = close_data['close'][-5:].mean()
        # 取得过去 10 天的平均价格
            ma10 = close_data['close'].mean()
        # 取得当前的现金
            if ma5>ma10:
                cnt=cnt+1
        record(cnt=cnt)
    

    你们一定很好奇这次得对比结果,所以小编故意卖关子放到了最后

    日线级别上,策略 B 回测时间对比表格

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1355 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 17:50 · PVG 01:50 · LAX 10:50 · JFK 13:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.