目次

(注意)記事の中で紹介したサンプルコードを使用する場合は自己責任でお願いします。

MQL5

MT5

Ubuntu

Python

HistData.comのヒストリカルデータをMT5にインポートする

HistData.comのヒストリカルデータをMT5にインポートしてみる。

先ずHistData.comからヒストリカルデータを手動でダウンロードするが、データをインポート用のファイルに変換するためにPythonを使っている。

HistData.comは有志のトレーダーによって運営されており、FXを中心に、株価指数や商品のヒストリカルデータを無料、無登録でダウンロードできる。

ただ、データの信頼性は不明である。

基準時間や夏時間の有無なども個々のデータで確認したほうがいい。

HistData.comのホームページ:https://www.histdata.com/

MT4/5用のヒストリカルデータ(1分足):https://www.histdata.com/download-free-forex-data/?/metatrader/1-minute-bar-quotes

環境

  • Python:3.11.7 64-bit
  • Spyder IDE:5.4.3
  • OANDA MetaTrader 5:5.00 build 4260

ヒストリカルデータのダウンロード

今回は例として日経225のヒストリカルデータのダウンロード、インポート用ファイルへの変換、インポート用ファイルのインポートをしてみる。

①以下のアドレスをクリックする。

https://www.histdata.com/download-free-forex-data/?/metatrader/1-minute-bar-quotes

②「JPX/JPY(2010/November)」をクリックする。

これが日経225である。

他の銘柄でも同様だと思うが、最新年のデータは1か月ごと、それ以外は1年ごとになっている。面倒臭いが、一つ一つダウンロードする。

ここでは2010年から2024年2月までのデータをダウンロードする。ファイルはすべてzipファイルである。

ここでは、ダウンロードしたファイルは

C:\Users\****\Downloads\JPXJPY

に保存しているとする。

インポート用ファイルへの変換

①Spyderを起動する。

別にSpyderでなくてもよいが、インポート用ファイルの変換にはPyhtonを使う。なお、SpyderとはPython用のIDEである。

②IPythonコンソールに以下のコードをコピー&ペーストして「Enter」キーを押す。

import pandas as pd
import shutil
from datetime import timedelta

symbol = 'JPXJPY' # 銘柄
start_year = 2010 # 開始年
end_month = 2 # 2024年の終了月
tick_vol = 15 # ティックボリューム。小さすぎるとヒストリー品質が悪くなる
spread = 50 # スプレッド
add_hour = 7 # 時間調整として加える時間
use_summer_time = True # 夏時間を適用するか否か
download_folder = 'C:\\Users\\****\\Downloads\\JPXJPY' # ダウンロード・フォルダー

# 2024年の各月のzipファイルを解凍する
for i in range(1,end_month+1):
    if(i<10):
        shutil.unpack_archive(download_folder+'\\HISTDATA_COM_MT_'+symbol+
                              '_M120240'+str(i)+'.zip', download_folder)
    else:
        shutil.unpack_archive(download_folder+'\\HISTDATA_COM_MT_'+symbol+
                              '_M12024'+str(i)+'.zip', download_folder)
# 2024年の各月のcsvファイルを読み込んでデータを結合する
data = pd.DataFrame()
for i in range(1,end_month+1):
    if(i<10):
        temp = pd.read_csv(download_folder+'\\DAT_MT_'+symbol+'_M1_20240'+
                           str(i)+'.csv', header=None)
    else:
        temp = pd.read_csv(download_folder+'\\DAT_MT_'+symbol+'_M1_2024'+
                           str(i)+'.csv', header=None)
    data = pd.concat([data, temp])
# データを年月日の行(0)を最優先、時間の行(1)をその次で並べ替える
data = data.sort_values([0,1])
# データを2024年のcsvファイルとして出力する
data.to_csv(download_folder+'\\DAT_MT_'+symbol+'_M1_2024.csv', index=False,
            header=False)
# 2023年以前のzipファイルを解凍する
for i in range(start_year,2024):
    shutil.unpack_archive(download_folder+'\\HISTDATA_COM_MT_'+symbol+
                              '_M1'+str(i)+'.zip', download_folder)
# 2023年以前と2024年のcsvファイルを読み込んでデータを結合する
data = pd.DataFrame()
for i in range(start_year,2025):
    temp = pd.read_csv(download_folder+'\\DAT_MT_'+symbol+'_M1_'+str(i)+'.csv',
                       header=None)
    data = pd.concat([data, temp])
# データを年月日の行(0)を最優先、時間の行(1)をその次で並べ替える
data = data.sort_values([0,1])
# データに7と8の列を加える
temp = pd.DataFrame(columns=[7,8])
data = pd.concat([data, temp], axis=1)
# 列名を変更する
data.columns = ['<DATE>','<TIME>','<OPEN>','<HIGH>','<LOW>','<CLOSE>',
                '<TICKVOL>','<VOL>','<SPREAD>']
# ティックボリューム、数量、スプレッドの列を埋める
data['<TICKVOL>'] = tick_vol
data['<VOL>'] = 0 # 数量は0でいい
data['<SPREAD>'] = spread
# 時間を調整する。必要あれば夏時間を適用する
dt = data['<DATE>']+' '+data['<TIME>']
dt = pd.to_datetime(dt)
dt = dt + timedelta(hours=add_hour)
if(use_summer_time==True):
    # 頭の悪いやり方だが…
    s10 = (dt>=pd.to_datetime('2010-03-14')) & (dt<pd.to_datetime('2010-11-07'))
    s11 = (dt>=pd.to_datetime('2011-03-13')) & (dt<pd.to_datetime('2011-11-06'))
    s12 = (dt>=pd.to_datetime('2012-03-11')) & (dt<pd.to_datetime('2012-11-04'))
    s13 = (dt>=pd.to_datetime('2013-03-10')) & (dt<pd.to_datetime('2013-11-03'))
    s14 = (dt>=pd.to_datetime('2014-03-09')) & (dt<pd.to_datetime('2014-11-02'))
    s15 = (dt>=pd.to_datetime('2015-03-08')) & (dt<pd.to_datetime('2015-11-01'))
    s16 = (dt>=pd.to_datetime('2016-03-13')) & (dt<pd.to_datetime('2016-11-06'))
    s17 = (dt>=pd.to_datetime('2017-03-12')) & (dt<pd.to_datetime('2017-11-05'))
    s18 = (dt>=pd.to_datetime('2018-03-11')) & (dt<pd.to_datetime('2018-11-04'))
    s19 = (dt>=pd.to_datetime('2019-03-10')) & (dt<pd.to_datetime('2019-11-03'))
    s20 = (dt>=pd.to_datetime('2020-03-08')) & (dt<pd.to_datetime('2020-11-01'))
    s21 = (dt>=pd.to_datetime('2021-03-14')) & (dt<pd.to_datetime('2021-11-07'))
    s22 = (dt>=pd.to_datetime('2022-03-13')) & (dt<pd.to_datetime('2022-11-06'))
    s23 = (dt>=pd.to_datetime('2023-03-12')) & (dt<pd.to_datetime('2023-11-05'))
    s24 = (dt>=pd.to_datetime('2024-03-10')) & (dt<pd.to_datetime('2024-11-03'))
    s = (s10 | s11 | s12 | s13 | s14 | s15 | s16 | s17 | s18 | s19 | s20 |
         s21 | s22 | s23 | s24)
    dt = dt + timedelta(hours=1) * s
date = dt.dt.strftime('%Y.%m.%d')
time = dt.dt.strftime('%H:%M')
data['<DATE>'] = date
data['<TIME>'] = time
# データを全期間のcsvファイルとして出力する
data.to_csv(download_folder+'\\'+symbol+'.csv', index=False)

現時点で最新年は2024年である。

上のコードは最新年が変わるたびに修正する必要がある。

コードのコメントでも説明しているが、いくつか補足する。

ティックボリューム

ティックボリュームは小さすぎるとヒストリー品質が悪くなる。

ティックボリュームが0だとインポートのときにエラーになるので、とりあえず1にしたところ、ヒストリー品質が10%程度になってしまった。

考えてみると、ティックボリュームとは価格の変化の回数をカウントしているのであるから、ティックボリュームが1ということは価格変化が1回しかなかったことになる。となると、始値、高値、安値、終値がすべて同じになるかもしれない。このためにヒストリー品質が低くなったのではないだろうか。

OANDAで日経225に相当するJP225では、ティックボリュームは平均して15くらいなので、ここでは15と設定した。すると、ヒストリー品質は99%になった。

もしかすると、ヒストリー品質とは

ヒストリー品質 = 1分足の本数 * ティックボリューム / MT5が期待するティックボリュームの総数 * 100

という計算式で算出されるものなのかもしれない。

ついでに書くと、バックテストのモデルに全ティックというのがあるが、もしティックボリュームが100だったら、それに合わせて100個分のデータを生成するのではないだろうか。

もしそうだとすると、全ティックでバックテストした場合、ティックボリュームが多いほど、価格変化の回数が多くなり、価格変化の大きさは小さく滑らかになるので、パフォーマンスに若干影響するかもしれない。

スプレッド

HistData.comのヒストリカルデータにはスプレッドの情報がなく、0になってしまう。

MT4、MT5両方で使えるようにサイトでは説明されているが、基本的にはMT4用と考えたほうがよさそうだ。

MT5でスプレッドの情報がないのは困るので、やはりJP225を参考にし、スプレッドを50ポイントとして設定した。

時間調整と夏時間の適用

JPXJPYの時間を調べてみると、米国東部時間が使われているようである。

一方、OANDAのMT5はニューヨーク・クロージングを基準にしている。ニューヨーク・クロージングは米国東部時間より7時間進んだ時間だ。

つまり、JPXJPYの時間はニューヨーク・クロージングより7時間遅れている。

そこで、上のコードでは7時間進ませてニューヨーク・クロージングと一致するように時間を調整した。

また、JPXJPYの時間は夏時間が適用されていないようである。米国東部時間を使いながら夏時間を適用しないのは謎だが、このため、米国が夏時間になると、JPXJPYの時間は1時間遅れてしまう。

そこで、上のコードでは夏時間を適用させ、米国が夏時間のときに1時間進ませて、ずれがないようにした。

夏時間の判定の仕方は力業で頭の悪いやり方だが、とりあえずこのまま。

なお、私が確認した範囲では

JPXJPY(日経平均):米国東部時間、夏時間適用なし
SPXUSD(S&P500):米国東部時間、夏時間適用なし
XAUUSD(金):米国東部時間、夏時間適用あり
WTIUSD(原油):米国東部時間、夏時間適用あり

のようである。必ずしも統一されていないので、注意が必要だ。

上のコードを実行すると、ダウンロードしたファイルを保存していた

C:\Users\****\Downloads\JPXJPY

に「JPXJPY.csv」ファイルが生成する。これがインポート用ファイルである。

インポート用ファイルのインポート

では、作成したインポート用ファイルをMT5にインポートする。

①MT5のメニューバーで「表示」をクリックする。

②「銘柄」をクリックする。

以下のabcはやらなくてもよいが、やれる環境にあればやっておくと便利。

JPXJPYはOANDAではJP225に相当する銘柄である。

abcをやっておくと、JPXJPYというカスタム銘柄を作成するに当たり、設定がJP225と同じになる。

その方が便利なので、後は必要にしたがって設定を修正すればいい。

  1. 「銘柄」で「仕様」タブをクリックする。

  2. 「OJ_Index」をクリックする。

  3. 「JP225」をクリックする。

③「銘柄」で「カスタム銘柄を作成する」をクリックする。

④「カスタム銘柄」で「銘柄」の行をダブルクリックする。

上のabcをやっている場合、「銘柄」の行は「JP225」になっているだろう。

⑤「JPXJPY」に変更する。

⑥真ん中やや下までスクロールし、「買いスワップ」と「売りスワップ」を「0」に変更する。

abcをやっている場合、「買いスワップ」と「売りスワップ」にある数値はたまたま、今この作業をしているときのJP225の数値にすぎず、これは日々変動するものだ。

ここではスワップは生じないものとして0にしておく。

⑦「YES」ボタンをクリックする。

⑧「銘柄」に戻って「チャートバー」タブをクリックする。

⑨銘柄を「JPXJPY」にする。

⑩「バーをインポートする」ボタンをクリックする。

⑪「レートをインポートします: JPXJPY」で「ファイル」の右にある「選択」ボタンをクリックする。

⑫「JPXJPY.csv」ファイルを選択して「開く」ボタンをクリックする。

⑬「区切り文字」で「,」を選択する。

HistData.comのファイルは「,」で区切っているようだ。ちなみにMT5内に元からあるヒストリカルデータだと「tab」で区切られているようだ。

⑭「スキップ」で「列」、「行」ともに「0」を選択する。

⑮「時差調整」で「0」を選択する。

⑯「ティックボリューム」でチェックを入れる。

⑰「YES」ボタンをクリックする。

⑱「銘柄」に戻り、インポートが終わったら「OK」ボタンをクリックする。

Anacondaでpandas-datareaderをインストール

Anacondaでpandas-datareaderをインストールする。

環境

インストール手順

①Anaconda Promtを起動する。

②Anaconda Promtに以下のコマンドをコピーアンドペーストして「Enter」キーを押す。

conda install anaconda::pandas-datareader

③「Proceed ([y]/n)?」と表示されたら「Enter」キーを押す。

Anacondaをインストール

Anacondaをインストールする。

Anaconda版のPythonはデータサイエンスや機械学習用のライブラリが多く用意されており、依存問題も解消されていて便利だ。そこで、ここではAnacondaをインストールしてPythonを使うこととする。

Anacondaをインストールすると10GBほど容量を消費するので注意。

環境

Anacondaのダウンロード

①以下のリンクをクリックする。

②「Download」ボタンをクリックする。

私の環境では「Downloads」フォルダーに「Anaconda3-2024.02-1-Windows-x86_64.exe」ファイルが保存された。

Anacondaのインストール

上に続けて実行する。

①「Anaconda3-2024.02-1-Windows-x86_64.exe」ファイルをダブルクリックする。

②「Anaconda3 2024.02-1 (64-bit) Setup」で「Next」ボタンをクリックする。

③「I Agree」ボタンをクリックする。

④「Just Me (recommended)」が選択されているのを確認して(選択されていなかったら選択して)「Next」ボタンをクリックする。

⑤インストール先が「C:\Users****\anaconda3」でよければ(よくなければ「Brouwse...」ボタンをクリックしてインストール先を選択して)「Next」ボタンをクリックする。

⑥私の環境ではオプションが4つあって2つが選択されていたが、デフォルトの選択のまま「Install」ボタンをクリックする。

⑦「Completed」と表示されたら「Next」ボタンをクリックする。

⑧「Next」ボタンをクリックする。

⑨私の環境ではオプションが2つあって2つとも選択されていたが、デフォルトの選択のまま「Finish」ボタンをクリックする。

⑩「Update Application」、「Sign In Now」いうのが表示されるが何もせずに閉じる。

前の手順でオプションの選択を外していれば表示されなかったかもしれない。

⑪「ANACONDA NAVIGATOR」も何もせずに閉じる。

⑫「Quit Application」で「Yes」ボタンをクリックする。

Anaconda Promptの起動

Windowsの検索窓に「Anaconda」と入力する。

②「Anaconda Prompt (anaconda3)」をクリックする。

Anacondaのアップデート

Anacondaをアップデートしたい場合は以下のようにする。インストール直後はやったほうがいい。

〇Anaconda Promptに以下のコマンドをコピー&ペーストして「Enter」キーを押す。

conda update --all --yes

まあまあ時間がかかる。

エラーが出ることもあるが気にしない。

Spyderの起動

Spyderを起動したい場合は以下のようにする。

〇Anaconda Promptに以下のコマンドをコピー&ペーストして「Enter」キーを押す。

spyder

PythonでMT5のウォークフォワードテストを少しだけ楽にする

言い訳がましいタイトルだが(笑)、PythonでMT5のウォークフォワードテストを少しだけ楽にするコードを書いてみた。残念ながらウォークフォワードテストそのものができるわけではない。

MT5側で最適化をする準備をした上でPyhtonでMT5を起動し、指定した開始時間と終了時間で最適化を実行する。

ただそれだけの機能だが、Python側で開始時間と終了時間をずらしながらループすれば、イン期間での最適化の繰り返しを自動でやってくれるというわけだ。ウォークフォワードテストではその部分が面倒なので、いくらかは楽になる。

文章だけ見ると、これはこれで面倒そうだが、一度やってみれば後は楽だ(と思う)。

なお、PythonでMT5を起動し、最適化する関数を作るにあたっては豊嶋先生の以下の記事を参考にした。

環境

  • Python: 3.9.16 64-bit
  • Spyder IDE: 5.4.1
  • OANDA MetaTrader 5: 5.00 build 4153
  • MetaEditor: 5.00 build 4153

MT5を起動し、最適化する関数

ここのコードと下にある「Pyhonで最適化のループ」のコードはSpyderで実行することを想定している。

Spyderの「IPython Console」にコピペして「Enter」キーを押せば実行できる。

なお、実行中、Spyderの作業フォルダに「myconfig.ini」というファイルを作ってバックテストに必要な設定を書き込んでいるが、もし万が一(そんな偶然はないと思うが)同フォルダ内に同名のファイルがあると上書きしてしまうので注意。

import pandas as pd
import subprocess
from bs4 import BeautifulSoup

def optimize(expert,symbol,period,start_year,start_month,start_day,end_year,
             end_month,end_day,model,optimization_criterion,install_folder,
             data_folder):
    from_date='FromDate='+str(start_year)+'.'+str(start_month)+'.'+str(start_day)
    to_date='ToDate='+str(end_year)+'.'+str(end_month)+'.'+str(end_day)
    f=open('myconfig.ini','w')
    param=['[Tester]\n',
           'Expert='+expert+'\n',
           'Symbol='+symbol+'\n',
           'Period='+period+'\n',
           'Model='+str(model)+'\n',
           'ExecutionMode=0\n',
           'Optimization=1\n',
           'OptimizationCriterion='+str(optimization_criterion)+'\n',
           'ForwardMode=0\n',
           'Report=report\n',
           'ReplaceReport=1\n',
           'ShutdownTerminal=1\n',
           from_date+'\n',
           to_date]
    f.writelines(param)
    f.close()
    subprocess.run(install_folder+'\\terminal64.exe /config:myconfig.ini')
    with open(data_folder+'\\report.xml') as fp:
        soup = BeautifulSoup(fp, 'xml')
    df = pd.DataFrame()
    for r,row in enumerate(soup.find_all('Row')):
        for c,cell in enumerate(row.find_all('Data')):
            df.loc[r, c] = cell.string
    df.columns=df.iloc[0]
    df=df.iloc[1]
    df=df.iloc[2:]
    return df

最適化をする前の準備

インストールフォルダーのパス

先ずMT5をインストールしているフォルダーのパスを調べる。

OANDAのMT5なら

C:\Program Files\OANDA MetaTrader 5

になると思うが、これを「'」で囲み、「\」は2つにして

'C:\\Program Files\\OANDA MetaTrader 5'

とする。

別の業者であればそれに合わせる。

データフォルダーのパス

次にMT5のデータフォルダーのパスを調べる。

その手順は

  1. MT5のメニューバーの「ファイル」をクリックする。
  2. 「データフォルダを開く」をクリックする。
  3. パスをコピーする。

恐らく

C:\Users\****\AppData\Roaming\MetaQuotes\Terminal\********************************

のようになっていると思うので、やはり、これを「'」で囲み、「\」は2つにして

'C:\\Users\\****\\AppData\\Roaming\\MetaQuotes\\Terminal\\********************************'

とする。

MT5側の準備

ここではサンプルEAの「MACD Sample」を使って最適化することにする。

最適化するパラメータは「Take Profit (pips)」と「Trailing Stop Level (pips)」の2つとし、20pipsから200pipsまで20pips刻みで最適化しよう。

その他のパラメータはデフォルトのまま。

  1. MT5のストラテジーテスターで「エクスパート」を「Examples\MACD\MACD Sample.ex5」にする。
  2. 「パラメータ」タブをクリックする。
  3. 「Take Profit (pips)」にチェックを入れ、「スタート」、「ステップ」、「ストップ」をそれぞれ20、20、200とする。
  4. 「Trailing Stop Level (pips)」にチェックを入れ、「スタート」、「ステップ」、「ストップ」をそれぞれ20、20、200とする。
  5. MT5を終了する。

開始時間と終了時間をずらしながら最適化するにはPythonによってMT5を起動する必要があるので、一旦終了させる。

Pyhonで最適化のループ

ここではユーロドルの1時間足を使って最適化することにしよう。

「モデル」は「始値のみ」、「オプティマイズ」の指標は「残高最大」にする。

なお、上の関数により、「フォワード」は「キャンセル」、「延滞」は「延滞ゼロ、理想的な実行」に自動的に変更される。

また、「入金」の数字と通貨、「レバレッジ」は普段使っている設定が使われる。ここでは「1000000」、「JPY」、「1:25」の設定で実行した。

開始期間は2009年1月1日、終了時間は2014年1月1日として、1年ずつ後ろにずらし、計10回ループする。

そして終了年と、オプティマイズ指標で最良だったパラメータ値でのパフォーマンス等を出力する。

ループが1回終了するたびにMT5は再起動する。

expert='Examples\MACD\MACD Sample.ex5' # 指定しないと正常に動作しない
symbol='EURUSD' # 指定しないと直前の設定が使われる
period='H1' # 指定しないとH1になる
start_year=2009
start_month=1
start_day=1
end_year=2014
end_month=1
end_day=1
model=2 # 0:全ティック、1:1分足OHLC、2:始値のみ、3:数値計算、4:リアルティックに基づいたすべてのティック
optimization_criterion=0 # 0:残高最大、1:最大利益率、2:最大予想ペイオフ、3:最小ドローダウン、4:最大回復係数、5:最大シャープレシオ、6:カスタム最大
install_folder='C:\\Program Files\\OANDA MetaTrader 5'
data_folder='C:\\Users\\****\\AppData\\Roaming\\MetaQuotes\\Terminal\\********************************'

for i in range(10):
    df=optimize(expert,symbol,period,start_year+i,start_month,start_day,
                end_year+i,end_month,end_day,model,optimization_criterion,
                install_folder,data_folder)
    print(end_year+i)
    print(df)
    print('\n')

出力

2014
0
Profit                  159821
Expected Payoff    1664.802083
Profit Factor         2.704830
Recovery Factor       4.692750
Sharpe Ratio          3.122384
Custom                       0
Equity DD %             3.2884
Trades                      96
InpTakeProfit               40
InpTrailingStop             20
Name: 1, dtype: object


2015
0
Profit                  190314
Expected Payoff    2409.037975
Profit Factor         2.377659
Recovery Factor       4.625334
Sharpe Ratio          2.146057
Custom                       0
Equity DD %             3.6932
Trades                      79
InpTakeProfit              200
InpTrailingStop            200
Name: 1, dtype: object


2016
0
Profit                  178058
Expected Payoff    2000.651685
Profit Factor         1.950870
Recovery Factor       1.881802
Sharpe Ratio          1.940135
Custom                       0
Equity DD %             8.0390
Trades                      89
InpTakeProfit              200
InpTrailingStop             40
Name: 1, dtype: object


2017
0
Profit                  140856
Expected Payoff    1697.060241
Profit Factor         1.627961
Recovery Factor       1.488634
Sharpe Ratio          1.359581
Custom                       0
Equity DD %             8.4097
Trades                      83
InpTakeProfit              200
InpTrailingStop             40
Name: 1, dtype: object


2018
0
Profit                  234116
Expected Payoff    2787.095238
Profit Factor         2.168535
Recovery Factor       2.474250
Sharpe Ratio          1.816929
Custom                       0
Equity DD %             8.5059
Trades                      84
InpTakeProfit              160
InpTrailingStop            200
Name: 1, dtype: object


2019
0
Profit                  203823
Expected Payoff    2516.333333
Profit Factor         1.983635
Recovery Factor       2.154099
Sharpe Ratio          1.554359
Custom                       0
Equity DD %             9.1116
Trades                      81
InpTakeProfit              160
InpTrailingStop            200
Name: 1, dtype: object


2020
0
Profit                  183006
Expected Payoff    2506.931507
Profit Factor         1.962571
Recovery Factor       1.934095
Sharpe Ratio          1.424284
Custom                       0
Equity DD %             9.2862
Trades                      73
InpTakeProfit              160
InpTrailingStop            200
Name: 1, dtype: object


2021
0
Profit                   79357
Expected Payoff    1167.014706
Profit Factor         1.461464
Recovery Factor       0.905034
Sharpe Ratio          0.990896
Custom                       0
Equity DD %             7.7021
Trades                      68
InpTakeProfit              120
InpTrailingStop             40
Name: 1, dtype: object


2022
0
Profit                   72227
Expected Payoff    1146.460317
Profit Factor         1.585237
Recovery Factor       0.823719
Sharpe Ratio          1.146981
Custom                       0
Equity DD %             7.8130
Trades                      63
InpTakeProfit              120
InpTrailingStop             40
Name: 1, dtype: object


2023
0
Profit                  20315
Expected Payoff    369.363636
Profit Factor        1.149393
Recovery Factor      0.232065
Sharpe Ratio         0.341220
Custom                      0
Equity DD %            8.5149
Trades                     55
InpTakeProfit              60
InpTrailingStop           200
Name: 1, dtype: object

イン期間ではそこそこ勝っているようだが、パラメータ値はあまり安定しておらず、果たしてアウト期間で勝てるかどうかといったところ。

また、1時間足で始値のみなので、利食い損切りのどちらが先にヒットしたかの判定もあいまいになり、この結果はあまり信用できないとも言える。

今回はあくまでも使い方の説明なので、細かいところには立ち入らない。

ウォークフォワードテストをする場合

ウォークフォワードテストをする場合は、上で調べた最適なパラメータ値を利用する。

アウト期間を1年とするなら、上の出力ではイン期間の終了年を表示させているので、その年をアウト期間にすればいい。

2014年から2023年まで実行するなら以下のようなコードをEAに加える。

そしてparam1が「Take Profit (pips)」に、param2が「Trailing Stop Level (pips)」に設定されるようにEAに修正を加える。

double param1=0;
double param2=0;
MqlDateTime mdt;
TimeCurrent(mdt);
if(mdt.year==2014)
  {
  param1=40;
  param2=20;
  }
(中略)
if(mdt.year==2023)
  {
  param1=60;
  param2=200;
  }

通貨の強弱の計算式

通貨の強弱の計算式を紹介する。

人によって呼び方は様々だが、最近、「通貨の強弱」という指標が相場分析でよく使われている印象がある。

昔、Ku-Chartと呼ばれる指標があったが、今の「通貨の強弱」はKu-Chart、あるいはその派生型なのではないだろうか。

当時、Ku-Chartの計算はやや複雑で、処理も重かった。

そこで私はもっと簡単に計算でき、処理も軽くなる方法を見つけた。

その計算式を通貨の強弱の計算式として、ここで紹介する。

仮定世界の通貨の価格

通貨の価格というのは他の通貨との交換価格なので、本来、通貨単体の価格というのは分からない。

だが、通貨をいくつか選択し、世界にはそれ以外の通貨は存在しないと仮定することによって、各通貨のその仮定世界での価格というのを表すことはできる。

そして、その価格の変化を比較することによって通貨の強弱を知ることができるというわけだ。

といってもよく分からないが、以下の計算式とそれを導出した過程を見れば理解できると思う。

通貨の強弱の計算式

今回は世界に

AUD:豪ドル
CAD:カナダドル
CHF:スイスフラン
EUR:ユーロ
GBP:ポンド
JPY:日本円
NZD:ニュージーランドドル
USD:米ドル

の8通貨しか存在しない世界を仮定する。

そして、この仮定世界での各通貨の価格を

A-AUD
A-CAD
A-CHF
A-EUR
A-GBP
A-JPY
A-NZD
A-USD

のように頭に「A-」を加えて表すこととする。

では計算式を以下に示す。

a = (log(AUDUSD)+log(EURUSD)+log(GBPUSD)+log(NZDUSD)-log(USDCAD)-log(USDCHF)-log(USDJPY)) / 8
A-AUD = log(AUDUSD) - a
A-EUR = log(EURUSD) - a
A-GBP = log(GBPUSD) - a
A-NZD = log(NZDUSD) - a
A-CAD = -log(USDCAD) - a
A-CHF = -log(USDCHF) - a
A-JPY = -log(USDJPY) - a
A-USD = -a

なお、logはここでは自然対数である。MQL5ならMathLog()で計算できる。Mathlog10()ではないので注意。

今回はUSDを含む通貨ペアを選び、計算したが、USDである必要はないし、8通貨である必要もない。

計算式のパターンに注目すれば、別の通貨ペアの組み合わせでも計算できるだろう。

計算式の解説

ではなぜ、このような計算式になるのかを説明しておく。興味のない人は読み飛ばしていただいて構わない。

かなりくどい書き方をしているが、順を追って見れば、数学が苦手な人でも分かりやすいかと思う。

ただし対数の知識は必要。

①ドルストレートの通貨ペアのみを材料として使い、通貨ペアを「ベース通貨 / クォート通貨」の形に変形する。

AUDUSD = AUD / USD
EURUSD = EUR / USD
GBPUSD = GBP / USD
NZDUSD = NZD / USD
USDCAD = USD / CAD
USDCHF = USD / CHF
USDJPY = USD / JPY
USDUSD = USD / USD(このような通貨ペアはないが、あるとする)

②左辺と右辺を入れ替える。

AUD / USD = AUDUSD
EUR / USD = EURUSD
GBP / USD = GBPUSD
NZD / USD = NZDUSD
USD / CAD = USDCAD
USD / CHF = USDCHF
USD / JPY = USDJPY
USD / USD = USDUSD

③クォート通貨をUSDに揃える。

AUD / USD = AUDUSD
EUR / USD = EURUSD
GBP / USD = GBPUSD
NZD / USD = NZDUSD
CAD / USD = 1 / USDCAD
CHF / USD = 1 / USDCHF
JPY / USD = 1 / USDJPY
USD / USD = 1

④Ku-Chartでは自然対数を使っているので、自然対数に変換する。

log(AUD/USD) = log(AUDUSD)
log(EUR/USD) = log(EURUSD)
log(GBP/USD) = log(GBPUSD)
log(NZD/USD) = log(NZDUSD)
log(CAD/USD) = log(1/USDCAD)
log(CHF/USD) = log(1/USDCHF)
log(JPY/USD) = log(1/USDJPY)
log(USD/USD) = log(1)

⑤除算を減算の形に変える。

log(AUD) - log(USD) = log(AUDUSD)
log(EUR) - log(USD) = log(EURUSD)
log(GBP) - log(USD) = log(GBPUSD)
log(NZD) - log(USD) = log(NZDUSD)
log(CAD) - log(USD) = log(1) - log(USDCAD)
log(CHF) - log(USD) = log(1) - log(USDCHF)
log(JPY) - log(USD) = log(1) - log(USDJPY)
log(USD) - log(USD) = log(1)

⑥計算を進める。

log(AUD) - log(USD) = log(AUDUSD)
log(EUR) - log(USD) = log(EURUSD)
log(GBP) - log(USD) = log(GBPUSD)
log(NZD) - log(USD) = log(NZDUSD)
log(CAD) - log(USD) = 0 - log(USDCAD)
log(CHF) - log(USD) = 0 - log(USDCHF)
log(JPY) - log(USD) = 0 - log(USDJPY)
log(USD) - log(USD) = 0

⑦更に進める。

log(AUD) - log(USD) = log(AUDUSD)
log(EUR) - log(USD) = log(EURUSD)
log(GBP) - log(USD) = log(GBPUSD)
log(NZD) - log(USD) = log(NZDUSD)
log(CAD) - log(USD) = -log(USDCAD)
log(CHF) - log(USD) = -log(USDCHF)
log(JPY) - log(USD) = -log(USDJPY)
log(USD) - log(USD) = 0

⑧log(USD)を右辺に移項する。

log(AUD) = log(AUDUSD) + log(USD)
log(EUR) = log(EURUSD) + log(USD)
log(GBP) = log(GBPUSD) + log(USD)
log(NZD) = log(NZDUSD) + log(USD)
log(CAD) = -log(USDCAD) + log(USD)
log(CHF) = -log(USDCHF) + log(USD)
log(JPY) = -log(USDJPY) + log(USD)
log(USD) = log(USD)

⑨Ku-Chartは平均を減じて全体の合計が0になるようにしている。そこで先ず平均を求める。

mean = (log(AUD)+log(EUR)+log(GBP)+log(NZD)+log(CAD)+log(CHF)+log(JPY)+log(USD)) / 8

⑩式を通貨ペアに置き換える

mean = (log(AUDUSD)+log(USD)+log(EURUSD)+log(USD)+log(GBPUSD)+log(USD)+log(NZDUSD)+log(USD)-log(USDCAD)+log(USD)-log(USDCHF)+log(USD)-log(USDJPY)+log(USD)+log(USD)) / 8

⑪計算を進める。

mean = (log(AUDUSD)+log(EURUSD)+log(GBPUSD)+log(NZDUSD)-log(USDCAD)-log(USDCHF)-log(USDJPY)+log(USD)*8) / 8

⑫更に進める。

mean = (log(AUDUSD)+log(EURUSD)+log(GBPUSD)+log(NZDUSD)-log(USDCAD)-log(USDCHF)-log(USDJPY)) / 8 + (log(USD)*8) / 8

⑬更に更に進める。

mean = (log(AUDUSD)+log(EURUSD)+log(GBPUSD)+log(NZDUSD)-log(USDCAD)-log(USDCHF)-log(USDJPY)) / 8 + log(USD)

⑭ここで「(log(AUDUSD)+log(EURUSD)+log(GBPUSD)+log(NZDUSD)-log(USDCAD)-log(USDCHF)-log(USDJPY)) / 8」を「a」として以下のように置き換える。

mean = a + log(usd)

⑮Ku-Chartは自然対数化した各通貨から上で計算した平均を減じたものであるので以下のようにする。

A-AUD = log(AUD) - mean
A-EUR = log(EUR) - mean
A-GBP = log(GBP) - mean
A-NZD = log(NZD) - mean
A-CAD = log(CAD) - mean
A-CHF = log(CHF) - mean
A-JPY = log(JPY) - mean
A-USD = log(USD) - mean

⑯平均をa + log(USD)に置き換える。

A-AUD = log(AUD) - (a+log(USD))
A-EUR = log(EUR) - (a+log(USD))
A-GBP = log(GBP) - (a+log(USD))
A-NZD = log(NZD) - (a+log(USD))
A-CAD = log(CAD) - (a+log(USD))
A-CHF = log(CHF) - (a+log(USD))
A-JPY = log(JPY) - (a+log(USD))
A-USD = log(USD) - (a+log(USD))

⑰()を外す。

A-AUD = log(AUD) - a - log(USD)
A-EUR = log(EUR) - a - log(USD)
A-GBP = log(GBP) - a - log(USD)
A-NZD = log(NZD) - a - log(USD)
A-CAD = log(CAD) - a - log(USD)
A-CHF = log(CHF) - a - log(USD)
A-JPY = log(JPY) - a - log(USD)
A-USD = log(USD) - a - log(USD)

⑱順番を入れ替える。

A-AUD = log(AUD) - log(USD) - a
A-EUR = log(EUR) - log(USD) - a
A-GBP = log(GBP) - log(USD) - a
A-NZD = log(NZD) - log(USD) - a
A-CAD = log(CAD) - log(USD) - a
A-CHF = log(CHF) - log(USD) - a
A-JPY = log(JPY) - log(USD) - a
A-USD = log(USD) - log(USD) - a

⑲減算を除算の形に戻す。

A-AUD = log(AUD/USD) - a
A-EUR = log(EUR/USD) - a
A-GBP = log(GBP/USD) - a
A-NZD = log(NZD/USD) - a
A-CAD = log(CAD/USD) - a
A-CHF = log(CHF/USD) - a
A-JPY = log(JPY/USD) - a
A-USD = log(USD/USD) - a

⑳計算を進める。

A-AUD = log(AUD/USD) - a
A-EUR = log(EUR/USD) - a
A-GBP = log(GBP/USD) - a
A-NZD = log(NZD/USD) - a
A-CAD = -log(USD/CAD) - a
A-CHF = -log(USD/CHF) - a
A-JPY = -log(USD/JPY) - a
A-USD = log(1) - a

㉑更に進める。

A-AUD = log(AUD/USD) - a
A-EUR = log(EUR/USD) - a
A-GBP = log(GBP/USD) - a
A-NZD = log(NZD/USD) - a
A-CAD = -log(USD/CAD) - a
A-CHF = -log(USD/CHF) - a
A-JPY = -log(USD/JPY) - a
A-USD = 0 - a

㉒更に更に進め、「ベース通貨 / クォート通貨」の形を通貨ペアに戻す。

A-AUD = log(AUDUSD) - a
A-EUR = log(EURUSD) - a
A-GBP = log(GBPUSD) - a
A-NZD = log(NZDUSD) - a
A-CAD = -log(USDCAD) - a
A-CHF = -log(USDCHF) - a
A-JPY = -log(USDJPY) - a
A-USD = -a

これで計算式が求められた。

この計算で求められた価格は自然対数なので、価格が0.01増えれば大体1%増加したということになる。

平均を減じる理由

計算の途中でなぜ平均を減じるかというと、例えば、ある通貨が1%増加したら、その他の通貨全体で1%減少する。

何かが増えればその分だけ他のところで減り、プラスマイナス0になる。

それを分かりやすく示すために通貨の価格全体を合計すると0にしておくのだ。

そうすれば、どの通貨がどれだけ増えようと減ろうと全体を合計すると0のままとなる。

そして合計が0になるためには平均を減じておくというわけだ。

AnacondaでTa-Libをインストール

AnacondaでTa-Libをインストールする。

環境

インストール手順

①Anaconda Promtを起動する。

②Anaconda Promtに以下のコマンドをコピーアンドペーストして「Enter」キーを押す。

conda install conda-forge::ta-lib

③「Proceed ([y]/n)?」と表示されたら「Enter」キーを押す。