量化投资——利用 Pyalgotrade 进行 SMA策略回测

过年前忙于其它项目,过年期间又贪玩偷懒了,今天简要的将几段学习代码更新上来,供大家参交流。
量化交易在实盘交易之前,必须对量化交易策略进行回测。在此,我们主要介绍其中比较优秀的 Pyalgotrade 框架。

Pyalgotrade 简介

Pyalgotrade是事件驱动的回测框架,支持虚盘和实盘两种交易。文档完整,整合了TA-Lib(技术分析库)。在速度和灵活方面都表现出众。但它的一大硬伤是不支持 Pandas 的模块和对象,而且数据格式不支持国内股票数据,需要我们自己实现数据转换。

PyAlgoTrade 六大组件:

  • Strategies策略: 定义的实现交易逻辑的类:何时买、何时卖,等等;
  • Feeds数据源:These are data providing abstractions. 例如,你可以使用CSV数据源从一个格式化后的csv(以逗号分割)文件中加载数据推送给策略。 数据源不仅限于bars。
  • Brokers经纪商:经纪商模块负责执行订单。
  • DataSeries数据序列:DataSeries 是用于管理时间序列的抽象类
  • Technicals指标计算:这是你用来对DataSeries进行计算的一组过滤(指标)器。 例如简单移动平均线(SMA),相对强弱指标(RSI)等. 这些过滤(指标)器被建模为DataSeries 的装饰器。
  • Optimizer优化:这是能让你在不同电脑之间、或多进程、或二者结合以加快回测效率的一组类。

自定义回归策略

这里不废话了,我们直接上代码,我们的交易策略类 MyStrategy 继承自Pyalgotrade.strategy.BacktestingStrategy 类:

# 基于 pyalgotrade 的交易策略类
class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, smaPeriod):
        super(MyStrategy, self).__init__(feed)
        self.__instrument = instrument
        self.__closed = feed[instrument].getCloseDataSeries()
        self.__sma = ma.SMA(self.__closed, smaPeriod)
        self.__position = None
        self.getBroker()
       
    def getSMA(self):
        return self.__sma
   
    def onEnterLong(self, position):
        print("onEnterLong", position.getShares())

    def onEnterOk(self, position):
        execInfo = position.getEntryOrder().getExecutionInfo()
        self.info("BUY at %.2f" % (execInfo.getPrice()))
       
    def onEnterCanceled(self, position):
        self.__position = None
        print("onEnterCanceled", position.getShares())
       
    def onExitOk(self, position):
        execInfo = position.getExitOrder().getExecutionInfo()
        self.info("SELL at $%.2f" % (execInfo.getPrice()))
        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.__sma) > 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.__sma) > 0:
            print("cross_below")
            self.__position.exitMarket()
           

    def getClose(self):
        return self.__closed

对股票进行策略回溯

示例代码,以 格力电器(000651)为例,初始资本 100万,回溯时间 2018年1月1日 至 2019年 2月 12日,SMA周期 30:

code = "000651" # 格力电器
feed = tsfeed.Feed()
feed.addBarsFromCode(code,start='2018-01-01',end='2019-02-12')

# Evaluate the strategy with the feed's bars.
myStrategy = MyStrategy(feed, code, 30) # SMA周期 30
returnsAnalyzer = returns.Returns()
myStrategy.attachAnalyzer(returnsAnalyzer)
sharpe_ratio = sharpe.SharpeRatio()
myStrategy.attachAnalyzer(sharpe_ratio)

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()

最终回测结果如下图:


格力电器,2018-01-01 至 2019-02-12 回溯

不同周期的策略回测结果

如上图所示,格力电器 2018年 初始资本 100万,末期资本 72.88 万,亏损近 30%。如此看来,是否交易策略无效呢?
我们不妨进行更长时间的回测,从 2009年01月01日开始,进行10年时间的回测,结果如下:


格力电器,2009-01-01 至 2019-02-12 回溯

可见,格力电器 10 年时间回测结果,末期资本为 340.66 万元, 10年收益 240%。此外,经过我们测试,SMA 不同周期对收益也有明显影响,下面,直接给出我们针对 格力电器和工商银行 不同周期,不同SMA频率的回测结果:

格力电器收益率 1年(2018-01-01~ 2019-02-12) 10 年(2009-01-01~ 2019-02-12 )
SMA 5 -18.32% 116.41%
SMA 10 -26.01% 166.57%
SMA 15 -28.76% 127.1%
SMA 20 -25.44% 347.41%
SMA 30 -27.12% 240.66%
SMA 50 -3.12% 229.2%
工商银行收益率 1年(2018-01-01~ 2019-02-12) 10 年(2009-01-01~ 2019-02-12 )
SMA 5 0% 110.65%
SMA 10 -16.41% 88.34%
SMA 15 -8.43% 113.07%
SMA 20 -11.74% 30%
SMA 30 -14.44% 66.24%
SMA 50 -6.39% 40.90%

结论

根据我们基于自定义策略对格力电器和 工商银行的回测分析,初步可以得到如下结论:

  1. 基于我们的策略,坚持长期策略投资,其最大收益高于大盘表现(最大收益率 347% :格力电器,10年期,SMA 20);
  2. 选取SMA 周期,在 20 左右(在15 ~ 30 之间),其收益最大,相对亏损风险较小;
  3. 选取SMA 周期越大(等于 50 时),风险越小;
  4. 综合来看,投资格力电器收益率更大,而风险在可控范围内。

注:以上结论只是作为对策略历史数据回测的结果分析,不可作为实际投资参考。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容