个人觉得老外搞的这个PyalgoTrade,优点是按照设计模式的规范编写,扩展起来可能方便些。缺点是代码繁琐,效率较低。
如果你是初次接触还是建议了解国内的吧。
tushare就是国内搞的免费获取股票数据的类库,很好用,代码简介使用方便,一个get_k_data()函数解决一切问题,函数名称也很秀气。
get_k_data可以获取国内几个大平台的免费股票数据,实时性虽然不行,但用在复盘回测上还是挺有用的。
PyalgoTrade内部提供的默认接口都是google或者yahoo国外的,在国内没法用。
下面首先介绍在pythong3.6(X64)版本上搭建。
1、基础类库安装:numpy、matplotlib、talib等,numpy, matplotlib安装简单可以通过python的pip install方式安装
talib稍微复杂一些,先要下载ta-lib(这个是C库)我使用VS2017编译,然后下载python封住的类库TA-Lib-0.4.16,下载tar.gz版本解压后通过命令python setup.py install方式安装,需要注意的时候c库的ta-lib需要复制到C盘,编译TA-LIB-0.4.16封装类库时需要在vs2017 64位编译环境下进行安装,如果选择默认编译环境会出现页内存不够的错误。
2、tushare和pyalgotrade我分别下载源码tushare-1.1.6.tar.gz和PyAlgoTrade-0.18.tar.gz目的是以后需要根据业务需求修改源码,扩展适合自己的回测平台。tushare采用python setup.py install即可安装成功,但是PyAlgoTrade是在Python2.X环境开发,不适合3.6,此时我们需要修改代码否则安装后测试程序无法运行。
3、PyAlgoTrade代码修改,目前我只简单做了几处BUG
1)首先需要安装3.6升级说明修改代码中关于filter, list,dict等iteritems变成items
2)filter, dict.getValues等这些在3.6版本里面会认为返回iterator,所以需要通过强制list等方式转换一下,否则会出现运行错误。
3)把调用sort的地方lamda参数修改位key=xxx
4)当然还有几处性能上的错误,比如把set()类型改为list类型,减少过多sort调用等
4、tushare增加到PyAlgoTrade
实现类barfeed / tsfeed.py文件,源码如下:
# coding="utf8"
#
# 这个文件是实现Tushare的数据处理,通过调用get_k_data()函数实现数据和Bars的绑定
#
from pyalgotrade.barfeed import membf
from pyalgotrade import bar
import tushare as ts
import datetime
def parse_date(date):
# This custom parsing works faster than:
# datetime.datetime.strptime(date, "%Y-%m-%d")
year = int(date[0:4])
month = int(date[5:7])
day = int(date[8:10])
d = datetime.datetime(year, month, day)
if len(date) > 10:
h = int(date[11:13])
m = int(date[14:16])
t = datetime.time(h,m)
ret = datetime.combine(d,t)
else:
ret = d
return ret
class Feed(membf.BarFeed):
def __init__(self, frequency = bar.Frequency.DAY, maxLen=None):
super(Feed, self).__init__(frequency, maxLen)
def rowParser(self, ds, frequency=bar.Frequency.DAY):
dt = parse_date(ds["date"])
open = float(ds["open"])
close = float(ds["close"])
high = float(ds["high"])
low = float(ds["low"])
volume = float(ds["volume"])
return bar.BasicBar(dt, open, high, low, close, volume, None, frequency)
def barsHaveAdjClose(self):
return False
def addBarsFromCode(self, code, start, end, ktype="D", index=False):
frequency = bar.Frequency.DAY
if ktype == "D":
frequency = bar.Frequency.DAY
elif ktype == "W":
frequency = bar.Frequency.WEEK
elif ktype == "M":
frequency = bar.Frquency.MONTH
elif ktype == "5" or ktype == "15" or ktype == "30" or ktype == "60":
frequency = bar.Frequency.MINUTE
ds = ts.get_k_data(code = code, start = start, end = end, ktype = ktype, index = index)
bars_ = []
for i in ds.index:
bar_ = self.rowParser(ds.loc[i], frequency)
bars_.append(bar_)
self.addBarsFromSequence(code, bars_)
5、下面是一个测试例子
# coding="utf8"
from pyalgotrade import strategy
from pyalgotrade.barfeed import tsfeed
from pyalgotrade import plotter
from pyalgotrade.technical import ma
from pyalgotrade.technical import cross
from pyalgotrade.stratanalyzer import returns
class MyStrategy(strategy.BacktestingStrategy):
def __init__(self, feed, instrument, smaPeriod):
super(MyStrategy, self).__init__(feed)
self.__instrument = instrument
self.__closed = feed[instrument].getCloseDataSeries()
self.__ma = ma.SMA(self.__closed, smaPeriod)
self.__position = None
def getSMA(self):
return self.__ma
def onEnterLong(self, position):
print("onEnterLong", position.getShares())
def onEnterCanceled(self, position):
self.__position = None
print("onEnterCanceled", position.getShares())
def onExitOk(self, position):
self.__position = None
print("onExitOk", position.getShares())
def onExitCanceled(self, position):
self.__position.exitMarket()
print("onExitCanceled", position.getShares())
def onBars(self, bars):
if self.__position is None:
if cross.cross_above(self.__closed, self.__ma) > 0:
shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice())
print("cross_above shares,", shares)
# Enter a buy market order. The order is good till canceled.
self.__position = self.enterLong(self.__instrument, shares, True)
elif not self.__position.exitActive() and cross.cross_below(self.__closed, self.__ma) > 0:
print("cross_below")
self.__position.exitMarket()
def getClose(self):
return self.__closed
code = "603019"
feed = tsfeed.Feed()
feed.addBarsFromCode(code,start='2018-01-29',end='2018-04-04')
# Evaluate the strategy with the feed's bars.
myStrategy = MyStrategy(feed, code, 5)
returnsAnalyzer = returns.Returns()
myStrategy.attachAnalyzer(returnsAnalyzer)
plt = plotter.StrategyPlotter(myStrategy)
plt.getInstrumentSubplot(code).addDataSeries("SMA", myStrategy.getSMA())
plt.getOrCreateSubplot("returns").addDataSeries("Simple returns", returnsAnalyzer.getReturns())
myStrategy.run()
myStrategy.info("Final portfolio value: $%.2f" % myStrategy.getResult())
plt.plot()
运行结果图形如下: