pyalgotrade量化交易回測

轉載請註明出處:https://blog.csdn.net/xuezoutianya/article/details/104059649

pyalgotrade官網文檔http://gbeced.github.io/pyalgotrade/docs/v0.20/html/

官方給的教程很容易上手,初學者可以參考官方文檔中的示例策略,唯一的問題是例程中的下載方法不適用於下載國內股票數據。

本博文介紹如何下載國內股票數據,以及如何定製化你自己的交易策略並顯示回測結果。

如何下載國內股票數據

在網上查了不少博文,找到兩種方式,但第一種方式我自己用會報錯,這裏還是列出來

  • 使用pyalgotrade_tushare模塊

安裝方法

pip install pyalgotrade_tushare

使用代碼

# 導入pyalgotrade_tushare模塊
from pyalgotrade_tushare import tools

'''
其他代碼段
'''

# 股票代碼,str類型,如"399300"(滬深300)
instruments = ["399300"]
# 第二個參數是起始日期,第三個參數是結束日期,第四個參數是下載目錄,如"."(當前目錄)
feeds = tools.build_feed(instruments, 2018, 2019, ".")
  • 自己封裝函數或者模塊

MyDownload.py

原代碼出處https://blog.csdn.net/lawme/article/details/51495349,我添加了臨時文件刪除,以及格式修正

import tushare as ts
import pandas as pd
import os
 
def download_csv(code, start_date, end_date, filepath):
    data = ts.get_hist_data(code, start=start_date, end=end_date)

    # 數據存盤
    data.to_csv('temp.csv')

    # 讀出數據,DataFrame格式
    df = pd.read_csv('temp.csv')

    # 從df中選取數據段,改變段名;新段'Adj Close'使用原有段'close'的數據  
    df2 = pd.DataFrame({'Date' : df['date'], 'Open' : df['open'],
                        'High' : df['high'],'Close' : df['close'],
                        'Low' : df['low'],'Volume' : df['volume'],
                        'Adj Close':df['close']})

    # 按照Yahoo格式的要求,調整df2各段的順序
    dt = df2.pop('Date')
    df2.insert(0,'Date',dt)
    o = df2.pop('Open')
    df2.insert(1,'Open',o)
    h = df2.pop('High')
    df2.insert(2,'High',h)
    l = df2.pop('Low')
    df2.insert(3,'Low',l)
    c = df2.pop('Close')
    df2.insert(4,'Close',c)
    v = df2.pop('Volume')
    df2.insert(5,'Volume',v)

    # 新格式數據存盤,不保存索引編號  
    df2.to_csv(filepath, index=False)
    os.remove("temp.csv")

在同目錄主代碼中使用上述模塊

from pyalgotrade.barfeed import quandlfeed
# 導入自定義模塊
import MyDownload

'''
其他代碼段
'''

instrument = "399300"
# 下載股票數據
MyDownload.download_csv(instrument, "2017-01-01", "2020-01-01", instrument+".csv")

# 從CSV文件加載bar feed
feed = quandlfeed.Feed()
feed.addBarsFromCSV(instrument, instrument+".csv")

注:由於pyalgotrade與pandas存在兼容性問題,使用第二種方法會出現警告,但不影響使用

如何定製化交易策略

交易策略的定製是爲了解決一下問題:

在什麼時候買?買多少?

在什麼時候賣?賣多少?

以下以日線交易數據爲例進行說明

onBar函數在遍歷到每天的交易數據都會被調用一次,所以上述問題的具體解決是在這個函數中實現。但是除了定投以外,股票的買賣需要擇時,需要參考股票的指標數據,pyalgotrade提供了很多股票分析常用的指標計算函數,具體請參考pyalgotrade.technical庫。

我將默認金額即$1000000分成10層倉位,並按照MACD的金叉買入3層,死叉賣出3層制定的交易策略代碼如下(其中還計算了很多其他指標但尚未用於交易策略的制定),可以通過修改加入新的股票指標,股票指標計算函數的參數,買賣判斷條件和倉位買賣層數自定義交易策略。

from pyalgotrade import strategy, broker, plotter
from pyalgotrade.barfeed import quandlfeed
from pyalgotrade.stratanalyzer import returns, sharpe
from pyalgotrade.technical import ma, macd, rsi, stoch, bollinger
from pyalgotrade import broker as basebroker

class MyStrategy(strategy.BacktestingStrategy):
    def __init__(self, feed, instrument, bBandsPeriod):
        super(MyStrategy, self).__init__(feed)
        self.__instrument = instrument
        # 使用調整後的數據
        if feed.barsHaveAdjClose():
            self.setUseAdjustedValues(True)
        # 持有倉位
        self.__holdPosition = 0.0
        # 空閒倉位
        self.__emptyPosition = 10.0
        # 單元持倉金額
        self.__unit = self.getBroker().getCash(False) / 10
        # 統計收盤價
        self.__priceDS = feed[instrument].getPriceDataSeries()
        # 計算macd指標
        self.__macd = macd.MACD(self.__priceDS, 12, 26, 9)
        # 計算KD指標
        self.__stoch = stoch.StochasticOscillator(feed[instrument], 9, 3)
        # 計算rsi指標
        self.__rsi7 = rsi.RSI(self.__priceDS, 7)
        self.__rsi14 = rsi.RSI(self.__priceDS, 14)
        # 計算布林線
        self.__bbands = bollinger.BollingerBands(self.__priceDS, bBandsPeriod, 2)

    def getPriceDS(self):
        return self.__priceDS
        
    def getMACD(self):
        return self.__macd

    def getStoch(self):
        return self.__stoch
        
    def getRSI7(self):
        return self.__rsi7
        
    def getRSI14(self):
        return self.__rsi14

    def getBollingerBands(self):
        return self.__bbands
    
    def onOrderUpdated(self, order):
        if order.isBuy():
            orderType = "Buy"
        else:
            orderType = "Sell"
        self.info("%s order %d updated - Status: %s" % (
            orderType, order.getId(), basebroker.Order.State.toString(order.getState())
        ))

    def onBars(self, bars):
        lower = self.__bbands.getLowerBand()[-1]
        middle = self.__bbands.getMiddleBand()[-1]
        upper = self.__bbands.getUpperBand()[-1]
        
        if lower is None:
            return

        bar = bars[self.__instrument]
        # 持有股票份額
        shares = self.getBroker().getShares(self.__instrument)
        # 最新股價
        price = bar.getPrice()
        
        # 買入策略
        if self.__macd.getHistogram()[-1] is None or self.__macd.getHistogram()[-2] is None:
            return
        # 金叉形成
        if self.__macd.getHistogram()[-2] < 0 and self.__macd.getHistogram()[-1] > 0:
            PositionToBuy = 0
            if self.__emptyPosition >= 3:
                PositionToBuy = 3
            else:
                PositionToBuy = self.__emptyPosition
            sharesToBuy = int(PositionToBuy * self.__unit / price)
            if(self.marketOrder(self.__instrument, sharesToBuy)):
                self.__holdPosition += PositionToBuy
                self.__emptyPosition -= PositionToBuy
                self.info("Placing buy market order for %s shares" % sharesToBuy)
        # 死叉形成
        elif self.__macd.getHistogram()[-2] > 0 and self.__macd.getHistogram()[-1] < 0:
            PositionToSell = 0
            if self.__holdPosition >= 3:
                PositionToSell = -3
            else:
                PositionToSell = self.__holdPosition
            sharesToSell = int(PositionToSell * self.__unit / price)
            if(self.marketOrder(self.__instrument, sharesToSell)):
                self.__holdPosition += PositionToSell
                self.__emptyPosition -= PositionToSell
                self.info("Placing sell market order for %s shares" % sharesToSell)

如何顯示回測結果

圖表顯示

輸出股票日線的折線圖

# 創建MyStrategy實例
myStrategy = MyStrategy(feed, instrument, bBandsPeriod)

plt = plotter.StrategyPlotter(myStrategy, True, True, True)
plt.plot()

在股票日線上添加指標曲線

# 圖表中添加BOLL
plt.getInstrumentSubplot(instrument).addDataSeries("upper", myStrategy.getBollingerBands().getUpperBand())
plt.getInstrumentSubplot(instrument).addDataSeries("middle", myStrategy.getBollingerBands().getMiddleBand())
plt.getInstrumentSubplot(instrument).addDataSeries("lower", myStrategy.getBollingerBands().getLowerBand())

爲MACD,STOCH,RSI各新建一個圖表

# 圖表添加MACD
plt.getOrCreateSubplot("macd").addDataSeries("DIF", myStrategy.getMACD())
plt.getOrCreateSubplot("macd").addDataSeries("DEA", myStrategy.getMACD().getSignal())
plt.getOrCreateSubplot("macd").addDataSeries("MACD", myStrategy.getMACD().getHistogram())

# 圖表添加KD
plt.getOrCreateSubplot("stoch").addDataSeries("K", myStrategy.getStoch())
plt.getOrCreateSubplot("stoch").addDataSeries("D", myStrategy.getStoch().getD())

# 圖表添加RSI
plt.getOrCreateSubplot("rsi").addDataSeries("RSI7", myStrategy.getRSI7())
plt.getOrCreateSubplot("rsi").addDataSeries("RSI14", myStrategy.getRSI14())
plt.getOrCreateSubplot("rsi").addLine("Overbought", 70)
plt.getOrCreateSubplot("rsi").addLine("Oversold", 30)

文本輸出

# 添加回測分析
returnsAnalyzer = returns.Returns()
myStrategy.attachAnalyzer(returnsAnalyzer)

# 添加夏普比率分析
sharpeRatioAnalyzer = sharpe.SharpeRatio()
myStrategy.attachAnalyzer(sharpeRatioAnalyzer)

# 運行策略
myStrategy.run()
    
# 輸出投資組合的最終資產總值
print("最終資產總值: $%.2f" % myStrategy.getBroker().getEquity())
# 輸出總收益
print("總收益: %.2f %%" % (returnsAnalyzer.getCumulativeReturns()[-1] * 100))
# 輸出夏普比率
print("夏普比率: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0))

運行結果示例

命令行輸出結果如下:

最終資產總值: $955164.54
總收益: -4.48 %
夏普比率: -0.12

注:這個是完整代碼的運行結果,從回測結果來看,我目前的交易策略並不給力,哈哈。

完整代碼見https://github.com/beyondyouth/pyal/tree/master/test01

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章