股票量化分析入门——PyAlgoTrade 中文文档

2018-06-11  本文已影响295人  coolwind

因为要学习,所以没找到官方手册的中文版。所以翻译一下,翻译的过程自己能够仔细阅读,翻译的结果也能给大家做一下参考。翻译的对应的版本是0.18,对应的原文地址在这里

分隔线以下为原文的翻译


教程

编写本教程的目的是提供 PyAlgoTrade 的快速入门。就像介绍里所说的, PyAlgoTrade 的目标是帮助你对股票交易的策略进行回测。或许你自己有一个交易的策略,并且你希望能够在历史数据上使用这个策略做模拟交易,一遍查看这个策略的效果。使用 PyAlgoTrade 只需要做很少的工作,就可以帮助你实现这个目标。

在继续之前我需要向 Pablo Jorge 表达我衷心的谢意,他帮助我校对了最初的设计和文档。

这个教程是在 UNIX 环境之下制作的,但是其中的步骤要用于 Windows 环境也很简单。

PyAlgoTrade 有6个主要的概念:

说了这么多,我们首先需要做的就是在一些数据上测试我们的交易策略。让我们使用2000年 Oracle 的股票价格数据,这些数据我们可以通过如下的命令下载到:

python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('orcl', 2000, 'orcl-2000.csv')"

pyalgotrade.tools.yahoofinance 包从哑无财经下载 CSV 格式的数据。orcl-2000.csv 文件看起来像这个样子:

Date,Open,High,Low,Close,Volume,Adj Close
2000-12-29,30.87,31.31,28.69,29.06,31655500,28.35
2000-12-28,30.56,31.12,30.37,31.06,25055600,30.30
2000-12-27,30.37,31.06,29.37,30.69,26441700,29.94
.
.
2000-01-04,115.50,118.62,105.00,107.69,116850000,26.26
2000-01-03,124.62,125.19,111.62,118.12,98122000,28.81</pre>

让我们从一个简单的策略开始,它的功能仅仅是打印出收盘价格,代码如下:

from pyalgotrade import strategy
from pyalgotrade.barfeed import yahoofeed

class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument):
        super(MyStrategy, self).__init__(feed)
        self.__instrument = instrument

    def onBars(self, bars):
        bar = bars[self.__instrument]
        self.info(bar.getClose())

#从 CSV 文件加载yahoo数据源
feed = yahoofeed.Feed()
feed.addBarsFromCSV("orcl", "orcl-2000.csv")

# 在数据源的记录中逐条执行策略。
myStrategy = MyStrategy(feed, "orcl")
myStrategy.run()

以上代码主要做了如下三件事:

  1. 申明一个新的策略。其中之定义了一个方法 onBars,这个方法将使用数据源的每一条数据作为参数,逐条调用。
  2. 从 CSV 文件加载数据源。
  3. 使用数据源提供给的每一条数据运行所定义的策略。

如果你运行这个脚本,你将会看到顺序排列的收盘价:

2000-01-03 00:00:00 strategy [INFO] 118.12
2000-01-04 00:00:00 strategy [INFO] 107.69
2000-01-05 00:00:00 strategy [INFO] 102.0
.
.
.
2000-12-27 00:00:00 strategy [INFO] 30.69
2000-12-28 00:00:00 strategy [INFO] 31.06
2000-12-29 00:00:00 strategy [INFO] 29.06

让我们前进一小步,将策略调整为打印收盘价的简单移动平均值,从而演示如何使用技术指标:

from pyalgotrade import strategy
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.technical import ma

class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument):
        super(MyStrategy, self).__init__(feed)
        # 我们希望以15作为周期在收盘加上计算 SMA 。
        self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 15)
        self.__instrument = instrument

    def onBars(self, bars):
        bar = bars[self.__instrument]
        self.info("%s  %s" % (bar.getClose(), self.__sma[-1]))

# 从 CSV 文件加载雅虎的数据源
feed = yahoofeed.Feed()
feed.addBarsFromCSV("orcl", "orcl-2000.csv")

# 使用数据源中的数据逐条运行策略。
myStrategy = MyStrategy(feed, "orcl")
myStrategy.run()

这和之前的那个示例很像,除了:
如果你运行这个脚本你将看到收盘价和对应的移动平均值,但是前14条记录的移动平均值是没有的。这是因为我们需要至少15条记录来得出移动平均值:

2000-01-03 00:00:00 strategy [INFO] 118.12 None
2000-01-04 00:00:00 strategy [INFO] 107.69 None
2000-01-05 00:00:00 strategy [INFO] 102.0 None
2000-01-06 00:00:00 strategy [INFO] 96.0 None
2000-01-07 00:00:00 strategy [INFO] 103.37 None
2000-01-10 00:00:00 strategy [INFO] 115.75 None
2000-01-11 00:00:00 strategy [INFO] 112.37 None
2000-01-12 00:00:00 strategy [INFO] 105.62 None
2000-01-13 00:00:00 strategy [INFO] 105.06 None
2000-01-14 00:00:00 strategy [INFO] 106.81 None
2000-01-18 00:00:00 strategy [INFO] 111.25 None
2000-01-19 00:00:00 strategy [INFO] 57.13 None
2000-01-20 00:00:00 strategy [INFO] 59.25 None
2000-01-21 00:00:00 strategy [INFO] 59.69 None
2000-01-24 00:00:00 strategy [INFO] 54.19 94.2866666667
2000-01-25 00:00:00 strategy [INFO] 56.44 90.1746666667
.
.
.
2000-12-27 00:00:00 strategy [INFO] 30.69 29.9866666667
2000-12-28 00:00:00 strategy [INFO] 31.06 30.0446666667
2000-12-29 00:00:00 strategy [INFO] 29.06 30.0946666667

当值在给定的时间区间上无法被计算出来的时候,所有的技术指标都会返回 None。

关于技术指标一个很重要的点是他们可以组合使用。这是因为它们都被设计为了数据序列。例如,基于收盘价计算 RSI,再基于RSI 计算 SMA,要实现上述操作十分简单,代码如下:

from pyalgotrade import strategy
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.technical import ma
from pyalgotrade.technical import rsi

class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument):
        super(MyStrategy, self).__init__(feed)
        self.__rsi = rsi.RSI(feed[instrument].getCloseDataSeries(), 14)
        self.__sma = ma.SMA(self.__rsi, 15)
        self.__instrument = instrument

    def onBars(self, bars):
        bar = bars[self.__instrument]
        self.info("%s  %s  %s" % (bar.getClose(), self.__rsi[-1], self.__sma[-1]))

# 从 CSV 文件加载雅虎数据源
feed = yahoofeed.Feed()
feed.addBarsFromCSV("orcl", "orcl-2000.csv")

# 使用数据源中的数据逐条运行策略。
myStrategy = MyStrategy(feed, "orcl")
myStrategy.run()

如果以运行这个脚本,将看到好几个值,如下边这样:

2000-01-03 00:00:00 strategy [INFO] 118.12 None None
2000-01-04 00:00:00 strategy [INFO] 107.69 None None
2000-01-05 00:00:00 strategy [INFO] 102.0 None None
2000-01-06 00:00:00 strategy [INFO] 96.0 None None
2000-01-07 00:00:00 strategy [INFO] 103.37 None None
2000-01-10 00:00:00 strategy [INFO] 115.75 None None
2000-01-11 00:00:00 strategy [INFO] 112.37 None None
2000-01-12 00:00:00 strategy [INFO] 105.62 None None
2000-01-13 00:00:00 strategy [INFO] 105.06 None None
2000-01-14 00:00:00 strategy [INFO] 106.81 None None
2000-01-18 00:00:00 strategy [INFO] 111.25 None None
2000-01-19 00:00:00 strategy [INFO] 57.13 None None
2000-01-20 00:00:00 strategy [INFO] 59.25 None None
2000-01-21 00:00:00 strategy [INFO] 59.69 None None
2000-01-24 00:00:00 strategy [INFO] 54.19 23.5673530141 None
2000-01-25 00:00:00 strategy [INFO] 56.44 25.0687519877 None
2000-01-26 00:00:00 strategy [INFO] 55.06 24.7476577095 None
2000-01-27 00:00:00 strategy [INFO] 51.81 23.9690136517 None
2000-01-28 00:00:00 strategy [INFO] 47.38 22.9108539956 None
2000-01-31 00:00:00 strategy [INFO] 49.95 24.980004823 None
2000-02-01 00:00:00 strategy [INFO] 54.0 28.2484181864 None
2000-02-02 00:00:00 strategy [INFO] 54.31 28.505177315 None
2000-02-03 00:00:00 strategy [INFO] 56.69 30.5596770599 None
2000-02-04 00:00:00 strategy [INFO] 57.81 31.5564353751 None
2000-02-07 00:00:00 strategy [INFO] 59.94 33.5111056589 None
2000-02-08 00:00:00 strategy [INFO] 59.56 33.3282358994 None
2000-02-09 00:00:00 strategy [INFO] 59.94 33.7177605915 None
2000-02-10 00:00:00 strategy [INFO] 62.31 36.2205441255 None
2000-02-11 00:00:00 strategy [INFO] 59.69 34.6623493641 29.0368892505
2000-02-14 00:00:00 strategy [INFO] 62.19 37.4284445543 29.9609620198
.
.
.
2000-12-27 00:00:00 strategy [INFO] 30.69 51.3196802735 49.8506368511
2000-12-28 00:00:00 strategy [INFO] 31.06 52.1646203455 49.997518354
2000-12-29 00:00:00 strategy [INFO] 29.06 47.3776678335 50.0790646925

交易

让我们将策略在推进一小步,这次我们模拟真实的交易。这个主意很简单:

from pyalgotrade import strategy
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.technical import ma

class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, smaPeriod):
        super(MyStrategy, self).__init__(feed, 1000)
        self.__position = None
        self.__instrument = instrument
        # 我们使用已调整收盘价代替常规的收盘价.
        self.setUseAdjustedValues(True)
        self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod)

    def onEnterOk(self, position):
        execInfo = position.getEntryOrder().getExecutionInfo()
        self.info("BUY at $%.2f" % (execInfo.getPrice()))

    def onEnterCanceled(self, position):
        self.__position = None

    def onExitOk(self, position):
        execInfo = position.getExitOrder().getExecutionInfo()
        self.info("SELL at $%.2f" % (execInfo.getPrice()))
        self.__position = None

    def onExitCanceled(self, position):
        # 如果退出被取消了,则再次提交它
        self.__position.exitMarket()

    def onBars(self, bars):
        # 等待足够的数据条数,以便计算SMA。
        if self.__sma[-1] is None:
            return

        bar = bars[self.__instrument]
        # 如果一个 position 还没打开,检查是否应该进入一个 long position。
        if self.__position is None:
            if bar.getPrice() > self.__sma[-1]:
                # 计入一个买单,买入10手。只要不取消就一直尝试。
                self.__position = self.enterLong(self.__instrument, 10, True)
        # 检查我们是否必须退出这个 position.
        elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive():
            self.__position.exitMarket()

def run_strategy(smaPeriod):
    # 从 CSV 文件加载雅虎数据源
    feed = yahoofeed.Feed()
    feed.addBarsFromCSV("orcl", "orcl-2000.csv")

    # 使用数据源计算策略
    myStrategy = MyStrategy(feed, "orcl", smaPeriod)
    myStrategy.run()
    print "Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity()

run_strategy(15)

如果你运行脚本,将会看到如下的输出:

2000-01-26 00:00:00 strategy [INFO] BUY at $27.26
2000-01-28 00:00:00 strategy [INFO] SELL at $24.74
2000-02-03 00:00:00 strategy [INFO] BUY at $26.60
2000-02-22 00:00:00 strategy [INFO] SELL at $28.40
2000-02-23 00:00:00 strategy [INFO] BUY at $28.91
2000-03-31 00:00:00 strategy [INFO] SELL at $38.51
2000-04-07 00:00:00 strategy [INFO] BUY at $40.19
2000-04-12 00:00:00 strategy [INFO] SELL at $37.44
2000-04-19 00:00:00 strategy [INFO] BUY at $37.76
2000-04-20 00:00:00 strategy [INFO] SELL at $35.45
2000-04-28 00:00:00 strategy [INFO] BUY at $37.70
2000-05-05 00:00:00 strategy [INFO] SELL at $35.54
2000-05-08 00:00:00 strategy [INFO] BUY at $36.17
2000-05-09 00:00:00 strategy [INFO] SELL at $35.39
2000-05-16 00:00:00 strategy [INFO] BUY at $37.28
2000-05-19 00:00:00 strategy [INFO] SELL at $34.58
2000-05-31 00:00:00 strategy [INFO] BUY at $35.18
2000-06-23 00:00:00 strategy [INFO] SELL at $38.81
2000-06-27 00:00:00 strategy [INFO] BUY at $39.56
2000-06-28 00:00:00 strategy [INFO] SELL at $39.42
2000-06-29 00:00:00 strategy [INFO] BUY at $39.41
2000-06-30 00:00:00 strategy [INFO] SELL at $38.60
2000-07-03 00:00:00 strategy [INFO] BUY at $38.96
2000-07-05 00:00:00 strategy [INFO] SELL at $36.89
2000-07-21 00:00:00 strategy [INFO] BUY at $37.19
2000-07-24 00:00:00 strategy [INFO] SELL at $37.04
2000-07-26 00:00:00 strategy [INFO] BUY at $35.93
2000-07-28 00:00:00 strategy [INFO] SELL at $36.08
2000-08-01 00:00:00 strategy [INFO] BUY at $36.11
2000-08-02 00:00:00 strategy [INFO] SELL at $35.06
2000-08-04 00:00:00 strategy [INFO] BUY at $37.61
2000-09-11 00:00:00 strategy [INFO] SELL at $41.34
2000-09-29 00:00:00 strategy [INFO] BUY at $39.07
2000-10-02 00:00:00 strategy [INFO] SELL at $38.30
2000-10-20 00:00:00 strategy [INFO] BUY at $34.71
2000-10-31 00:00:00 strategy [INFO] SELL at $31.34
2000-11-20 00:00:00 strategy [INFO] BUY at $23.35
2000-11-21 00:00:00 strategy [INFO] SELL at $23.83
2000-12-01 00:00:00 strategy [INFO] BUY at $25.33
2000-12-21 00:00:00 strategy [INFO] SELL at $26.72
2000-12-22 00:00:00 strategy [INFO] BUY at $29.17
Final portfolio value: $979.44

如果我们把计算移动平均的区间从15天调整为30天会怎样?这会让我们获得跟好的收益么?我们可以立即验证我们这个想法,如下:

for i in range(10, 30):
    run_strategy(i)

然后我们会发现使用SMA(20)我们可以获得最佳的收益:

Final portfolio value: $1075.38

如果我们只是尝试有限的几个参数调整,这样是可以的。但是如果我们要测试很多的参数,这样串行执行方法的规模会随着策略的复杂而增长。

优化

让我们来看一下优化器组件。它的实现思路也很简单:

为了演示它,我们将使用 RSI2 策略 (http://stockcharts.com/school/doku.php?id=chart_school:trading_strategies:rsi2) 它需要如下的参数:

如果我的数学知识还行,那么这里有 4409559 种不同的组合。

使用其中一种组合测试这个策略耗时0.16秒。如果我顺序测试所有的组合,将需要8.5天来找出里边的最优参数。这是一个很长的时间,但是如果我使用10台8核的计算机来做这个事情,那么总时间将下降为2.5小时。

缩短运行时间, 我们需要使用并行计算.

让我们从下载3年的‘道琼斯工业指数’的日数据开始:

python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('dia', 2009, 'dia-2009.csv')"
python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('dia', 2010, 'dia-2010.csv')"
python -c "from pyalgotrade.tools import yahoofinance; yahoofinance.download_daily_bars('dia', 2011, 'dia-2011.csv')"

把以下代码放入 rsi2.py 文件:

from pyalgotrade import strategy
from pyalgotrade.technical import ma
from pyalgotrade.technical import rsi
from pyalgotrade.technical import cross

class RSI2(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold):
        super(RSI2, self).__init__(feed)
        self.__instrument = instrument
        # We'll use adjusted close values, if available, instead of regular close values.
        if feed.barsHaveAdjClose():
            self.setUseAdjustedValues(True)
        self.__priceDS = feed[instrument].getPriceDataSeries()
        self.__entrySMA = ma.SMA(self.__priceDS, entrySMA)
        self.__exitSMA = ma.SMA(self.__priceDS, exitSMA)
        self.__rsi = rsi.RSI(self.__priceDS, rsiPeriod)
        self.__overBoughtThreshold = overBoughtThreshold
        self.__overSoldThreshold = overSoldThreshold
        self.__longPos = None
        self.__shortPos = None

    def getEntrySMA(self):
        return self.__entrySMA

    def getExitSMA(self):
        return self.__exitSMA

    def getRSI(self):
        return self.__rsi

    def onEnterCanceled(self, position):
        if self.__longPos == position:
            self.__longPos = None
        elif self.__shortPos == position:
            self.__shortPos = None
        else:
            assert(False)

    def onExitOk(self, position):
        if self.__longPos == position:
            self.__longPos = None
        elif self.__shortPos == position:
            self.__shortPos = None
        else:
            assert(False)

    def onExitCanceled(self, position):
        # If the exit was canceled, re-submit it.
        position.exitMarket()

    def onBars(self, bars):
        # Wait for enough bars to be available to calculate SMA and RSI.
        if self.__exitSMA[-1] is None or self.__entrySMA[-1] is None or self.__rsi[-1] is None:
            return

        bar = bars[self.__instrument]
        if self.__longPos is not None:
            if self.exitLongSignal():
                self.__longPos.exitMarket()
        elif self.__shortPos is not None:
            if self.exitShortSignal():
                self.__shortPos.exitMarket()
        else:
            if self.enterLongSignal(bar):
                shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice())
                self.__longPos = self.enterLong(self.__instrument, shares, True)
            elif self.enterShortSignal(bar):
                shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice())
                self.__shortPos = self.enterShort(self.__instrument, shares, True)

    def enterLongSignal(self, bar):
        return bar.getPrice() > self.__entrySMA[-1] and self.__rsi[-1] <= self.__overSoldThreshold

    def exitLongSignal(self):
        return cross.cross_above(self.__priceDS, self.__exitSMA) and not self.__longPos.exitActive()

    def enterShortSignal(self, bar):
        return bar.getPrice() < self.__entrySMA[-1] and self.__rsi[-1] >= self.__overBoughtThreshold

    def exitShortSignal(self):
        return cross.cross_below(self.__priceDS, self.__exitSMA) and not self.__shortPos.exitActive()

这是服务器的脚步:

import itertools
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.optimizer import server

def parameters_generator():
    instrument = ["dia"]
    entrySMA = range(150, 251)
    exitSMA = range(5, 16)
    rsiPeriod = range(2, 11)
    overBoughtThreshold = range(75, 96)
    overSoldThreshold = range(5, 26)
    return itertools.product(instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold)

# The if __name__ == '__main__' part is necessary if running on Windows.
if __name__ == '__main__':
    # Load the feed from the CSV files.
    feed = yahoofeed.Feed()
    feed.addBarsFromCSV("dia", "dia-2009.csv")
    feed.addBarsFromCSV("dia", "dia-2010.csv")
    feed.addBarsFromCSV("dia", "dia-2011.csv")

    # Run the server.
    server.serve(feed, parameters_generator(), "localhost", 5000)

服务器的代码作了3件事情:
这是使用 pyalgotrade.optimizer.worker 模块的工作脚本,用于在服务器提供的数据上并行的计算策略:

from pyalgotrade.optimizer import worker
import rsi2

# The if __name__ == '__main__' part is necessary if running on Windows.
if __name__ == '__main__':
    worker.run(rsi2.RSI2, "localhost", 5000, workerName="localworker")

当你运行服务器和客户端,你将会在服务器的控制台看到类似的输出:

2014-05-03 15:04:01,083 server [INFO] Loading bars
2014-05-03 15:04:01,348 server [INFO] Waiting for workers
2014-05-03 15:04:58,277 server [INFO] Partial result 1242173.28754 with parameters: ('dia', 150, 5, 2, 91, 19) from localworker
2014-05-03 15:04:58,566 server [INFO] Partial result 1203266.33502 with parameters: ('dia', 150, 5, 2, 81, 19) from localworker
2014-05-03 15:05:50,965 server [INFO] Partial result 1220763.1579 with parameters: ('dia', 150, 5, 3, 83, 24) from localworker
2014-05-03 15:05:51,325 server [INFO] Partial result 1221627.50793 with parameters: ('dia', 150, 5, 3, 80, 24) from localworker
.
.

在“工作者”的控制台看到如下的输出:

2014-05-03 15:02:25,360 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 15)
2014-05-03 15:02:25,377 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 94, 5)
2014-05-03 15:02:25,661 localworker [INFO] Result 1090481.06342
2014-05-03 15:02:25,661 localworker [INFO] Result 1031470.23717
2014-05-03 15:02:25,662 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 93, 25)
2014-05-03 15:02:25,665 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 14)
2014-05-03 15:02:25,995 localworker [INFO] Result 1135558.55667
2014-05-03 15:02:25,996 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 93, 24)
2014-05-03 15:02:26,006 localworker [INFO] Result 1083987.18174
2014-05-03 15:02:26,007 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 13)
2014-05-03 15:02:26,256 localworker [INFO] Result 1093736.17175
2014-05-03 15:02:26,257 localworker [INFO] Running strategy with parameters ('dia', 150, 5, 2, 84, 12)
2014-05-03 15:02:26,280 localworker [INFO] Result 1135558.55667
.
.

注意,你应当 仅运行一个服务器和一或多个“工作者”

如果你只是在你的个人电脑上尝试一下并行计算,你可以从 pyalgotrade.optimizer.local 模块中获得好处,就像这样:

import itertools
from pyalgotrade.optimizer import local
from pyalgotrade.barfeed import yahoofeed
import rsi2

def parameters_generator():
    instrument = ["dia"]
    entrySMA = range(150, 251)
    exitSMA = range(5, 16)
    rsiPeriod = range(2, 11)
    overBoughtThreshold = range(75, 96)
    overSoldThreshold = range(5, 26)
    return itertools.product(instrument, entrySMA, exitSMA, rsiPeriod, overBoughtThreshold, overSoldThreshold)

# The if __name__ == '__main__' part is necessary if running on Windows.
if __name__ == '__main__':
    # Load the feed from the CSV files.
    feed = yahoofeed.Feed()
    feed.addBarsFromCSV("dia", "dia-2009.csv")
    feed.addBarsFromCSV("dia", "dia-2010.csv")
    feed.addBarsFromCSV("dia", "dia-2011.csv")

    local.run(rsi2.RSI2, feed, parameters_generator())

以上代码作了3件事情:

  1. 生命一个生成器函数,由它产生不同的参数组合。
  2. 加载我们下载的 CSV 数据。
  3. 使用 pyalgotrade.optimizer.local 模块,并行的运行策略找到最佳的收益。

当你运行这段代码,你将会看到如下的输出:

2014-05-03 15:08:06,587 server [INFO] Loading bars
2014-05-03 15:08:06,910 server [INFO] Waiting for workers
2014-05-03 15:08:58,347 server [INFO] Partial result 1242173.28754 with parameters: ('dia', 150, 5, 2, 91, 19) from worker-95583
2014-05-03 15:08:58,967 server [INFO] Partial result 1203266.33502 with parameters: ('dia', 150, 5, 2, 81, 19) from worker-95584
2014-05-03 15:09:52,097 server [INFO] Partial result 1220763.1579 with parameters: ('dia', 150, 5, 3, 83, 24) from worker-95584
2014-05-03 15:09:52,921 server [INFO] Partial result 1221627.50793 with parameters: ('dia', 150, 5, 3, 80, 24) from worker-95583
2014-05-03 15:10:40,826 server [INFO] Partial result 1142162.23912 with parameters: ('dia', 150, 5, 4, 76, 17) from worker-95584
2014-05-03 15:10:41,318 server [INFO] Partial result 1107487.03214 with parameters: ('dia', 150, 5, 4, 83, 17) from worker-95583
.
.

对于所提供的历史数据,最优的收益是 $2314.40,产生这个收益的对应参数如下:

  1. entrySMA: 154
  2. exitSMA: 5
  3. rsiPeriod: 2
  4. overBoughtThreshold: 91
  5. overSoldThreshold: 18

图表

PyAlgoTrade 绘制一个策略的执行情况很容易。

把以下代码放入 sma_crossover.py 文件:

from pyalgotrade import strategy
from pyalgotrade.technical import ma
from pyalgotrade.technical import cross

class SMACrossOver(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, smaPeriod):
        super(SMACrossOver, self).__init__(feed)
        self.__instrument = instrument
        self.__position = None
        # We'll use adjusted close values instead of regular close values.
        self.setUseAdjustedValues(True)
        self.__prices = feed[instrument].getPriceDataSeries()
        self.__sma = ma.SMA(self.__prices, smaPeriod)

    def getSMA(self):
        return self.__sma

    def onEnterCanceled(self, position):
        self.__position = None

    def onExitOk(self, position):
        self.__position = None

    def onExitCanceled(self, position):
        # If the exit was canceled, re-submit it.
        self.__position.exitMarket()

    def onBars(self, bars):
        # If a position was not opened, check if we should enter a long position.
        if self.__position is None:
            if cross.cross_above(self.__prices, self.__sma) > 0:
                shares = int(self.getBroker().getCash() * 0.9 / bars[self.__instrument].getPrice())
                # Enter a buy market order. The order is good till canceled.
                self.__position = self.enterLong(self.__instrument, shares, True)
        # Check if we have to exit the position.
        elif not self.__position.exitActive() and cross.cross_below(self.__prices, self.__sma) > 0:
            self.__position.exitMarket()

再将以下代码放入另一个文件:

from pyalgotrade import plotter
from pyalgotrade.barfeed import yahoofeed
from pyalgotrade.stratanalyzer import returns
import sma_crossover

# Load the yahoo feed from the CSV file
feed = yahoofeed.Feed()
feed.addBarsFromCSV("orcl", "orcl-2000.csv")

# Evaluate the strategy with the feed's bars.
myStrategy = sma_crossover.SMACrossOver(feed, "orcl", 20)

# Attach a returns analyzers to the strategy.
returnsAnalyzer = returns.Returns()
myStrategy.attachAnalyzer(returnsAnalyzer)

# Attach the plotter to the strategy.
plt = plotter.StrategyPlotter(myStrategy)
# Include the SMA in the instrument's subplot to get it displayed along with the closing prices.
plt.getInstrumentSubplot("orcl").addDataSeries("SMA", myStrategy.getSMA())
# Plot the simple returns on each bar.
plt.getOrCreateSubplot("returns").addDataSeries("Simple returns", returnsAnalyzer.getReturns())

# Run the strategy.
myStrategy.run()
myStrategy.info("Final portfolio value: $%.2f" % myStrategy.getResult())

# Plot the strategy.
plt.plot()

这些代码作了3件事情:

  1. 从 CSV 文件加载数据源。
  2. 在所提供的数据上运行策略,并将结果附加到 StrategyPlotter 。
  3. 绘制策略。

生成的图表如下:

tutorial-5.png
我希望你喜欢这个入门教程。我建议你下载 PyAlgoTrade 从: http://gbeced.github.io/pyalgotrade/downloads/index.html 然后开始写你自己的策略。

你可以找到跟多的示例,在 简单策略 一节。

上一篇下一篇

猜你喜欢

热点阅读