主流 Python 量化回测平台,回测速度客观评测 - V2EX
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 5048 次点击
    这是一个创建于 3190 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 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     Solana     5242 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 05:59 PVG 13:59 LAX 21:59 JFK 00:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86