多个量化平台的回测速度评测 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
raquant
V2EX    互联网

多个量化平台的回测速度评测

  •  
  •   raquant 2017-03-21 15:17:13 +08:00 3878 次点击
    这是一个创建于 3203 天前的主题,其中的信息可能已经有所发展或是发生改变。

    多个量化平台的回测速度评测

    每个有梦想的人都自己私下里憋着一股劲,默默地努力着,希望它日能冷不妨让大家大吃一惊。本人也不例外,经常混迹于各个量化平台,有灵感了就验证一下,看自己是否能成为下一颗冉冉升起的新星。正如大家所知道的那样,这种事情并没有发生。

    前两天看一篇文章,大意是说大多数人都已彻底丧失了构建投资(机)战略眼光的能力,所以注定平庸。我认同。在任何方面,只要有人说大部分人注定平庸,我都会认同。基于这种认知,所以上面说的新星升起这种事大概将来也不会发生吧。

    你们不同。毕竟能看到这篇文章并点赞的是极少数人,你们既然不属于大多数人,想来比我更有机会冉冉。苟富贵,莫想忘。

    然而作会一个注定平庸的人,我一直坚持用战术上的勤奋来掩盖自己写不出好策略这一点。比如,我在超过 6 个量化平台上写了相同的策略(这是重复劳动?),目的就是想看一下哪个平台跑的更欢快。大家都在达成小目标的路上忙碌着,我想这些粗活我可以适当的干点儿。下面我会贴出其中 4 个平台上的双均线策略代码,大家有兴趣可以自己去它们的主子平台那试一把。在此被点名的量化平台有 JDquant , Joinquant, Raquant, Ricequant ,后序可能会更新更多。

    定场诗唠完,上点实在的。我用来测试的策略是这样的,在大量的股票集上,实现均线策略。确切的说是在 2400 支股票内,搜寻短线超长线的股票买入;搜录长线跌出短线的策略卖出。为什么是 2400 支呢,因为各家的 API 不同,数据也不同,如果直接选可交易的所有股票的全集,一些平台没有相应的接口,即使有相应的接口,大家对停牌,新股等的处理不同,造成最终选中的股票集大小也不同。所以大家都取个交集,定 2400 支了。

    1. 京东量化

    首先出场的是在量化之路上飞奔的 JDquant ,京东量化是强哥在金融方面对抗马爸爸的一颗棋子(为什么辈份不一样?)。本人是京东金融的一名忠实用户,最近在抱怨小白理财怎么没什么可买的了,到处限额。是的,被你发现了,一个整天谈着量化投资,程式化交易的人,居然把钱放到理财里去了,而且是“小白”理财,太丢人了!

    但难道你认为整天在朋友圈里发商业模式、 IPO 、 ABCDEFG 轮融资的人个个都是头上有角的独角兽,或者背后长着翅膀的天使?

    总之,京东金融的进展非常快,京东的量化平台也是。从一开始粗糙,到后来的达到主流水平,到现在的有局部优势,也是良心工厂了。但是,它也是上面我说的没有找到取股票全集方法的平台之一。这并不是什么大问题,问题是我要保证 2400 啊,这样才好对比。所以,我用财务选股的方法把这个问题搞定了,我选资产大于 0 的,肯定海量了吧。

    def choose_stock_finance(): dataframe = get_fundamentals( query( fundamentals.equity_valuation_indicator.market_cap_2 ).filter( fundamentals.equity_valuation_indicator.market_cap_2 > 0 ).limit( 2400 )) return dataframe.columns.values 

    你如果读下去,会发现 Ricequant 我也是如法炮制的。

    下面上完整代码:

    import talib import numpy as np import math import pandas import time import datetime from functools import reduce #init 方法是您的初始化逻辑, context 对象可以在任何函数之间传递 def init(context): #滑点默认值为 2 ‰ context.set_slippage(0.002) #交易费默认值为 0.25 ‰ context.set_commission(0.00025) context.codes = choose_stock_finance() logger.info("Total codes %d" %len(context.codes)) context.SHORTPERIOD = 5 context.LOnGPERIOD= 20 #每天开盘前进行选股 def before_trade(context): pass #日或分钟或实时数据更新,将会调用这个函数 def handle_data(context,data_dict): for stock in context.codes: try : # 因为策略需要用到均线,所以需要读取历史数据 prices = get_history(context.LONGPERIOD+1,'1d','close')[stock].values # 使用 talib 计算长短两根均线,均线以 array 的格式表达 short_avg = talib.SMA(prices, context.SHORTPERIOD) long_avg = talib.SMA(prices, context.LONGPERIOD) logger.info("short: %f, long: %f"% (short_avg[-1],long_avg[-1])) if stock=='601318.SH' : draw("short avg", short_avg[-1]) draw("long avg", long_avg[-1]) # 计算现在 portfolio 中股票的仓位 cur_position = context.portfolio.positions[stock].quantity # 计算现在 portfolio 中的现金可以购买多少股票 shares = context.portfolio.cash/data_dict[stock].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(stock, 0) # 如果短均线从下往上突破长均线,为入场信号 if short_avg[-1] - long_avg[-1] > 0 and short_avg[-2] - long_avg[-2] < 0: order_percent(stock, 0.05) except : continue #选股函数 def choose_stock_finance(): dataframe = get_fundamentals( query( fundamentals.equity_valuation_indicator.market_cap_2 ).filter( fundamentals.equity_valuation_indicator.market_cap_2 > 0 ).limit( 2400 )) return dataframe.columns.values 

    当我按下编译按钮之后,这会玩家跑出了 1 ‘ 15 ’‘的成绩,并向我展示了我的策略的回测情况:

    你不要小看我,我是能用最简单的策略写出年化 37.55%收益的男人!巴菲特才多少?而且巴菲特有教过你怎么赚钱的吗,我的策略代码你可以全部拿走,请忽略知识产权问题,分文不取!如果因为这个策略实盘导致破产吃不上饭,请联系我,我会帮你转接社会救济服务站。

    2. Joinquant 聚宽

    国内起步比较早的一家量化平台,我早期经常混迹其中。号称用户超多,但其它几个平台的数据我也看不到,所以也不知道是不是“超”多。首页上有一个醒目的位置,向你展示他们感觉比较得意的策略,运行个一两年,收益过百分之一千也很常见,而且相当数量的代码你可以看到。你可以不知不觉的拿走实盘,你要是这样出了问题,显然不应该联系我。但现面这个策略可以:

    import talib # 初始化函数,设定要操作的股票、基准等等 def initialize(context): context.SHORTPERIOD = 5 context.LOnGPERIOD= 20 # 定义一个全局变量, 保存要操作的股票 # 000001(股票:平安银行) g.securities = list(get_all_securities(['stock']).index)[0:2400] log.info("total: %d"% len(g.securities)); # 设定沪深 300 作为基准 set_benchmark('000300.XSHG') # 每个单位时间(如果按天回测,则每天调用一次,如果按分钟,则每分钟调用一次)调用一次 def handle_data(context, data): for stock in g.securities : try : # 因为策略需要用到均线,所以需要读取历史数据 prices = attribute_history(stock, 21, '1d', ['close'],df=False)['close'] # 使用 talib 计算长短两根均线,均线以 array 的格式表达 short_avg = talib.SMA(prices, context.SHORTPERIOD) long_avg = talib.SMA(prices, context.LONGPERIOD) #logger.info("short: %f, long: %f"% (short_avg[-1],long_avg[-1])) if stock=='601318.XSHG' : record(short_avg=short_avg[-1],long_avg=long_avg[-1]) # 计算现在 portfolio 中股票的仓位 cur_position = context.portfolio.positions[stock].total_amount # 如果短均线从上往下跌破长均线,也就是在目前的 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(stock, 0) # 如果短均线从下往上突破长均线,为入场信号 if short_avg[-1] - long_avg[-1] > 0 and short_avg[-2] - long_avg[-2] < 0: order_value(stock, context.portfolio.total_value*0.05) except : continue 

    这会玩家跑出了 3 ’ 08 ‘’的成绩,并向我们展示了回测结果:

    正如你所期待的那样,又双 赚 钱 啦!赚钱的策略是如此容易写,赔钱的策略将成会稀缺资源。

    3. Raquant 镭矿

    这是一家新兴的量化平台,用户数量暂时较少,至于我为什么要拿它来测评,你看完下面就知道了。

    他们也支持 Java ,而且与京东和大米 Java 版本的风格完全不同,作为一个 Java 程序员,首先我是难以抗拒的,但这并不是我决定把它提上测试表的原因。他们的口号是”镭厉风行,快速验证你的策略“,我只在强哥的地盘上看到有人敢说快,其它都是说数据怎么好,用户怎么多。我因此被镭到了。先看代码吧:

    public class SpeedTest extends Strategy{ Factor fSmaShort = new SMAFactor(5); Factor fSmaLOng= new SMAFactor(20); @Override public void init(BackTestContext context) throws Exception { StockList list = new StockList().addAll(); context.universe.addAll(list.subList(0,2400)); log.info("Total: "+context.universe.size()); } @Override public void handleData(BackTestContext context, BarData piece) throws Exception { for(String stock:context.universe) { double smaShort1 = fSmaShort.get(stock, -1); double smaLong1 = fSmaLong.get(stock, -1); double smaShort2 = fSmaShort.get(stock, -2); double smaLong2 = fSmaLong.get(stock, -2); if(stock.equals("sha-601318")) { record("sma long", smaLong1); record("sma short", smaShort1); } for(Position pos:context.portfolio.getAllPositions()) { if(smaShort1<smaLong1 && smaShort2>smaLong2) orderPercent(pos.getSecurity(), 0); } if(smaShort1>smaLong1 && smaShort2<smaLong2) { orderPercent(stock, 5); } } } } 

    什么情况,不是说 Java 模拟个脱裤子放屁的程序都要 10000 行代码,建 100 个对象么,怎么代码反而少了这么多?

    原来他们把技术指标封装了,看他们的 API ,应该是把所有的 TA 指标都做进去了,统一新建一个对象,然后 get()就可以了。为什么别的平台不这么做呢,写 SMA 指标的时候还好,写什么 ATR,KSI 以及需要更多输入变量的指标的时候,有没有想过我的感受?我只是想用一下啊,又是取数,又是计算又是定位的,半个屏没有了。

    建议各平台的大大们都这样封装一下吧,有余力地顺遍把 MQ4 的海量指标翻译过来啊,好让我们这些小白直接使用啊。大家就想用个指标还得搞清各种输入输出,人生太短暂。

    看一下结果,这位选手跑出了 10 ”,额,节省一个标点,是的,是 10 秒的成绩。由于它不太耗时,我又多点了几下,它是有机率可以跑进 10 秒的。

    不出所料,又挣到钱了,我不悲不喜,已经麻木。

    到这里,我只想问一下雷矿的团队里是否有印度来的员工,跑个策略都咖喱味十足,跟开了挂似的。为什么要跑进 10 秒,你们的服务器强哥的好么?

    我猜肯定不是,大概是在后台做了很多工作,把运算批量进行了或并行了之类的。但是,有功夫搞这个没功夫把网站完善一下吗,我进去有点摸不着北。

    4. Ricequant 米矿

    这也是一家专门做量化的网站,摊子铺的很大,在他们还没声称要放弃对 Java 平台支持之前,我在上面写过 Java 策略, Instrument 什么的,事件驱动什么的,我一度怀疑京东是打这获得的“灵感”。其实我骨子里还是个 Java 程序员,但在量化平台的用武之地越来越少了,也许哪天我因此敌视用 Python 做量化的平台,就再也不写了,所以请珍惜你眼下的阅读。

    大米开源了回测引擎,各种宣传各种好,有兴趣的可以去研究一下。你要在此基础上偷摸整出来了另一个回测平台,我可以帮你也测评一下。

    上代码:

    # 可以自己 import 我们平台支持的第三方 python 模块,比如 pandas 、 numpy 等。 # 可以自己 import 我们平台支持的第三方 python 模块,比如 pandas 、 numpy 等。 import talib import pandas as pd import numpy as np # 在这个方法中编写任何的初始化逻辑。 context 对象将会在你的算法策略的任何方法之间做传递。 def init(context): context.codes = choose_stock_finance()[0:2830] logger.info("Total codes %d" %len(context.codes)) # 设置这个策略当中会用到的参数,在策略中可以随时调用,这个策略使用长短均线,我们在这里设定长线和短线的区间,在调试寻找最佳区间的时候只需要在这里进行数值改动 context.SHORTPERIOD = 5 context.LOnGPERIOD= 20 # 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新 def handle_bar(context, bar_dict): # 开始编写你的主要的算法逻辑 # bar_dict[order_book_id] 可以拿到某个证券的 bar 信息 # context.portfolio 可以拿到现在的投资组合状态信息 # 使用 order_shares(id_or_ins, amount)方法进行落单 # TODO: 开始编写你的算法吧! for stock in context.codes: try : # 因为策略需要用到均线,所以需要读取历史数据 prices = history_bars(stock,context.LONGPERIOD+1, '1d', 'close') # 使用 talib 计算长短两根均线,均线以 array 的格式表达 short_avg = talib.SMA(prices, context.SHORTPERIOD) long_avg = talib.SMA(prices, context.LONGPERIOD) logger.info("short: %f, long: %f"% (short_avg[-1],long_avg[-1])) if stock=='601318.XSHG' : plot("short avg", short_avg[-1]) plot("long avg", long_avg[-1]) # 计算现在 portfolio 中股票的仓位 cur_position = context.portfolio.positions[stock].quantity # 计算现在 portfolio 中的现金可以购买多少股票 shares = context.portfolio.cash/bar_dict[stock].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(stock, 0) # 如果短均线从下往上突破长均线,为入场信号 if short_avg[-1] - long_avg[-1] > 0 and short_avg[-2] - long_avg[-2] < 0: # 满仓入股 order_percent(stock, 0.05) except : continue def choose_stock_finance(): fundamental_df = get_fundamentals( query( fundamentals.income_statement.revenue, fundamentals.eod_derivative_indicator.pe_ratio ).order_by( fundamentals.eod_derivative_indicator.pe_ratio.desc() ).limit( 2400 ) ) return fundamental_df.columns.values 

    经过,额,中间我等不下去吃饭去了,回来测完了,我只好重新测。经过 27 ‘ 48 “,额这个数字比较难以启齿,希望我是搞错了。你可以去亲测一下,我还有别的事要做。

    总结

    实际上没什么好总结的,我就是想简单测一下速度,如果你看完上面的内容,心里也都有数了。 Raquant 10 秒,京东 1 分 15 秒,聚宽 3 分 8 秒, Ricequant 基本接近 4 分钟了。收益么,都赚钱啦,不过我也不关心这个。

    友情提示,速度、回测收益什么的并不是最重要的。速度快就是验证你想法的速度快点,假如你有的只是烂策略,速度越快只是意味着在一定时间内,这个平台更能证明你拙劣的想法很多而已。如果因为回测收益高就觉得实盘收益也会高,这跟说五千万将近一个亿没什么区别。(且不说上述这些平台自身也并不是没有问题的)。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2573 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 06:29 PVG 14:29 LAX 22:29 JFK 01:29
    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