【fx、python】Backtesting.pyでバックテストしてみる

Backtesting.pyという簡単にバックテストを行うことができるライブラリを用いて
バックテストをしてみます。
OANDA API から取得したデータを使ってバックテストを実行してみます。

最低限のpythonの操作はできることを想定しています。

バックテストとは

バックテストは、過去のデータを用いて、売買ルールの性能を検証することを言います。

自分の売買アルゴリズムを検証したい時にはバックテストをしてから、実際の環境に適用していきます。

OANDAとは

今回はOANDA APIを使って取得したデータを使ってバックテストを行います。
OANDA Japanはアメリカのfx会社です。

pythonから使えるapiを提供しているので、
pythonでfx関連のことをしようとする人はよく使うと思います。
今回はoandaからデータを取得して、ローソク足チャートをプロットするので
oandaの口座を持っている人前提の記事になります。

apiを使うためにはoandaで本番口座かデモ講座を作る必要があります。
OANDA Japanのページにいき口座を作成してから以下をお読みください。

Backtesting.pyとは

Backtesting.pyとはpythonで簡単にバックテストを行うことが出来るライブラリです。

Backtesting.py

Backtesting.pyは非常に軽く、スムーズに動作してくれます。
簡単に実装できるので、バックテスト初心者の自分のような人にとってはとてもありがたいライブラリです。

ちなみにBacktesting.pyはpythonの3系で動きます。

Backtesting.pyは以下のコマンドでインストール出来ます。

pip install backtesting

OANDA APIとBacktesting.pyを使ってバックテストをしてみる(実装)

公式のサンプルコードを元に実装していきます。

まずは必要なライブラリ等をインストールします。

from oandapyV20 import API
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 APIから取得します。

# OANDA APIを用いてデータの取得
def get_candles(instrument, params, api):
    instruments_candles = instruments.InstrumentsCandles(
        instrument=instrument, params=params)

    api.request(instruments_candles)
    response = instruments_candles.response

    df = pd.DataFrame(response["candles"])
    return df

# OANDA APIからデータを取得する
params = {
    "granularity": "M5",
    "count": 5000,
    "price": "B",
}

api = API(
    access_token="your access_token",
    environment="practice")
data = get_candles(instrument="USD_JPY", params=params, api=api)
data["time"] = pd.to_datetime(data["time"])

your_access_tokenは自分用に変更してください。

OANDA APIからデータを取得することに関しては以下の記事を参照してください。

次にbacktesting.pyで用いるデータはohlcのDataFrame形式でなければいけなので、それどおりに変更します。

# 取得したデータを変換していく
ohlc = [[float(d["o"]), float(d["h"]), float(d["l"]), float(d["c"])]
        for d in data["bid"]]

df_ohlc = pd.DataFrame(ohlc, columns=["Open", "High", "Low", "Close"])
df_ohlc["time"] = data["time"]

df_ohlc = df_ohlc.set_index("time")

これでbacktesting.pyに投入するようの為替データの作成は終了です。

戦略(Strategy)

取引の戦略を実装していきます。

戦略を実装するときにはbacktestingのStrategyクラスを継承して実装していきます。

公式のサンプルコードを用います。

class SmaCross(Strategy):

    # 移動平均線のパラメータ(区間)。このパラメータを最適化する
    n1 = 10
    n2 = 20

    def init(self):
        # 2つの移動平均線の定義
        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()

init関数はこの戦略の初期化。ここでは移動平均線の初期化を行っています。

next関数は定期的に呼ばれ、売買の判断を行います。
ここでは2つの移動平均のクロスオーバーで売買の判定を行っています。

バックテストの実行

ではバックテスト実行してみます。

# バックテストの実行
bt = Backtest(data=df_ohlc, strategy=SmaCross, cash=10000, commission=.002)
output = bt.run()
print(output)

Backtestクラスを用いて、btインスタンスを生成し、
bt.run()を実行することで実行できます。

Backtestの各引数は、

  • data:チャートデータ(pandsのDataFrame)
  • strategy:戦略(先程実装したクラス)
  • cash: 資金(恐らくドル?)
  • commission: 手数料

上記のコードを実行すると、以下のようなバックテストの結果の出力と、

Start                     2015-11-26 22:00:00
End                       2019-09-26 21:00:00
Duration                   1399 days 23:00:00
Exposure [%]                          93.5712
Equity Final [$]                      9093.72
Equity Peak [$]                       10466.3
Return [%]                           -9.06279
Buy & Hold Return [%]                 12.1015
Max. Drawdown [%]                    -18.5343
Avg. Drawdown [%]                    -2.48036
Max. Drawdown Duration       43 days 00:00:00
Avg. Drawdown Duration       27 days 00:00:00
# Trades                                   36
Win Rate [%]                          33.3333
Best Trade [%]                        9.96785
Worst Trade [%]                      -4.83694
Avg. Trade [%]                      -0.257426
Max. Trade Duration         100 days 00:00:00
Avg. Trade Duration          37 days 00:00:00
Expectancy [%]                        1.77259
SQN                                 -0.681403
Sharpe Ratio                       -0.0981922
Sortino Ratio                       -0.204831
Calmar Ratio                       -0.0138892
_strategy                            SmaCross

ブラウザに以下のようなグラフがプロットされます。

上記のグラフからどこで売買したかなどがわかります。

おわりに

今回はBacktesting.pyを使って、バックテストを試してみました。
比較的、簡単にバックテストを試すことができたので、これからも使っていこうと思います。

まだ、私も使い始めたばかりなので、Backtesting.pyについて、わからないことが多いので今後も調べていこうと思います。

参考文献

Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理

https://kernc.github.io/backtesting.py/
関連記事

付録:ソースコードの全体像

from oandapyV20 import API
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


def get_candles(instrument, params, api):
    instruments_candles = instruments.InstrumentsCandles(
        instrument=instrument, params=params)

    api.request(instruments_candles)
    response = instruments_candles.response

    df = pd.DataFrame(response["candles"])
    return df

# OANDA APIからデータを取得する
params = {
    "granularity": "M5",
    "count": 5000,
    "price": "B",
}

api = API(
    access_token="your_access_token",
    environment="practice")
data = get_candles(instrument="USD_JPY", params=params, api=api)
data["time"] = pd.to_datetime(data["time"])

# 取得したデータを変換していく
ohlc = [[float(d["o"]), float(d["h"]), float(d["l"]), float(d["c"])]
        for d in data["bid"]]

df_ohlc = pd.DataFrame(ohlc, columns=["Open", "High", "Low", "Close"])
df_ohlc["time"] = data["time"]

df_ohlc = df_ohlc.set_index("time")


class SmaCross(Strategy):

    # 移動平均線のパラメータ(区間)。このパラメータを最適化する
    n1 = 10
    n2 = 20

    def init(self):
        # 2つの移動平均線の定義
        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(data=df_ohlc, strategy=SmaCross, cash=10000, commission=.002)
output = bt.run()
print(output)
タイトルとURLをコピーしました