PyAlgoTrade 0.20中文笔记

参考英文官方链接:http://gbeced.github.io/pyalgotrade/docs/v0.20/html/tutorial.html#optimizing(最新0.20版本)

参考中文链接:https://pyalgotrade-docs-zh-cn.readthedocs.io/zh_CN/develop/tutorial.html(最新0.18版本,python2使用)

 

实例的代码都是以日线为标准。

首先下载回测的日线数据:

python -m "pyalgotrade.tools.quandl" --source-code="WIKI" --table-code="ORCL" --from-year=2000 --to-year=2000 --storage=. --force-download --frequency=daily

文件格式如下:

Date,Open,High,Low,Close,Volume,Ex-Dividend,Split Ratio,Adj. Open,Adj. High,Adj. Low,Adj. Close,Adj. Volume
2000-12-29,30.88,31.31,28.69,29.06,31702200.0,0.0,1.0,28.121945213877797,28.513539658242028,26.127545601883227,26.46449896098733,31702200.0
2000-12-28,30.56,31.63,30.38,31.06,25053600.0,0.0,1.0,27.830526092490462,28.804958779629363,27.666602836710087,28.285868469658173,25053600.0
2000-12-27,30.38,31.06,29.38,30.69,26437500.0,0.0,1.0,27.666602836710087,28.285868469658173,26.755918082374667,27.94891511055407,26437500.0
2000-12-26,31.5,32.19,30.0,30.94,20589500.0,0.0,1.0,28.68656976156576,29.3149422420572,27.32054263006263,28.176586299137927,20589500.0
2000-12-22,30.38,31.98,30.0,31.88,35568200.0,0.0,1.0,27.666602836710087,29.123698443646763,27.32054263006263,29.032629968213218,35568200.0
2000-12-21,27.81,30.25,27.31,29.5,46719700.0,0.0,1.0,25.326143018068056,27.548213818646484,24.870800640900345,26.86520025289492,46719700.0
2000-12-20,28.06,29.81,27.5,28.5,54440500.0,0.0,1.0,25.55381420665191,27.147512526738897,25.043830744224078,25.9545154985595,54440500.0
2000-12-19,31.81,33.13,30.13,30.63,58653700.0,0.0,1.0,28.968882035409738,30.170985911132497,27.438931648126232,27.894274025293942,58653700.0
2000-12-18,30.0,32.44,29.94,32.0,61640100.0,0.0,1.0,27.32054263006263,29.542613430641055,27.265901544802503,29.14191213873347,61640100.0

 

from pyalgotrade import strategy
from pyalgotrade.barfeed import quandlfeed


class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument):
# 调用父方法,读取回测的数据,这里同事创建默认的交易对象broker super(MyStrategy, self).__init__(feed) self.__instrument = instrument def onBars(self, bars): bar = bars[self.__instrument]
# OnBars函数会通过对象run的方法多次调用,每次读取一行回测数据的信息。 self.info(bar.getClose()) # Load the bar feed from the CSV file
@ 可以在Feed类中重新定义切割读取csv中每一列数据的形式,默认的方式与下载读取的文件相同 feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed's bars. myStrategy = MyStrategy(feed, "orcl") myStrategy.run()

 

最简单的示例代码,继承strategy.BacktestingStrategy,重写__init__与onBars方法。

这里可以看出,myStrategy的示例中,基本包含了所有的对象,这个对象内部可以读取或者操作brokers,dataseries,feeds,technicals以及strategies

按照我自己的理解,完全可以首先创建feed对象,也就是提供的回测数据对象,

然后定义自己的strategy类,在Onbars内写逻辑,在__init__里面写具体的技术指标或者参数指标

 

第二个日均曲线的代码:

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


# 这个是为了直接调用round会None操作报错 def safe_round(value, digits): if value is not None: value = round(value, digits) return value class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument): super(MyStrategy, self).__init__(feed) # We want a 15 period SMA over the closing prices.
# 这个是技术线,第一个参数为数据集,这个为15日均线 self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 15) self.__instrument = instrument def onBars(self, bars):
# 通过bars[instrument]获取每次行数据或者就是蜡烛点的数据。 bar = bars[self.__instrument] self.info("%s %s" % (bar.getClose(), safe_round(self.__sma[-1], 2))) # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed's bars. myStrategy = MyStrategy(feed, "orcl") myStrategy.run()

 这个简短函数已经在MyStrategy类中导入了technical包中的ma模块中的SMA类,并实例化。

同样实例化MyStrategy并通过run启动该对象,这里主要是技术曲线的使用对象为查询机也就是提供历史数据的行数据集合feed[instrument].getCloseDataSeries()

 

多次调用技术面指标,并且第二个指标在第一个指标的基础上,技术指标也是一个查询集

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


def safe_round(value, digits):
    if value is not None:
        value = round(value, digits)
    return value


class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument):
        super(MyStrategy, self).__init__(feed)
# 首先获取14天的rsi线 self.__rsi = rsi.RSI(feed[instrument].getCloseDataSeries(), 14)
# 明显rsi的返回值也是一个查寻集,SMA将在ris有15个值的时候,获取rsi的平均值 self.__sma = ma.SMA(self.__rsi, 15) self.__instrument = instrument def onBars(self, bars): bar = bars[self.__instrument]
# 输出每一个蜡烛点的数据,由于技术线返回的都是查寻集,所以需要返回最新的值,需要用-1取值 self.info("%s %s %s" % ( bar.getClose(), safe_round(self.__rsi[-1], 2), safe_round(self.__sma[-1], 2) )) # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed's bars. myStrategy = MyStrategy(feed, "orcl") myStrategy.run()

 上面的代码很好的说明的了,技术指标由于受到回测数据每日的波动,所以根据日线的技术指标也是一个查询集

 

交易模型的代码

from __future__ import print_function

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


class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, smaPeriod):
# 调用父类初始化方法,并传入feed实例,并手动定义账户的初始化金额 super(MyStrategy, self).__init__(feed, 1000) self.__position = None self.__instrument = instrument # We'll use adjusted close values instead of regular close values.
# 这个参数具体效果还不知道 self.setUseAdjustedValues(True)
# 设置日均线 self.__sma = ma.SMA(feed[instrument].getPriceDataSeries(), smaPeriod)
# 在交易成功的时候触发该函数 def onEnterOk(self, position):
# position为enterLong创建,但为什么在函数内部变成了内部参数,源码那里还每看明白 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): # If the exit was canceled, re-submit it. self.__position.exitMarket() def onBars(self, bars): # Wait for enough bars to be available to calculate a SMA. if self.__sma[-1] is None: return
# 这里是具体的业务逻辑,所有的输出都在特定的函数来激活,在交易的函数中,默认带参数position示例。 bar = bars[self.__instrument]
# 当position没有定义的时候,需要通过enter来创建positon # If a position was not opened, check if we should enter a long position. if self.__position is None: if bar.getPrice() > self.__sma[-1]: # Enter a buy market order for 10 shares. The order is good till canceled.
# 创建订单,并将返回值赋值给self.__position self.__position = self.enterLong(self.__instrument, 10, True) # Check if we have to exit the position. elif bar.getPrice() < self.__sma[-1] and not self.__position.exitActive(): self.__position.exitMarket() def run_strategy(smaPeriod): # Load the bar feed from the CSV file feed = quandlfeed.Feed() feed.addBarsFromCSV("orcl", "WIKI-ORCL-2000-quandl.csv") # Evaluate the strategy with the feed. myStrategy = MyStrategy(feed, "orcl", smaPeriod) myStrategy.run()
# 通过getBroker()获取broker对象,并查寻该对象内部的属性 print("Final portfolio value: $%.2f" % myStrategy.getBroker().getEquity()) run_strategy(15)

 

这个代码,我主要疑问是在几个触发函数的内置默认参数为什么能够直接读取出来,框架里面通过什么样子的手段实现,

待理解以后写上

 

优化

这一块不是理解的很好,只能先写一下大概的理解。

首先下载IBM的三年的数据

Let’s start by downloading 3 years of daily bars for ‘IBM’:

python -m "pyalgotrade.tools.quandl" --source-code="WIKI" --table-code="IBM" --from-year=2009 --to-year=2011 --storage=. --force-download --frequency=daily

 

这是运行的需要的主类型

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)
# 初始化了6个技术指标,两个position的指标,初始为None 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()

 

This is the server script:

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


def parameters_generator():
    instrument = ["ibm"]
    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 bar feed from the CSV files.
    feed = quandlfeed.Feed()
    feed.addBarsFromCSV("ibm", "WIKI-IBM-2009-quandl.csv")
    feed.addBarsFromCSV("ibm", "WIKI-IBM-2010-quandl.csv")
    feed.addBarsFromCSV("ibm", "WIKI-IBM-2011-quandl.csv")

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

 

posted @ 2020-07-28 17:58  就是想学习  阅读(626)  评论(0编辑  收藏  举报