機械学習でFX: Backtesting.pyでバックテスト実施

[前回の記事](https://qiita.com/THERE2/items/f716565c884e7750c6c1)では、PythonからFXの自動売買をするためのOANDA APIについて紹介させて頂きました。

今回はPythonのライブラリを使ったバックテストについて紹介したいと思います。

今回の記事でも以下のサイトを参考にさせていただきました。
こちらはPythonでFX自動売買を実現する方法がいろいろ紹介されていて大変オススメです。

[Pythonで為替取引のロジックのバックテストを行う方法（入門編）](http://www.algo-fx-blog.com/python-backtesting-beginner/)

# バックテストとは

予測モデルと売買アルゴリズムを用意して、いきなり本番投入するのは非常にリクスが高いでしょう。
しかし、本番投入前に過去のデータ（学習では使っていないデータ）でアルゴリズムを運用させてみて、実際に利益が出るかどうかを評価する、というテストができればかなりリスクを減らす事ができます。

OANDAではデモ環境などを使ってシミュレーションを行う方法が用意されており、アルゴリズムの検証用として非常に信頼がおけますが、実時間と同じテスト時間が必要となる事が難点です。
例えば3ヶ月の期間のテストを実施しようと思ったら本当に3ヶ月の時間がかかってしまいます。

しかし例えば過去36ヵ月分のデータを用意して、最初の33ヵ月を学習用データ、残りの3ヵ月分をバックテスト用データとし、その3ヵ月をあたかも未来のデータかのようにシミュレーションができれば、迅速に繰り返しテストが可能となります。

こういった目的に応えるため、pythonのバックテスト用ライブラリでは過去データを使って本番に近い形でシミュレーションテストができるようになっています。

通常、機械学習のモデルを使ってFX自動売買の本番運用に乗せるまでには次のようなステップが必要になるでしょう。

36ヵ月分の過去データが手元にあるとして、次のように進めて行きます。それぞれのデータの期間は一つの例です。

1. 最初の30ヵ月分のデータを機械学習のTrainingデータとして使う。
2. 次の3ヵ月分のデータを機械学習のValidation用データとして使う。
3. 最後の3ヵ月分のデータをバックテスト用のデータとして使う。
4. ここまでで精度が良さそうであれば、デモ環境でシミュレーションで1〜3ヵ月ほど運用する。
5. デモ環境のシミュレーションで利益が出る事が分かったら、本番環境で本番運用開始する。

今回の記事ではこのうち3.のバックテストを実施剃る方法の紹介となります。

# Backtesting.py

pythonのバックテスト用のライブラリはいろいろ存在するようですが、今回は素人に優しいと評判のBacktesting.pyを利用する事にしました。

[公式ドキュメント](https://kernc.github.io/backtesting.py/doc/backtesting/#backtestingpy-documentation)

pipを使って簡単にインストールできます。

```py
pip install backtesting
```

## バックテスト用データの準備

バックテストに必要な時系列の価格データを用意します。
公式ドキュメントより、次の条件を満たす必要があります。

- pandas DataFrame形式であること
- 'Open', 'High', 'Low', 'Close', 列を持っている事。（'Volume'はオプション）
- indexはpandasのDatetime形式であること。
- 他の列は存在していても問題なし。

[前回の記事](https://qiita.com/THERE2/items/f716565c884e7750c6c1) でデータを取得していれば、そのデータを使う事ができます。

以下ではcsvファイルを読み込んでpandasのDataFrameに格納していますが、csvファイルを読み込んだ場合は明示的に`pd.to_datetime(df.index)`でindex列をDatetime型に設定しておく必要があります。

```py
import datetime
import pandas as pd
df = pd.read_csv('./data/USD_JPY_201601-201908_M10.csv', index_col='Datetime')
df.index = pd.to_datetime(df.index)
```

## Custom Strategyの作成

各データポイントでbuy/sellの指示を出すためのクラスを作成します。
backtestingのStrategyクラスを継承して、init()とnext()メソッドを実装する必要があります。

- `init()` メソッド: 最初に呼び出されるので、効率化のためにテクニカルの指標等を事前に計算しておく事が推奨されています。
- `next()` メソッド: こちらがメインのメソッドで、Backtestingから各データポイント毎に呼び出されます。各データポイントに対するbuyやsellの行動は、次のデータポイントのOpen値に対して実行されます。

### 価格データの確認

Backtestingでは、テストデータの各行（データポイント）を順に呼び出していきますが、その時にnext()メソッドがコールされます。そのnext()メソッド内では、そのデータポイント以前の価格データを参照する事ができますが、そのデータポイントより未来の価格データは確認する事ができません。

つまり、next()メソッドの中においては、過去のデータから未来を予測する必要があります。

### 注文の指示

nextメソッドの中でbuyやsellの指示を出す事ができます。self.buy()、self.sell()をコールするだけです。引数で価格やロスカットポイントなどを指定できます。

- buy : 現在のポジションをクローズし、全力（予算全額）で買います。引数で購入価格（未設定の場合は成行）、損切値、利確値を指定できます。
- sell : buyの逆です。

他にもPositionのclose()メソッドでポジションをクローズする事もできます。

今回はサンプルとして単純にClose値がOpen値より高ければ買い、それ以外は売りというシンプルなCustom Strategyを作成してみました。

```py
from backtesting import Strategy
class myCustomStrategy(Strategy):
    def init(self):
        pass
        
    def next(self): 
        self.buy if self.data.Close[-1]> self.data.Open[-1] else self.sell()
```

ここで、`Close[-1]`や`Open[-1]`は、そのデータポイントの時点で一番最後の（最新の）Close値、Open値となります。
機械学習モデルでは、ここで価格データやその時点で手に入る様々なデータをモデルに渡して推論し、その結果に応じて売買を行うロジックを書くという事になるでしょう。
強化学習を使えば、強化学習から売買の指示を出す事も可能かと思います。

## バックテストの実行

データとstrategyが用意できたので、早速バックテストを実行してみましょう。
バックテストの実行は非常に簡単です。

Backtestクラスのインスタンスを作成して、run()を呼ぶだけです。
コンストラクタの引数には、テストで利用するデータ（df）と、上記で作成した`custom strategy`クラス、`cash`（予算）、`comission`（売買手数料）を指定します。

cashは10万円を、手数料はFXでは標準的な0.4銭に近い0.00004としました。

```py
from backtesting import Backtest
bt = Backtest(df[100000:], myCustomStrategy, cash=100000, commission=.00004)
bt.run()
```

実行が終わると、以下のような結果が表示されます。

```py

Start                     2018-09-06 13:10:00
End                       2019-08-01 03:50:00
Duration                    328 days 14:40:00
Exposure [%]                          97.9882
Equity Final [$]                       100906
Equity Peak [$]                        106208
Return [%]                           0.905664
Buy & Hold Return [%]                 1.76702
Max. Drawdown [%]                    -5.38639
Avg. Drawdown [%]                   -0.220275
Max. Drawdown Duration       56 days 20:10:00
Avg. Drawdown Duration        1 days 10:39:00
# Trades                                  228
Win Rate [%]                          48.2456
Best Trade [%]                        1.70577
Worst Trade [%]                      -1.19012
Avg. Trade [%]                     0.00302987
Max. Trade Duration           3 days 00:00:00
Avg. Trade Duration           1 days 09:54:00
Expectancy [%]                       0.270007
SQN                                  0.098817
Sharpe Ratio                       0.00847371
Sortino Ratio                       0.0141131
Calmar Ratio                      0.000562504
_strategy                    myCustomStrategy
dtype: object

```

実行結果で主要な値を簡単に解説します。

|項目|意味|
|-------------- |---------------------------------|
|Start, End | バックテスト対象期間の開始日、終了日 |
| Duration | バックテストの期間。ここでは328日分が対象となっていました。|
| Exposure[%] | 資産の変動率といった意味でしょうか。|
| Equity Final [$] | 最終的に資産がどうなったか。ここでは10万円が10万906円となった事を意味します。若干プラスとなっていますね |
| Equity Peak [$] | 一番儲かっていた時の資産額。ここでは10万6208円がピークでした。 |
| Return [%] | リターン率。これが一番需要ですね。0.9%の増でした | 
| Win Rate [%] | 取引の勝率です。48.2456%と五分を若干下回る戦績でした。|
| Best Trade [%] | 一番良い取引の率 |
| Worst Trade [%] | 一番悪かった取引の率 |
| Avg Trade [%] |  平均のリターン率 |

これらの情報を見ることで、アルゴリズムの性能の評価が可能となります。
また、`plot()`メソッドの呼び出しでいい感じのインタラクティブグラフを出力してくれます。

```py
bt.plot()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508567/d8d8adeb-890a-8284-83ee-db64943575ac.png)

APIのデータ取得、バックテストによるモデルの評価ができるようになりました。
次からは機械学習アルゴリズムを使ったFXを実装していきたいと思います。

@There2