Python Backtraderで米国株/ETFのバックテストをはじめてみる

『 _Backtrader_ 』というバックテスト/システムトレード向けのPythonフレームワークが気になったので触れてみることに。
本記事では、環境構築～簡易サンプルコード実行までを(備忘録用に)まとめました。

私の環境は下記の通りです。

```python:
Windows 10
Python 3.10.0
Jupyter Notebook
```
# 準備
本記事のサンプルコードを動かすためのライブラリをインストールしていきます。

>[Backtrader Documentation](https://www.backtrader.com/)のQuickstart Guideのサンプルコードを一通り動かす場合は、[backtrader githubページ](https://github.com/mementum/backtrader)記載の手順に従えばOKです。
Download ZIPで『datas』フォルダのデータをローカルに持ってくることもお忘れなく。

### backtrader
このフレームワークを利用することによって、バックテスト/トレーディングのためのインフラ構築を簡単に行うことができます。
端末で下記いずれかのコマンドを実行し、インストールします。
```python:
pip install backtrader
pip install backtrader[plotting] # matplotlib　未インストールの場合
```
>plotするには _matplotlib_ のバージョンは最低 _1.4.1_ は必要みたいです。`

### yfinance
Yahoo!Financeからマーケット情報をダウンロードするために使用します。
[yfinance githubページ](https://github.com/ranaroussi/yfinance)にも記載されている通り、Yahoo, Inc.公認のAPIではないため、注意して扱う必要がありそうです。
```python:
pip install yfinance
```
>_yfinence_ を使用するためには、他に _Pandas_ , _Numpy_ , _requests_ , _lxml_　が必要になります。
各ライブラリの必要なバージョン等については、上記リンクよりご確認ください。




# 動作確認
今回はできるだけシンプルに、コードが機能するかを確かめます。
従って、取引戦略を凝ったり、手数料を考慮する等、細かいロジックは実装しません。

### データ取得
まずは _yfinance_ を使ってデータを取りに行きましょう。
今回の対象は米国ETFの _VTI_ とします。
```python:
import yfinance as yf
data = yf.download('VTI', start='2022-04-01', end='2022-05-01')
data

''' 実行結果 -> '''
	Open	High	Low	Close	Adj Close	Volume
Date						
2022-03-31	230.679993	231.320007	227.610001	227.669998	227.669998	4087000
2022-04-01	228.660004	228.750000	226.630005	228.580002	228.580002	3455900
2022-04-04	228.860001	230.539993	228.380005	230.520004	230.520004	3241100
...	...	...	...	...	...	...
2022-04-27	209.850006	212.399994	208.550003	209.660004	209.660004	4955900
2022-04-28	211.880005	215.750000	209.449997	214.770004	214.770004	3767700
2022-04-29	212.899994	214.309998	206.710007	206.880005	206.880005	7326200
```
日毎の始値や終値などの情報が入っていますね。
ちなみに、データの型はDataFrameです。
```python:
type(data)

''' 実行結果 -> '''
pandas.core.frame.DataFrame
```



### Backtraderについて
サンプル全体を動かす前に、backtraderの基礎を一度おさえておきます。
基本的な構成は以下のようになります。
<dl>

  <dt>1. Strategyの構築</dt>
  <dd> 調整するパラメータを決定したうえで、マーケットへのEntry/Exitのロジックを記述します。 必要となる指標があればStrategy内でインスタンス化します。
</dd>

<dt>2. Cerebro Engineの作成</dt>
  <dd> データ入力やStrategyの実行、データ監視をまとめて行ってくれます。バックテスト/データ供給/トレーディングの核を担う部分になります。</dd>

<dt>3. Cerebro Engineの実行</dt>
  <dd> データのロードと入力を行い(cerebro.adddata)、実行(cerebro.run())します。cerebro.plot()で結果を図示できます。 </dd>
</dl>

```python:基本構成
import backtrader as bt

# Strategyの構築
class MyStrategy(bt.Strategy):
   ''' 処理を記載 '''

# Cerebro Engineの作成
cerebro = bt.Cerebro()

# データロード/入力
data = ''' データを取ってくる '''
cerebro.adddata(data)

# 実行
cerebro.run()
```
自作したMyStrategyクラスはbt.Strategyクラスを継承しているため、特定のメソッドを記述するだけで、Cerebro Engineの方で良い感じに実行してくれます。

### サンプルコードの実行
それでは、_VTI_ のData FeedをCerebro Engineに渡して、動かしてみましょう。

<details><summary>サンプルコード</summary>

```python:サンプルコード全体
import backtrader as bt
import yfinance as yf

class MyStrategy(bt.Strategy):

    # 実行記録
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    # コンストラクタ
    def __init__(self):
        
        self.dataclose = self.datas[0].close

        self.order = None

    # 処理通知部
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return

        # 取引内容を通知
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('BUY EXECUTED, %.2f' % order.executed.price)
            elif order.issell():
                self.log('SELL EXECUTED, %.2f' % order.executed.price)

            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

        self.order = None

    # 売買処理部
    def next(self):
        
        self.log('Close, %.2f' % self.dataclose[0])

        if self.order:
            return

        # マーケットにいるかチェック
        if not self.position:

            # 3セクション連続下落で購入
            if self.dataclose[0] < self.dataclose[-1]:
                if self.dataclose[-1] < self.dataclose[-2]:
                    
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
                    self.order = self.buy()

        else:

            # 5セクション経過で良かれ悪かれ売却
            if len(self) >= (self.bar_executed + 5):
                
                self.log('SELL CREATE, %.2f' % self.dataclose[0])
                self.order = self.sell()
                

# ETF:VTIのデータをダウンロード
yf_data = yf.download('VTI', start='2022-04-01', end='2022-05-01')
# データをCerebro Engine用のフォーマットに変換
data = bt.feeds.PandasData(dataname=yf_data)

# Cerebro Engineの作成
cerebro = bt.Cerebro()
# Strategyの読み込み
cerebro.addstrategy(MyStrategy)
# データ入力
cerebro.adddata(data)

# ターゲット資産の設定
cerebro.broker.setcash(400)

# 実行
cerebro.run()

# 最終結果を表示
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
```

</details>


<details><summary>実行結果</summary>

```python:実行結果
[*********************100%***********************]  1 of 1 completed
2022-03-31, Close, 227.67
2022-04-01, Close, 228.58
2022-04-04, Close, 230.52
2022-04-05, Close, 227.15
2022-04-06, Close, 224.54
2022-04-06, BUY CREATE, 224.54
2022-04-07, BUY EXECUTED, 224.20
2022-04-07, Close, 225.39
2022-04-08, Close, 224.69
2022-04-11, Close, 221.18
2022-04-12, Close, 220.50
2022-04-13, Close, 223.39
2022-04-14, Close, 220.67
2022-04-14, SELL CREATE, 220.67
2022-04-18, SELL EXECUTED, 220.41
2022-04-18, Close, 220.29
2022-04-18, BUY CREATE, 220.29
2022-04-19, BUY EXECUTED, 220.23
2022-04-19, Close, 224.17
2022-04-20, Close, 223.88
2022-04-21, Close, 220.22
2022-04-22, Close, 214.22
2022-04-25, Close, 215.39
2022-04-26, Close, 209.25
2022-04-26, SELL CREATE, 209.25
2022-04-27, SELL EXECUTED, 209.85
2022-04-27, Close, 209.66
2022-04-28, Close, 214.77
2022-04-29, Close, 206.88
Final Portfolio Value: 385.83
```

</details>

Strategyの売買ロジックとしては、
3セクション連続で基準価格(終値)が下落した場合に買い注文(成行)を実施し、
5セクション経過で売り注文を実施しています。
ただし、まだマーケットに出ていない場合は、買い注文のみの実行となっています。
注意点として、売買の判断材料は対象銘柄の終値となっているため、売買が実行されるのは **翌営業日** になっています。
```python:一例
2022-04-26, SELL CREATE, 209.25
2022-04-27, SELL EXECUTED, 209.85
```



ターゲット資産400.00 [dol] に対し、このStrategyを適用すると、資産が385.83 [dol]　まで減ってしまいました。
利益を上げるには、Strategyをしっかり練る必要がありそうです。


今回の目的である「 _backtrader_ の動作確認」は達成しましたので、本記事の内容としては以上になります。