ホーム システムトレード 投稿

MetaTrader 5のシンボル履歴を全てダウンロードする方法

添付ファイル
56324.zip (3.28 KB, ダウンロード 2回)

皆さん、こんにちは!今日はMetaTrader 5を使って、シンボルのすべてのティックデータをダウンロードする方法についてお話しします。このエキスパートアドバイザー(EA)は、ユーザーのブローカーのマーケットウォッチをスキャンし、利用可能なティックをすべてダウンロードします。また、特定の日付までのティックもダウンロードできます。

この方法を使えば、バックテスト用のシンボル履歴を簡単に取得したり、そのティックからカスタムチャートを作成するのに役立ちます。

ティックデータは、データフォルダ内にキャッシュされるので、十分なハードドライブの空き容量を確保してくださいね。

シンボルのダウンロードをスムーズに行うために、まずはダウンロードマネージャーを用意します。

CDownloadManager構造体には、必要な情報が全て含まれています。

struct CDownloadManager {
    bool m_started, m_finished;
    string m_symbols[], m_current;
    int m_index;
}

この構造体では、以下の情報を管理します:

  • ダウンロードの状態(開始/終了)
  • スキャンするシンボルのリスト
  • 現在のシンボル
  • スキャン中のシンボルのインデックス

また、ハードドライブへの読み書きが必要なので、シンボルに関しては、バイナリファイルから文字列を読み書きするための2つの簡単な関数を作成します。

ファイルに文字列を保存する関数:

void writeStringToFile(int f, string thestring) {
    //シンボル文字列を保存
    char sysave[];
    int charstotal = StringToCharArray(thestring, sysave, 0, StringLen(thestring), CP_ACP);
    FileWriteInteger(f, charstotal, INT_VALUE);
    for(int i = 0; i < charstotal; i++) {
        FileWriteInteger(f, sysave[i], CHAR_VALUE);
    }
}

この関数は以下を受け取ります:

  • ファイルハンドル f(書き込み用に開かれたファイル)
  • ファイルに書き込む文字列

この関数は、文字列内の文字数を整数として書き込み、その後各文字を保存します。

ファイルから文字列を読み込む関数:

string readStringFromFile(int f) {
    string result = "";
    //シンボル文字列を読み込む
    char syload[];
    int charstotal = (int)FileReadInteger(f, INT_VALUE);
    if(charstotal > 0) {
        ArrayResize(syload, charstotal, 0);
        for(int i = 0; i < charstotal; i++) {
            syload[i] = (char)FileReadInteger(f, CHAR_VALUE);
        }
        result = CharArrayToString(syload, 0, charstotal, CP_ACP);
    }
    return(result);
}

この関数は以下を受け取ります:

  • ファイルハンドル f(バイナリとして読み込み用に開かれたファイル)

この関数はファイル内で期待される文字数を整数として読み込み、その後、その文字をchar配列に読み込み、最終的に文字列を作成して返します。

次に、CDownloadManager構造体を初期化し、マーケットウォッチからデータを取得する方法を見ていきましょう。

//+------------------------------------------------------------------+
//| マーケットウォッチからシンボルを取得 |
//+------------------------------------------------------------------+
void grab_symbols() {
    //! マーケットウォッチからのみ
    int s = SymbolsTotal(true);
    ArrayResize(m_symbols, s, 0);
    for(int i = 0; i < ArraySize(m_symbols); i++) {
        m_symbols[i] = SymbolName(i, true);
    }
}

非常にシンプルな流れですね:

  • マーケットウォッチに存在するシンボルの数を取得する
  • m_symbols配列を必要なサイズにリサイズする
  • 全シンボルをループし、シンボル名を取得する

ダウンロードの管理を行うための関数も必要になります。これが実質的なマネージャーの役割を果たします:

//+------------------------------------------------------------------+
//| シンボルのダウンロードプロセスを管理 |
//+------------------------------------------------------------------+
void manage(string folder, string filename) {
    // 新規または継続のダウンロードを判定
    if(ArraySize(m_symbols) > 0) {
        //開始されていない場合
        if(!m_started) {
            m_started = true;
            //最初のシンボルに移動
            m_current = m_symbols[0];
            m_index = 1;
            save(folder, filename);
            if(_Symbol != m_current) {
                ChartSetSymbolPeriod(ChartID(), m_current, _Period);
            }
            else {
                ENUM_TIMEFRAMES new_period = PERIOD_M1;
            for(int p = 0; p < ArraySize(TFS); p++) {
                if(_Period != TFS[p]) {
                    new_period = TFS[p];
                    break;
                }
            }
            ChartSetSymbolPeriod(ChartID(), m_current, new_period);
            }
            return;
        }
        //開始されている場合
        else {
            m_index++;
            if(m_index <= ArraySize(m_symbols)) {
                m_current = m_symbols[m_index - 1];
                save(folder, filename);
                if(_Symbol != m_current) {
                    ChartSetSymbolPeriod(ChartID(), m_current, _Period);
                }
                return;
        }
        else {
            m_finished = true;
            FileDelete(folder + "\" + filename);
            Print("完了");
            ExpertRemove();
            return;
        }
    }
    }
    else {
        Print("まずシンボルを取得してください");
    }
    //if set ends here
}

このシステムの仕組み:

  • チャートを開き、1つのチャートとタイマーを設定します。
  • そのタイマーが実行され、タイマーをキャンセルします。
  • 新規ダウンロードか継続的なダウンロードかを確認します。
  • 新規ダウンロードの場合、全シンボルを取得します。
  • 継続的ダウンロードの場合、現在のシンボルのデータをダウンロードします。

このコードがタイマーでのダウンロードを実行します:

//+------------------------------------------------------------------+
//| タイマー |
//+------------------------------------------------------------------+
void OnTimer() {
    //--- 同期されている場合
    if(SymbolIsSynchronized(_Symbol) && TerminalInfoInteger(TERMINAL_CONNECTED) == 1) {
        EventKillTimer();
        //--- システムをここでロード
        if(MANAGER.load(MANAGER_FOLDER, MANAGER_STATUS_FILE)) {
            //--- システムがロードされたので、ここでシンボルをスキャン中
            Comment("システムがロードされ、処理中: " + MANAGER.m_current);
            //--- ティックのロード
            //--- まず、ブローカーで利用可能な最も古いティックを見つける
            int attempts = 0;
            int ping = -1;
            datetime cursor = flatten(TimeTradeServer());
            long cursorMSC = (long)cursor * 1000;
            long jump = 2592000000;//60*60*24*30*1000;
            MqlTick receiver[];
            long oldest = LONG_MAX;
            Comment("お待ちください");
            while(attempts < 5) {
            ping = CopyTicks(_Symbol, receiver, COPY_TICKS_ALL, cursorMSC, 1);
            if(ping == 1) {
                if(receiver[0].time_msc == oldest) {
                    attempts++;
                }
                }
            else {
                attempts = 0;
            }
            if(receiver[0].time_msc < oldest) {
                oldest = receiver[0].time_msc;
            }
            cursorMSC -= jump;
            if(limitDate && receiver[0].time <= oldestLimit) {
                break;
                }
            }
            else {
                attempts++;
              }
            Sleep(44);
            Comment("最古のティック: " + TimeToString((datetime)(oldest / 1000), TIME_DATE | TIME_MINUTES | TIME_SECONDS) + " カーソル(" + TimeToString((datetime)(cursorMSC / 1000), TIME_DATE | TIME_MINUTES | TIME_SECONDS) + ") 試行回数(" + IntegerToString(attempts) + ") お待ちください...");
        }
            //--- この時点で最古のティックが判明
            //--- 最古から最新へのティックを要求開始
            if(oldest != LONG_MAX) {
                ArrayFree(receiver);
                datetime newest_tick = 0;
            //--- このシンボルに保存されている最後のティックの時間を取得
            datetime most_recent_candle = (datetime)SymbolInfoInteger(_Symbol, SYMBOL_TIME);
            while(newest_tick < most_recent_candle) {
                //--- 最古の時間からティックの数を指定して新しいバッチを要求
                int pulled = CopyTicks(_Symbol, receiver, COPY_TICKS_ALL, oldest, tick_packets);
                if(pulled > 0) {
                    newest_tick = receiver[pulled - 1].time;
                    oldest = receiver[pulled - 1].time_msc;
                    ArrayFree(receiver);
                }
                //--- サーバーリクエストのタイムアウト、必要に応じて変更してください
                Sleep(44);
                Comment("最新ティックまで引き出しました: " + TimeToString(newest_tick, TIME_DATE | TIME_MINUTES | TIME_SECONDS) + "まで");
              }
        }
        else {
            Alert("ターミナルを閉じてください ティックフォルダーに移動して 空のフォルダーを削除してください");
            ExpertRemove();
        }
        //--- マネージャーを更新し、次に進む
        MANAGER.manage(MANAGER_FOLDER, MANAGER_STATUS_FILE);
    }
    else {
        //--- マーケットウォッチのシンボルを取得してダウンロードを開始します
        Comment("MWを取得中、開始します");
        MANAGER.grab_symbols();
        MANAGER.manage(MANAGER_FOLDER, MANAGER_STATUS_FILE);
    }
    }
}

関連記事

コメント (0)