每个有梦想的人都自己私下里憋着一股劲,默默地努力着,希望它日能冷不妨让大家大吃一惊。本人也不例外,经常混迹于各个量化平台,有灵感了就验证一下,看自己是否能成为下一颗冉冉升起的新星。正如大家所知道的那样,这种事情并没有发生。
前两天看一篇文章,大意是说大多数人都已彻底丧失了构建投资(机)战略眼光的能力,所以注定平庸。我认同。在任何方面,只要有人说大部分人注定平庸,我都会认同。基于这种认知,所以上面说的新星升起这种事大概将来也不会发生吧。
你们不同。毕竟能看到这篇文章并点赞的是极少数人,你们既然不属于大多数人,想来比我更有机会冉冉。苟富贵,莫想忘。
然而作会一个注定平庸的人,我一直坚持用战术上的勤奋来掩盖自己写不出好策略这一点。比如,我在超过 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 分钟了。收益么,都赚钱啦,不过我也不关心这个。
友情提示,速度、回测收益什么的并不是最重要的。速度快就是验证你想法的速度快点,假如你有的只是烂策略,速度越快只是意味着在一定时间内,这个平台更能证明你拙劣的想法很多而已。如果因为回测收益高就觉得实盘收益也会高,这跟说五千万将近一个亿没什么区别。(且不说上述这些平台自身也并不是没有问题的)。