
每个有梦想的人都自己私下里憋着一股劲,默默地努力着,希望它日能冷不妨让大家大吃一惊。本人也不例外,经常混迹于各个量化平台,有灵感了就验证一下,看自己是否能成为下一颗冉冉升起的新星。正如大家所知道的那样,这种事情并没有发生。
前两天看一篇文章,大意是说大多数人都已彻底丧失了构建投资(机)战略眼光的能力,所以注定平庸。我认同。在任何方面,只要有人说大部分人注定平庸,我都会认同。基于这种认知,所以上面说的新星升起这种事大概将来也不会发生吧。
你们不同。毕竟能看到这篇文章并点赞的是极少数人,你们既然不属于大多数人,想来比我更有机会冉冉。苟富贵,莫想忘。
然而作会一个注定平庸的人,我一直坚持用战术上的勤奋来掩盖自己写不出好策略这一点。比如,我在超过 6 个量化平台上写了相同的策略(这是重复劳动?),目的就是想看一下哪个平台跑的更欢快。大家都在达成小目标的路上忙碌着,我想这些粗活我可以适当的干点儿。下面我会贴出其中 4 个平台上的双均线策略代码,大家有兴趣可以自己去它们的主子平台那试一把。在此被点名的量化平台有 JDquant , Joinquant, Raquant, Ricequant ,后序可能会更新更多。
定场诗唠完,上点实在的。我用来测试的策略是这样的,在大量的股票集上,实现均线策略。确切的说是在 2400 支股票内,搜寻短线超长线的股票买入;搜录长线跌出短线的策略卖出。为什么是 2400 支呢,因为各家的 API 不同,数据也不同,如果直接选可交易的所有股票的全集,一些平台没有相应的接口,即使有相应的接口,大家对停牌,新股等的处理不同,造成最终选中的股票集大小也不同。所以大家都取个交集,定 2400 支了。
首先出场的是在量化之路上飞奔的 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%收益的男人!巴菲特才多少?而且巴菲特有教过你怎么赚钱的吗,我的策略代码你可以全部拿走,请忽略知识产权问题,分文不取!如果因为这个策略实盘导致破产吃不上饭,请联系我,我会帮你转接社会救济服务站。
国内起步比较早的一家量化平台,我早期经常混迹其中。号称用户超多,但其它几个平台的数据我也看不到,所以也不知道是不是“超”多。首页上有一个醒目的位置,向你展示他们感觉比较得意的策略,运行个一两年,收益过百分之一千也很常见,而且相当数量的代码你可以看到。你可以不知不觉的拿走实盘,你要是这样出了问题,显然不应该联系我。但现面这个策略可以:
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 ‘’的成绩,并向我们展示了回测结果:
正如你所期待的那样,又双 赚 钱 啦!赚钱的策略是如此容易写,赔钱的策略将成会稀缺资源。
这是一家新兴的量化平台,用户数量暂时较少,至于我为什么要拿它来测评,你看完下面就知道了。
他们也支持 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 秒,你们的服务器强哥的好么?
我猜肯定不是,大概是在后台做了很多工作,把运算批量进行了或并行了之类的。但是,有功夫搞这个没功夫把网站完善一下吗,我进去有点摸不着北。
这也是一家专门做量化的网站,摊子铺的很大,在他们还没声称要放弃对 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 分钟了。收益么,都赚钱啦,不过我也不关心这个。
友情提示,速度、回测收益什么的并不是最重要的。速度快就是验证你想法的速度快点,假如你有的只是烂策略,速度越快只是意味着在一定时间内,这个平台更能证明你拙劣的想法很多而已。如果因为回测收益高就觉得实盘收益也会高,这跟说五千万将近一个亿没什么区别。(且不说上述这些平台自身也并不是没有问题的)。