FX自動売買(EA)の作り方Python

PythonでFX自動売買ツールのバックテストを行う方法

※アフィリエイト広告を利用しています

広告

FX自動売買ツール(EA)のバックテストには複数の方法が存在します。MT4やMT5を使うものや、ティックデータを入手してPythonなどで独自にデータ処理する方法などです。

ここでは、Google Colabを用いて、Pythonでバックテストを行う方法をご紹介します。

  • Goolge ColabはPython実行環境を整える面倒な準備は不要で、すぐにPythonが使える。
  • Pythonを使えばMT4やMT5のバックテストでは確認することが難しかった細かい指標やグラフによる視覚化も実装可能で、オリジナルのバックテストツールを作成できる。
  • Pythonは機械学習に必要なパッケージも充実していて、機械学習を取り入れたFX自動売買ツールのバックテストも行うことが出来る。

    以下の記事ではnumbaを用いて高速化したPythonによるバックテストをご紹介しましたが、今回はそれをさらに拡張し、テクニカル指標も反映して、より実践的なバックテストを行います。

    一度こちらの記事をお読みいただいてから進んでいただけるとスムーズです。

    広告

    各種初期設定

    Google Driveにマウント

    まず、Google Driveにマウントしておきます。

    from google.colab import drive
    drive.mount('/content/drive')

    これで、ColabからGoogle Drive内のファイルを読み込んだり、Google Drive内にファイルを出力したりすることが可能になります。

    Ta-Libのインストール

    Ta-Libとは、テクニカル指標を計算するためのライブラリです。ColabではTa-Libがそのまま使える状態ではないので、インストール処理が必要です。以下のコードを実行します。

    !curl -L http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz -O && tar xzvf ta-lib-0.4.0-src.tar.gz
    !cd ta-lib && ./configure --prefix=/usr && make && make install && cd - && pip install ta-lib

    少し時間がかかりますが、最後に「Successfully installed ta-lib-0.4.25」のような表示が出ればインストール成功です。

    Google ColabのPythonのバージョンがアップグレードされた関係で、「ImportError: numpy.core.multiarray failed to import」というエラーが出るようになってしまいました。この記事に記載の方法では、Ta-Lib使用部分は上手くいかない可能性が高いです。代替策としてpandas-taを用いたバックテスト方法は以下の記事でご紹介しています。

    各種ライブラリのインポート

    各種ライブラリをインポートします。

    import pandas as pd
    import numpy as np
    from datetime import datetime, timedelta
    import talib
    import numba
    from tqdm.notebook import tqdm
    import os
    

    Ta-Lib以外は、Colabに備わっているので、インストール処理は不要で上記コードのみでそのままインポート可能になっています。

    ティックデータにテクニカル指標を追加

    使用するティックデータは、前回と同じMT5からダウンロードしたGOLD#(XMTrading KIWAMI極口座のゴールド)の2022年12月分です。

    ティックデータを準備

    まず、前回の記事を参考に、以下のようなdfを用意します。(前回の記事で作成したものです)

    テクニカル指標の追加

    さらに、以下の記事を参考に、ティックデータにテクニカル指標を追加します。

    すると、以下のようなdfが作成出来ます。

    追加したテクニカル指標を簡単に解説しておきます。

    • MA_M1:1分足の単純移動平均線
    • DMI_flg:「+DI > -DIであれば、1」、「+DI < -DIであれば、-1」というフラグ。それぞれ、1分足、5分足、15分足、1時間足
    • ATR_H1:1時間足のATR

    このdfをGoogle Driveのマイドライブ直下のbacktestフォルダに、「GOLD#_tick_202212.pkl」というファイル名で保存しておきます。

      取引履歴のdfを用意

      以下の通り、取引履歴を格納するためのdf_ordersを用意しておきます。こちらは前回の記事と同じです。

      cols = [
             'time',
             'bid',
             'ask',
             'type', # 注文タイプ
             'profit', # 実現損益
             'buy_position', # buyポジション数
             'buy_lot', # buyポジションのlot数合計
             'buy_price', # buyポジションの平均価格
             'buy_profit', # buyポジションの含み損益
             'sell_position', # sellポジション数
             'sell_lot', # sellポジションのlot数合計
             'sell_price', # sellポジションの平均価格
             'sell_profit' # sellポジションの含み損益
             ]
      df_orders = pd.DataFrame(index=[], columns=cols)

      パラメーター設定

      ここでパラメーターを設定しておきます。

      first_lot = 0.01 # 初期ロット
      pip_value = 135 # ピップ値
      point = 0.01 # 価格の最小単位
      spread_limit = 15 # 許容スプレッド
      position_limit = 5 # 最大ポジション数
      martin_factor = 2.0 # マーチン倍率
      entry_range = 60 # エントリー幅
      lower_ATR = 2.8 # エントリーATR下限
      upper_ATR = 10.0 # エントリーATR上限
      

      前回の記事から、ナンピン幅、利確幅、ロスカット幅を削除して、エントリー幅、エントリーATR下限、エントリーATR上限の3つを追加しています。

      取引期間の設定

      取引期間も設定しておきます。

      date_index = pd.date_range("2022-12-01", periods=31, freq="D")
      AC_date_list = date_index.to_series().dt.strftime("%Y-%m-%d")
      AC_date_list0 = AC_date_list[:5]
      AC_date_list[-1]

      ここで出来上がるAC_date_listというのは、単純に2022-12-01から2022-12-31までの日付の文字列を格納したのみのリストです。後ほどループ処理する際に使います。

      EAのロジック部分(calc_orders)

      続いて、EAのロジック部分です。ここが肝心なところではあります。結構長いのでパートごとに簡単に解説します。

      インプットの設定

      まずはインプットの設定です。MA、DMI、ATRといったテクニカル指標もインプットに加えています。

      @numba.njit
      def calc_orders(
                      first_lot,
                      current_buy_lot,
                      current_buy_price,
                      current_sell_lot,
                      current_sell_price,
                      buy_position,
                      buy_lot,
                      buy_price,
                      buy_profit,
                      sell_position,
                      sell_lot,
                      sell_price,
                      sell_profit,
                      point,
                      spread_limit,
                      tick_len,
                      tick_bid,
                      tick_ask,
                      entry_range,
                      current_buy_ATR,
                      current_sell_ATR,
                      lower_ATR,
                      upper_ATR,
                      tick_MA_M1,
                      tick_DMI_M1_flg,
                      tick_DMI_M5_flg,
                      tick_DMI_M15_flg,
                      tick_DMI_H1_flg,
                      tick_ATR_H1,    
                              ):
          
          orders_time = tick_bid.copy()
          orders_time[:] = np.nan
      
          orders_bid = tick_bid.copy()
          orders_bid[:] = np.nan
      
          orders_ask = tick_bid.copy()
          orders_ask[:] = np.nan
      
          orders_type = tick_bid.copy()
          orders_type[:] = np.nan
      
          orders_buy_position = tick_bid.copy()
          orders_buy_position[:] = np.nan
      
          orders_sell_position = tick_bid.copy()
          orders_sell_position[:] = np.nan
      
          orders_profit = tick_bid.copy()
          orders_profit[:] = np.nan
      
          orders_buy_profit = tick_bid.copy()
          orders_buy_profit[:] = np.nan
      
          orders_sell_profit = tick_bid.copy()
          orders_sell_profit[:] = np.nan
      
          orders_buy_lot = tick_bid.copy()
          orders_buy_lot[:] = np.nan
      
          orders_sell_lot = tick_bid.copy()
          orders_sell_lot[:] = np.nan
      
          orders_buy_price = tick_bid.copy()
          orders_buy_price[:] = np.nan
      
          orders_sell_price = tick_bid.copy()
          orders_sell_price[:] = np.nan

      ループ開始〜テクニカル指標反映

      if tick_len > 0:
            
              for i in range(tick_len):
      
                  ##現在時刻、価格の取得
                  bid = tick_bid[i]
                  ask = tick_ask[i]
                  spread = ask - bid
      
                  #flg判定
                  MA_M1 = tick_MA_M1[i]
                  DMI_M1_flg = tick_DMI_M1_flg[i]
                  DMI_M5_flg = tick_DMI_M5_flg[i]
                  DMI_M15_flg = tick_DMI_M15_flg[i]
                  DMI_H1_flg = tick_DMI_H1_flg[i]
                  ATR_H1 = tick_ATR_H1[i]
      
                 ###entry_flgロジック
                  if MA_M1+spread-entry_range*point > ask and spread<spread_limit*point:
                      entry_flg = 1
                  elif MA_M1+entry_range*point < bid and spread<spread_limit*point:
                      entry_flg = -1
                  else:
                      entry_flg = 0
                  
                  if lower_ATR <= ATR_H1 and ATR_H1 < upper_ATR:
                      ATR_flg = 1
                  else:
                      ATR_flg = 0
                  
                  if DMI_M1_flg==1 and DMI_M5_flg==1 and DMI_M15_flg==1 and DMI_H1_flg==1:
                      DMI_flg = 1
                  elif DMI_M1_flg==-1 and DMI_M5_flg==-1 and DMI_M15_flg==-1 and DMI_H1_flg==-1:
                      DMI_flg = -1
                  else:
                      DMI_flg = 0
                  
                  if entry_flg==1 and ATR_flg==1 and DMI_flg==1:
                      order_flg=1
                  elif entry_flg==-1 and ATR_flg==1 and DMI_flg==-1:
                      order_flg=-1
                  else:
                      order_flg=0

      ここでエントリー条件のロジックを入れています。

      • 現在地がMA_M1よりentry_rangeだけ乖離していて、
      • ATR_H1が指定の範囲内で、
      • 1分足〜1時間足までのDMI_flgが全て揃ったら、
      • buy or sellのフラグが立つ

        というようなロジックです。

        ポジションの確認

        ポジションを確認して含み損益を計算しています。

        ##ポジションの含み損益の確認
                    if buy_lot == 0:
                        buy_profit = 0
                    else:
                        buy_profit = round((bid - buy_price) * buy_lot * pip_value / 0.01, 0)
        
                    if sell_lot == 0:
                        sell_profit = 0
                    else:
                        sell_profit = round(-(ask - sell_price) * sell_lot * pip_value / 0.01, 0)
                    
                    ##ordersに出力
                    orders_bid[i] = bid
                    orders_ask[i] = ask
                    orders_buy_position[i] = buy_position
                    orders_buy_lot[i] = buy_lot
                    orders_buy_price[i] = buy_price
                    orders_buy_profit[i] = buy_profit
                    orders_sell_position[i] = sell_position
                    orders_sell_lot[i] = sell_lot
                    orders_sell_price[i] = sell_price
                    orders_sell_profit[i] = sell_profit

        それをordersに出力しておきます。後ほど、DD(ドローダウン)の計算に使えます。

        新規エントリー注文

        新規エントリー注文です。order_flgが1または-1かどうかで、buyエントリーまたはsellエントリーが発生します。

        ##新規buyエントリー
                    if buy_position == 0 and order_flg == 1:
                        buy_position = 1 # buyポジション数
                        current_buy_lot = first_lot # 最新のbuyポジションのlot数
                        buy_lot = first_lot # buyポジションのlot数合計
                        current_buy_price = ask # 最新のbuyポジション価格
                        buy_price = ask # buyポジションの平均価格
                        current_buy_ATR =ATR_H1
        
                        ##ordersに出力
                        orders_type[i] = 1
                        orders_buy_position[i] = buy_position
                        orders_profit[i] = 0
                        orders_buy_lot[i] = current_buy_lot
                        orders_buy_price[i] = current_buy_price
        
                    
                    ##新規sellエントリー
                    if sell_position == 0 and order_flg == -1:
                        sell_position = 1 # sellポジション数
                        current_sell_lot = first_lot # 最新のsellポジションのlot数
                        sell_lot = first_lot # sellポジションのlot数合計
                        current_sell_price = bid # 最新のsellポジション価格
                        sell_price = bid # sellポジションの平均価格
                        current_sell_ATR =ATR_H1
        
                        ##ordersに出力
                        orders_type[i] = 2
                        orders_sell_position[i] = sell_position
                        orders_profit[i] = 0
                        orders_sell_lot[i] = current_sell_lot
                        orders_sell_price[i] = current_sell_price

        また、その注文情報をordersに出力しておきます。

        追加エントリー(ナンピン)注文

        ナンピン注文です。ここはナンピン幅を一つ前にポジションを持った時のATR(1時間足)という設定にしています。

        ##追加buyエントリー
                    if (
                        buy_position > 0 and 
                        buy_position < position_limit and
                        order_flg == 1 and
                        ask < current_buy_price - current_buy_ATR ): buy_position += 1 # buyポジション数 x = buy_lot * buy_price # 平均価格算出用 current_buy_lot = round(current_buy_lot * martin_factor + 0.001, 2) # 最新のbuyポジションのlot数 buy_lot += current_buy_lot # buyポジションのlot数合計 current_buy_price = ask # 最新のbuyポジション価格 y = current_buy_lot * current_buy_price # 平均価格算出用 buy_price = round(( x + y ) / buy_lot, 2) # buyポジションの平均価格 current_buy_ATR =ATR_H1 ##ordersに出力 orders_type[i] = 3 orders_buy_position[i] = buy_position orders_profit[i] = 0 orders_buy_lot[i] = current_buy_lot orders_buy_price[i] = current_buy_price ##追加sellエントリー if ( sell_position > 0 and 
                        sell_position < position_limit and order_flg == -1 and bid > current_sell_price + current_sell_ATR
                        ):
        
                        sell_position += 1 # sellポジション数
                        x = sell_lot * sell_price # 平均価格算出用
                        current_sell_lot = round(current_sell_lot * martin_factor + 0.001, 2) # 最新のsellポジションのlot数
                        sell_lot += current_sell_lot # sellポジションのlot数合計
                        current_sell_price = bid # 最新のsellポジション価格
                        y = current_sell_lot * current_sell_price # 平均価格算出用
                        sell_price = round(( x + y ) / sell_lot, 2) # sellポジションの平均価格
                        current_sell_ATR =ATR_H1
        
                        ##ordersに出力
                        orders_type[i] = 4
                        orders_sell_position[i] = sell_position
                        orders_profit[i] = 0
                        orders_sell_lot[i] = current_sell_lot
                        orders_sell_price[i] = current_sell_price

        クローズ注文(利確)

        ポジションクローズ注文です。利確条件は、ポジションの平均価格がATR(1時間足)だけの利確幅に達したら、です。

        ##buyクローズtp
                    if buy_position >= 1 and bid > buy_price + current_buy_ATR:
                        
                        ##ordersに出力
                        orders_type[i] = 5
                        orders_profit[i] = buy_profit
                        orders_buy_profit[i] = 0
        
        
                        buy_position = 0 # buyポジション数の初期化
                        buy_profit = 0 # buy_profitの初期化
                        current_buy_lot = 0 # 最新のbuyポジションのlot数の初期化
                        buy_lot = 0 # buyポジションのlot数合計の初期化
                        current_buy_price = 0 # 最新のbuyポジション価格の初期化
                        buy_price = 0 # buyポジションの平均価格の初期化
        
                    ##sellクローズtp
                    if sell_position >= 1 and ask < sell_price - current_sell_ATR:
                        
                        ##ordersに出力
                        orders_type[i] = 6
                        orders_profit[i] = sell_profit
                        orders_sell_profit[i] = 0
        
                        
                        sell_position = 0 # sellポジション数の初期化
                        sell_profit = 0 # sell_profitの初期化
                        current_sell_lot = 0 # 最新のsellポジションのlot数の初期化
                        sell_lot = 0 # sellポジションのlot数合計の初期化
                        current_sell_price = 0 # 最新のsellポジション価格の初期化
                        sell_price = 0 # sellポジションの平均価格の初期化

        クローズ注文(損切り)

        今度は損切りの注文です。損切り条件は、最終ポジションの価格よりもATR(1時間足)だけさらに動いたら、です。

        ##buyクローズlc
                    if buy_position >= position_limit and bid < current_buy_price - current_buy_ATR: ##ordersに出力 orders_type[i] = 7 orders_profit[i] = buy_profit orders_buy_profit[i] = 0 buy_position = 0 # buyポジション数の初期化 buy_profit = 0 # buy_profitの初期化 current_buy_lot = 0 # 最新のbuyポジションのlot数の初期化 buy_lot = 0 # buyポジションのlot数合計の初期化 current_buy_price = 0 # 最新のbuyポジション価格の初期化 buy_price = 0 # buyポジションの平均価格の初期化 ##sellクローズlc if sell_position >= position_limit and ask > current_sell_price + current_sell_ATR:
                        
                        ##ordersに出力
                        orders_type[i] = 8
                        orders_profit[i] = sell_profit
                        orders_sell_profit[i] = 0
        
                        
                        sell_position = 0 # sellポジション数の初期化
                        sell_profit = 0 # sell_profitの初期化
                        current_sell_lot = 0 # 最新のsellポジションのlot数の初期化
                        sell_lot = 0 # sellポジションのlot数合計の初期化
                        current_sell_price = 0 # 最新のsellポジション価格の初期化
                        sell_price = 0 # sellポジションの平均価格の初期化

        アウトプット

        最後にアウトプットです。以下の項目を出力する関数です。

        return (orders_bid,
                           orders_ask,
                           orders_type,
                           orders_profit,
                           orders_buy_position,
                           orders_buy_lot,
                           orders_buy_price,
                           orders_buy_profit,
                           orders_sell_position,
                           orders_sell_lot,
                           orders_sell_price,
                           orders_sell_profit,
                           current_buy_lot,
                           current_buy_price,
                           current_sell_lot,
                           current_sell_price,
                           buy_position,
                           buy_lot,
                           buy_price,
                           buy_profit,
                           sell_position,
                           sell_lot,
                           sell_price,
                           sell_profit,
                           current_buy_ATR,
                           current_sell_ATR
                           )

        ループ処理

        上記のcalc_orders関数を用いて、orders(取引履歴)をdf_ordersというハコに納めていきます。

        current_buy_lot=0.00
        current_buy_price=0.00
        current_sell_lot=0.00
        current_sell_price=0.00
        buy_position=0
        buy_lot=0.00
        buy_price=0.00
        buy_profit=0
        sell_position=0
        sell_lot=0.00
        sell_price=0.00
        sell_profit=0
        current_buy_ATR=0.00
        current_sell_ATR=0.00
        
        
        for AC_date in tqdm(AC_date_list):
            
            YYYYMM = AC_date[:4] + AC_date[5:7]
        
            df = pd.read_pickle('/content/drive/My Drive/backtest/tick/GOLD#_tick_'+YYYYMM+'.pkl')
        
            df = df[AC_date + ' 00:00:00' : AC_date + ' 23:59:59' ]
            df_tick = df.reset_index()
            
            if(len(df_tick)>0):
                
                df = pd.DataFrame(index=[], columns=cols)
        
                (
                 df['bid'], df['ask'], df['type'], df['profit'],
                 df['buy_position'], df['buy_lot'], df['buy_price'], df['buy_profit'],
                 df['sell_position'], df['sell_lot'], df['sell_price'], df['sell_profit'],
                 current_buy_lot, current_buy_price,
                 current_sell_lot, current_sell_price,
                 buy_position, buy_lot, buy_price, buy_profit,
                 sell_position, sell_lot, sell_price, sell_profit,
                 current_buy_ATR, current_sell_ATR
                 )= calc_orders(
                    first_lot=first_lot,
                    current_buy_lot=current_buy_lot,
                    current_buy_price=current_buy_price,
                    current_sell_lot=current_sell_lot,
                    current_sell_price=current_sell_price,
                    buy_position=buy_position,
                    buy_lot=buy_lot,
                    buy_price=buy_price,
                    buy_profit=buy_profit,
                    sell_position=sell_position,
                    sell_lot=sell_lot,
                    sell_price=sell_price,
                    sell_profit=sell_profit,
                    point=point,
                    spread_limit=spread_limit,
                    entry_range=entry_range,
                    lower_ATR=lower_ATR,
                    upper_ATR=upper_ATR,
                    current_buy_ATR=current_buy_ATR,
                    current_sell_ATR=current_sell_ATR,
                    tick_len=len(df_tick),
                    tick_bid=df_tick['bid'].values,
                    tick_ask=df_tick['ask'].values,
                    tick_MA_M1=df_tick['MA_M1'].values,
                    tick_DMI_M1_flg=df_tick['DMI_M1_flg'].values,
                    tick_DMI_M5_flg=df_tick['DMI_M5_flg'].values,
                    tick_DMI_M15_flg=df_tick['DMI_M15_flg'].values,
                    tick_DMI_H1_flg=df_tick['DMI_H1_flg'].values,
                    tick_ATR_H1=df_tick['ATR_H1'].values,
                    )
                 
                df['time'] = df_tick['time']
                df['spread'] = df_tick['spread']
                df['pip_value'] = pip_value
        
                #df = df.dropna( )
                df.to_pickle('/content/drive/My Drive/backtest/outputs/'+outputs+'/df_orders_'+AC_date+'.pkl')

        取引期間

        以下の通り、AC_data_listの日数分だけ繰り返す処理です。今回は先ほど指定した通り、2022-12-01から2022-12-31までの31日分だけループします。

        for AC_date in tqdm(AC_date_list):

        tqdm()というのは、Pythonでfor文の処理状況を確認するのに役立つライブラリです。以下のようにループ処理の進捗状況を表示してくれます。

        ティックデータの読み込み

        ここは、GOLD#のティックデータが「GOLD#_tick_YYYYMM.pkl」という形でbactestフォルダに年月毎に保存されている前提のコードです。

        YYYYMM = AC_date[:4] + AC_date[5:7]
        df = pd.read_pickle('/content/drive/My Drive/backtest/GOLD#_tick_'+YYYYMM+'.pkl')
        df = df[AC_date + ' 00:00:00' : AC_date + ' 23:59:59' ]
        df_tick = df.reset_index()

        AC_date=2022-12-01であれば、YYYYMM=202212となり、2022年12月のティックデータを読みに行きます。さらに、2022年12月1日だけのティックデータに絞り込んでから、処理を始めます。

        if(len(df_tick)>0):

        これは、絞り込んだ後のdf_tickにレコードが存在しているかの確認です。土日だとティックデータも存在しないので、その日はスキップするような処理です。

        calc_orders関数部分

        初期パラメータ情報やポジション情報およびティックデータをインプット、各取引履歴の情報をアウトプットとして、df_ordersを作っていきます。このcalc_orders関数部分ではpandasのdfは直接使わずに、@numba.njitで高速化しています。これをするのとしないのとでは、かなり処理速度が変わってくるかと思います。

        ちなみに何故ポジション情報(current_buy_lot等)をインプットとアウトプットにしているかですが、これはポジション持ち越しに対応するためです。この処理は1日毎にループ処理を進めて行きますが、その日の終わりのポジション情報をアウトプットしておいて、次の日の始まりにインプットしてポジション情報が継続するような工夫を入れています。

        #df = df.dropna( )

        最後の行の一つ前に、#df = df.dropna( )というコードを入れています。#が入っているため有効化されていませんが、これを有効化すると、取引を行なったタイミングのレコードだけを取り出したデータになります。

        これを有効化していないと、ティックデータ全てに対して、取引情報が付加されたデータになります。取引が行われていないレコードは該当箇所がNaNになります。

        つまり、

        • 含み損(DD)を正確に捉える場合には、#df = df.dropna( )としておいた方がよい。
        • データのサイズを大きくしたくない場合は、df = df.dropna( )を有効化した方がよい。

        ということになります。

        dr_ordersの統合

        さて、これまでの処理を行えば、Google Drive内に以下のようなファイルが出来上がっているはずです。

        これを統合する処理を行います。

        for AC_date in AC_date_list0:
            file_exists = os.path.exists('/content/drive/My Drive/backtest/outputs/df_orders_'+AC_date+'.pkl')
            if file_exists:
                df = pd.read_pickle('/content/drive/My Drive/backtest/outputs/df_orders_'+AC_date+'.pkl')
                df_orders = df.iloc[0:0]
        
        for AC_date in AC_date_list:
            file_exists = os.path.exists('/content/drive/My Drive/backtest/outputs/df_orders_'+AC_date+'.pkl')
            if file_exists:
                df = pd.read_pickle('/content/drive/My Drive/backtest/outputs/df_orders_'+AC_date+'.pkl')
                df_orders = df_orders.append(df)
        
        df_orders.to_pickle('/content/drive/My Drive/backtest/outputs/df_orders_summary.pkl')
        

        簡単に処理概要を解説しておきますと、outputsフォルダ内の指定の日付のファイルが存在するか確認して、存在する場合はdf_ordersに追加していっています。

        最後に統合したファイルが、df_orders_summaryです。

        結果の確認

        累積利益の確認

        以下の通り、df_orders_summaryのprofitを累積してみた結果を示しておきます。

        initial_fund = 1000000
        
        df = df_orders_summary
        df = df.reset_index(drop=True)
        df = df.set_index('time')
        df['total_profit'] = df['profit'].cumsum()
        df['total_profit'] = df['total_profit'] + initial_fund
        
        df['total_profit'].plot(figsize=(18,6),lw=3)

        今回は最大ポジション数を5にしていて、それからさらにATRだけ動いてしまったら損切りするというロジックにしています。つまり、最後の損切りしているところ以外はポジション数5以内で利益を出せたという結果ですね。

        最大含み損(DD)の確認

        以下のように、利益だけでなく含み損も考慮してグラフ化してみます。

        initial_fund = 1000000
        
        df = df_orders
        df = df.reset_index(drop=True)
        df = df.set_index('time')
        df['total_profit'] = df['profit'].cumsum()
        df['total_profit'] = df['total_profit'].fillna(method='ffill')
        df['total_profit'] = df['total_profit'] + initial_fund
        df['margin'] = df['total_profit'] + df['buy_profit'] + df['sell_profit']
        
        df['total_profit'].plot(figsize=(18, 6),lw=3)
        df['margin'].plot(figsize=(18, 6),lw=1,color='darkcyan',style=':')

        緑の線がいわゆるDD(ドローダウン)で、その動きがより正確になっています。

        5万円くらい利益を積み重ねたところで3万円くらい損切りして、結果2万円くらいの利益となっています。

        まとめ

        以上の通り、MT5からダウンロードしたGOLD#のティックデータをインプットとして、Python(Google Colab)でバックテストを行う方法についてご紹介しました。

        今回はエントリー条件にテクニカル指標を取り入れるパターンでご紹介しました。

        少し長くなってしまいましたが、一つずつ追っていけば、それぞれのコードの意味がわかるはずです。こちらをベースに細かくカスタマイズすれば、お好みのロジックでMT4やMT5のバックテストと同等以上のものは作れるのではないかと思います。

        キャッシュバック口座の開設

        作成したEAを利用して実際に運用する際は、通常の口座開設ではなく、キャッシュバック口座を開設するのがおすすめです。以下の記事で、キャッシュバックサイト経由で海外FX口座を開設する際のメリットとデメリットをまとめていますのでご覧ください。

        リアル口座をどのように開設するのが最適かを考える参考になると思います。

        タイトルとURLをコピーしました