Pythonによるバックテスト1

2023年7月5日FX,Python,株Backtesting,Python

Pythonによるバックテスト

Backtesting.py

Pythonでバックテストを行うためのライブラリです。MQLが苦手だけど、Pythonは少しできるといった方は、こちらを使うのが良いのではないでしょうか?機械学習を用いたトレード検証を行うのに有益なツールの1つです。

概要

ウェブサイトからの文言を抜粋します。

Does it seem like you had missed getting rich during the recent crypto craze? Fret not, the international financial markets continue their move rightwards every day. You still have your chance. But successful traders all agree emotions have no place in trading — if you are ever to enjoy a fortune attained by your trading, better first make sure your strategy or system is well-tested and working reliably to consistent profit. Mechanical or algorithmic trading, they call it. They’ll usually recommend signing up with a broker and trading on a demo account for a few months … But you know better. You know some programming.

最近の暗号ブームで一攫千金を逃したように思われますか?ご心配なく、国際金融市場は日々右肩上がりの動きを続けています。以前としてチャンスはあります。しかし、成功しているトレーダーは皆、トレーディングに感情は関係ないことに同意しています。もし、これまでトレーディングによって得た富を享受したことがあるなら、まず最初にした方が良いのは、あなたの戦略やシステムが十分にテストされ、安心して安定した利益が得られるかどうかです。機械的取引、もしくはアルゴリズム取引、人々をそう呼んでいます。普通はブローカーと契約し、数ヶ月間デモ口座で取引することを勧めます… しかし、あなたはもっと分別があります。プログラミングを知っています。

It is far better to foresee even without certainty than not to foresee at all.

確信がなくても予見することは、まったく予見しないよりはるかに良い。

— Henri Poincare

Backtesting.py is a Python framework for inferring viability of trading strategies on historical (past) data. Of course, past performance is not indicative of future results, but a strategy that proves itself resilient in a multitude of market conditions can, with a little luck, remain just as reliable in the future. Improved upon the vision of Backtrader, and by all means surpassingly comparable to other accessible alternatives, Backtesting.py is lightweight, fast, user-friendly, intuitive, interactive, intelligent and, hopefully, future-proof. It is also documented well, including a handful of tutorials.

Backtesting.pyは、過去のデータから取引戦略の実行可能性を推測するためのPythonフレームワークです。もちろん、過去のパフォーマンスは将来の結果を示すものではありませんが、多くの市場環境においてそれ自体が回復力を持つことを証明した戦略は、少しの運があれば、将来も同様に信頼性を保つことができます。Backtraderの構想に基づいて改良されたBacktesting.pyは、軽量で、高速で、ユーザーフレンドリーで、直感的で、インタラクティブで、インテリジェントで、うまくいけば、将来性もあります。また、チュートリアルを含め、ドキュメントも充実しています。

インストール

以下を実行するだけです。Windowsでも問題なく動きます。

pip install backtesting

使い方

基本的な使い方を解説します。以下のような単純な戦略を考えてみましょう。

期間の異なる単純移動平均(SMA)を考えます。Fast SMAは10日間移動平均、Slow SMAは20日間移動平均とします。

  • ゴールデンクロス: Fast SMAがSlow SMAをクロスしたらロング
  • デッドクロス:Slow SMAがFast SMAをクロスしたらショート

SMAの計算はライブラリで用意されていますが、カスタムインジケータを利用する場合を想定してpandasで計算を行います。

import pandas as pd

def SMA(data, period):
    return pd.Series(data).rolling(window=period).mean()

次に、backtestingライブラリから必要なモジュールを読み込みます。

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

class SmaCross(Strategy):
    n1 = 10
    n2 = 20
    def init(self):
        close = self.data.Close
        self.fast_sma = self.I(SMA, close, self.n1)
        self.slow_sma = self.I(SMA, close, self.n2)

    def next(self):
        if crossover(self.fast_sma, self.slow_sma):
            self.buy()
        elif crossover(self.slow_sma, self.fast_sma):
            self.sell()

戦略を作るときにStrategyクラスを継承したクラスを作成します。次にinit()で必要な機能を最初に定義します。StrategyのメソッドにI(インジケータ)があります。これは単なる値の配列ですが、next()で次の値が呼ばれるようになっています。

corssover関数によって2つSMAの値の大小関係をチェックすることができます。Fast SMAがslow SMAをクロスしたらself.buy()で実際にロングします。このメソッドはOrderを返します。これによって、移動平均による取引戦略を実装することができます。

なお、MQLだと以下のような形で結構長くなります。

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>  


input int ShortTerm_MA_Period = 10; 
input int LongTerm_MA_Period = 20;
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1;
input ENUM_MA_METHOD ma_method=MODE_SMA;
input ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE;
input double Lots = 1.0;

int Slow_MA_Handle;
int Fast_MA_Handle;
double Short_MA_Array[];
double Long_MA_Array[];

CTrade m_trade;
CSymbolInfo m_symbol;

void OnInit() 
{
    Fast_MA_Handle = iMA(m_symbol.Name(), Timeframe, ShortTerm_MA_Period, 0, ma_method, applied_price);
    Slow_MA_Handle = iMA(m_symbol.Name(), Timeframe, LongTerm_MA_Period, 0, ma_method, applied_price);
}

void OnTick() 
{
    int bar = Bars(Symbol(), Timeframe);
    ArrayResize(Short_MA_Array, bar);
    ArrayResize(Long_MA_Array, bar);
    CopyBuffer(Short_MA_Handle, 0, 0, bar, Fast_MA_Handle);
    CopyBuffer(Long_MA_Handle, 0, 0, bar, Slow_MA_Handle);

    // 新しいバーが開始したら取引戦略を実行する
    if (Volume[0] == 1) 
    {
        if (Short_MA_Array[1] < Long_MA_Array[1] && Short_MA_Array[0] > Long_MA_Array[0]) 
        {
            // ゴールデンクロスが発生したら、BUY注文を出す
            m_trade.Buy(Lots, m_symbol.Name(), m_symbol.Ask(), 0, 0);
        } 
        else if (Short_MA_Array[1] > Long_MA_Array[1] && Short_MA_Array[0] < Long_MA_Array[0]) 
        {
            // デッドクロスが発生したら、SELL注文を出す
            m_trade.Buy(Lots, m_symbol.Name(), m_symbol.Bid(), 0, 0);
        }
    }
}

※ 実際には#include <Expert> からCExpert ExtExpert;を使った方が良いと思います。とはいえ、PythonよりもMQLの方がトレードに特化した機能があるので、どちらが良いかは一概には言えません。特に、機械学習を利用する場合はPythonの方が圧倒的に楽です(MQLの記事を見ると機械学習はできそうですが、結構面倒そうでした)。

上記のように定義した戦略クラスを利用して、バックテストを実行します。ここではGoogleのデータを利用します。

from backtesting.test import GOOG

GOOG
Date Open High Low Close Volume
2004-08-19 100.00 104.06 95.96 100.34 22351900
2004-08-20 101.01 109.08 100.50 108.31 11428600
2004-08-23 110.75 113.48 109.05 109.40 9137200
2004-08-24 111.24 111.60 103.57 104.87 7631300
2004-08-25 104.96 108.00 103.88 106.00 4598900
… … … … … …
2013-02-25 802.30 808.41 790.49 790.77 2303900
2013-02-26 795.00 795.95 784.40 790.13 2202500
2013-02-27 794.80 804.75 791.11 799.78 2026100
2013-02-28 801.10 806.99 801.03 801.20 2265800
2013-03-01 797.80 807.14 796.15 806.19 2175400
bt = Backtest(GOOG, SmaCross, cash=10000, commission=.002, exclusive_orders=True)
output = bt.run()
bt.plot()

結果

チャート

my plot1

バックテストの結果

Start2004-08-19 00:00:00
End2013-03-01 00:00:00
Duration3116 days 00:00:00
Exposure Time [%]97.067039
Equity Final [$]68221.96986
Equity Peak [$]68991.21986
Return [%]582.219699
Buy & Hold Return [%]703.458242
Return (Ann.) [%]25.266427
Volatility (Ann.) [%]38.383008
Sharpe Ratio0.658271
Sortino Ratio1.288779
Calmar Ratio0.763748
Max. Drawdown [%]-33.082172
Avg. Drawdown [%]-5.581506
Max. Drawdown Duration688 days 00:00:00
Avg. Drawdown Duration41 days 00:00:00
# Trades94
Win Rate [%]54.255319
Best Trade [%]57.11931
Worst Trade [%]-16.629898
Avg. Trade [%]2.074326
Max. Trade Duration121 days 00:00:00
Avg. Trade Duration33 days 00:00:00
Profit Factor2.190805
Expectancy [%]2.606294
SQN1.990216
_strategySmaCross
_equity_curveEquity DrawdownPct DrawdownD…
_tradesSize EntryBar ExitBar EntryPrice ExitP..

Googleの株価に対しては移動平均を使った取引戦略はバイアンドホールドよりも悪いことから良い戦略とは言えないことがわかります。とはいえ、シャープレシオが0.658271とプラスで、マイナスになっていないことから一部の期間ではワークするのかもしれません。

他にも様々な機能があるようなので、サンプルコードを実行した結果などを載せていけたらと思います。