(注意)記事の中で紹介したサンプルコードを使用する場合は自己責任でお願いします。
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
環境
ヒストリカルデータのダウンロード
今回は例として日経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と同じになる。
その方が便利なので、後は必要にしたがって設定を修正すればいい。
「銘柄」で「仕様」タブをクリックする。
「OJ_Index」をクリックする。
「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ほど容量を消費するので注意。
環境
- Windows 11 Home
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のデータフォルダーのパスを調べる。
その手順は
- MT5のメニューバーの「ファイル」をクリックする。
- 「データフォルダを開く」をクリックする。
- パスをコピーする。
恐らく
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刻みで最適化しよう。
その他のパラメータはデフォルトのまま。
- MT5のストラテジーテスターで「エクスパート」を「Examples\MACD\MACD Sample.ex5」にする。
- 「パラメータ」タブをクリックする。
- 「Take Profit (pips)」にチェックを入れ、「スタート」、「ステップ」、「ストップ」をそれぞれ20、20、200とする。
- 「Trailing Stop Level (pips)」にチェックを入れ、「スタート」、「ステップ」、「ストップ」をそれぞれ20、20、200とする。
- 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」キーを押す。