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」ボタンをクリックする。