Backtesting.pyでバックテスト OANDA
はじめに
元ネタは以下の記事です。
OANDA APIのバージョンが上がっていて、V2でなければAPI接続できないため、
こちらの記事を参考にV2バージョンで実装してみました。
【fx、python】Backtesting.pyでバックテストしてみる | たぬきねこのAI&FX研究室
環境
- Windows10
- Python 3.8.1
- pip 20.1.1
- oandapyV20==0.6.3
- pandas==1.0.1
- Backtesting==0.1.7
Backtestingのインストール
pipでインストール
pip install backtesting
Backtesting.py - Backtest trading strategies in Python
バックテストの実装
公式のサンプルコードを元に実装していきます。
ライブラリのインポート
from datetime import datetime import oandapyV20 import oandapyV20.endpoints.instruments as instruments import pandas as pd from backtesting import Backtest, Strategy from backtesting.lib import crossover from backtesting.test import SMA
必要に応じてライブラリはpip等でインストールしておきます。
pip install oandapyV20 pandas
バックテスト用のデータ抽出(OANDA API)
OANDA APIを用いてデータを取得します。
ここでは日足を指定します。
oanda = oandapyV20.API(environment='live', access_token='APIキー') params = {"count": 1000, "granularity": "D"} r = instruments.InstrumentsCandles(instrument="USD_JPY", params=params,) oanda.request(r)
「instruments.InstrumentsCandles」がOANDA APIのV1では「get_history」に相当するAPIです。
Dataframeに変換
backtesting.pyで用いるデータはpandasのDataFrameでなければいけないので、
取得したデータをDataFrameに変換します。
# 取得したデータをpandasのDataFrameに変換 df = pd.DataFrame(r.response['candles']) # 'time', 'Open', 'High', 'Low', 'Close', 'Volume'を抽出 data = [] for raw in r.response['candles']: data.append([raw['time'], float(raw['mid']['o']), float(raw['mid']['h']), float(raw['mid']['l']), float(raw['mid']['c']), float(raw['volume'])]) # リストからPandas DataFrameへ変換 df_ohlc = pd.DataFrame(data) # backtesting.pyのデータに合わせてcolumns名を設定 df_ohlc.columns = ['time', 'Open', 'High', 'Low', 'Close', 'Volume'] # インデックスを変更する df_ohlc = df_ohlc.set_index('time') # 時間をdatetime型に変更 df_ohlc.index = pd.to_datetime(df_ohlc.index)
テストに使う戦略(Strategy)
公式のサンプルを使います。
from backtesting import Backtest, Strategy from backtesting.lib import crossover from backtesting.test import SMA class SmaCross(Strategy): n1 = 10 n2 = 30 def init(self): self.sma1 = self.I(SMA, self.data.Close, self.n1) self.sma2 = self.I(SMA, self.data.Close, self.n2) def next(self): if crossover(self.sma1, self.sma2): self.buy() elif crossover(self.sma2, self.sma1): self.sell()
バックテストの実行
from backtesting import Backtest # バックテストの実行 bt = Backtest(df_ohlc, SmaCross, cash=10000, commission=.002) output = bt.run() print(output) bt.plot()
実行するとコンソールに下記のような結果が出力され、
ブラウザにグラフがプロットされます。
Start 2016-08-15 21:00:00+00:00 End 2020-05-28 21:00:00+00:00 Duration 1382 days 00:00:00 Exposure [%] 95.4414 Equity Final [$] 8707.69 Equity Peak [$] 11379.8 Return [%] -12.9231 Buy & Hold Return [%] 7.48993 Max. Drawdown [%] -23.6653 Avg. Drawdown [%] -2.95408 Max. Drawdown Duration 1261 days 00:00:00 Avg. Drawdown Duration 132 days 00:00:00 # Trades 42 Win Rate [%] 28.5714 Best Trade [%] 9.97924 Worst Trade [%] -3.57319 Avg. Trade [%] -0.306852 Max. Trade Duration 99 days 00:00:00 Avg. Trade Duration 32 days 00:00:00 Expectancy [%] 1.39783 SQN -0.911754 Sharpe Ratio -0.141554 Sortino Ratio -0.342624 Calmar Ratio -0.0129663 _strategy SmaCross
ソースコードの全体
from datetime import datetime import oandapyV20 import oandapyV20.endpoints.instruments as instruments import pandas as pd from backtesting import Backtest, Strategy from backtesting.lib import crossover from backtesting.test import SMA # OANDA APIを用いてデータの取得 oanda = oandapyV20.API(environment='live', access_token='APIキー') params = {"count": 1000, "granularity": "D"} r = instruments.InstrumentsCandles(instrument="USD_JPY", params=params,) oanda.request(r) # 取得したデータをpandasのDataFrameに変換 df = pd.DataFrame(r.response['candles']) # 'time', 'Open', 'High', 'Low', 'Close', 'Volume'を抽出 data = [] for raw in r.response['candles']: data.append([raw['time'], float(raw['mid']['o']), float(raw['mid']['h']), float(raw['mid']['l']), float(raw['mid']['c']), float(raw['volume'])]) # リストからPandas DataFrameへ変換 df_ohlc = pd.DataFrame(data) # backtesting.pyのデータに合わせてcolumns名を設定 df_ohlc.columns = ['time', 'Open', 'High', 'Low', 'Close', 'Volume'] # インデックスを変更する df_ohlc = df_ohlc.set_index('time') # df_ohlc.head() # # date型を綺麗にする df_ohlc.index = pd.to_datetime(df_ohlc.index) # df_ohlc.tail() # 売買戦略 class SmaCross(Strategy): n1 = 10 n2 = 30 def init(self): self.sma1 = self.I(SMA, self.data.Close, self.n1) self.sma2 = self.I(SMA, self.data.Close, self.n2) def next(self): if crossover(self.sma1, self.sma2): self.buy() elif crossover(self.sma2, self.sma1): self.sell() # バックテストの実行 bt = Backtest(df_ohlc, SmaCross, cash=10000, commission=.002) output = bt.run() print(output) bt.plot()
おまけ
instruments.InstrumentsCandlesの引数"granularity"に指定する値を変えることによって、
様々な時間軸の情報を取得することができます。
詳細は下記ページに載っています。 レート | OANDA API
- “S5” - 5 秒
- “S10” - 10 秒
- “S15” - 15 秒
- “S30” - 30 秒
- “M1” - 1 分
- “M2” - 2 分
- “M3” - 3 分
- “M5” - 5 分
- “M10” - 10 分
- “M15” - 15 分
- “M30” - 30 分
- “H1” - 1 時間
- “H2” - 2 時間
- “H3” - 3 時間
- “H4” - 4 時間
- “H6” - 6 時間
- “H8” - 8 時間
- “H12” - 12 時間
- “D” - 1 日
- “W” - 1 週
- “M” - 1 か月
もしgranularityパラメータが設定されなかった場合は、granularity のデフォルト値は”S5”となります。