MQL5のdatetime完全ガイド|時間取得・変換・計算・タイムゾーン対応まで徹底解説

目次

1. MQL5の日時(datetime)とは何か

1.1 datetimeの正体(内部は秒単位の整数)

MQL5におけるdatetimeは、「日時」を扱うための専用型ですが、その実体は1970年1月1日 00:00:00(UTC)からの経過秒数を表す整数(long型)です。
この形式は一般に「UNIX時間(Unix Time)」と呼ばれます。

つまり、datetimeは以下のようなイメージです。

  • 2024年1月1日 → 約1704067200(秒)
  • 数値として扱える → 計算が可能

実際のコード例:

datetime now = TimeCurrent();
Print(now);  // 数値として出力される

このように、datetimeは人間が読む日時ではなく、計算用の数値として扱われます。


1.2 なぜdatetime理解が重要か

datetimeの理解は、EA(自動売買)開発において極めて重要です。理由は以下の通りです。

  • エントリータイミングを制御するため
  • 特定時間帯(例:ロンドン時間)での売買制御
  • バックテストと実運用の挙動一致
  • ログ分析やデバッグ

特に重要なのは「時間=ロジックのトリガー」になる点です。

例えば:

  • 1時間ごとに処理する
  • 特定の時間だけエントリーする
  • 連続エントリーを防ぐ(クールダウン)

これらはすべてdatetimeの正しい扱いが前提です。

誤るとどうなるか:

  • 意図しない時間にエントリー
  • EAが暴走
  • バックテストは正常なのに本番で崩壊

これは実運用で最も多い致命バグの一つです。


1.3 つまずきポイント

datetime周りで初心者がつまずくポイントはほぼ共通しています。

よくある誤解①:文字列として扱う

datetimeは文字列ではありません。

// NG例
string time = TimeCurrent();  // 型が違う

→ 必ずdatetime型で扱い、必要に応じて変換する


よくある誤解②:タイムゾーンを無視する

MQL5の時間は基本的にブローカーのサーバー時間です。

  • 日本時間ではない
  • GMTとも一致しない場合あり

これを無視すると:

  • ロンドン時間で動かない
  • 指標回避が失敗

よくある誤解③:現在時間とバー時間の混同

初心者が最もハマるポイントです。

  • TimeCurrent() → 現在時刻
  • iTime() → ローソク足の開始時刻

この違いを理解していないと:

  • シグナル判定がズレる
  • 未確定バーで誤判定

よくある誤解④:秒単位であることを忘れる

datetimeは秒単位です。

// 60秒 = 1分
if(TimeCurrent() - last_time > 60)
{
   // 処理
}

→ 分や時間で扱う場合は変換が必要


1.4 注意点(実務視点)

  • datetimeは軽量で高速な数値型 → 計算に強い
  • ただし可読性は低い → 表示時は変換必須
  • 環境(ブローカー)によって時間基準が異なる場合がある

そのため、実務では以下が基本です:

  • 計算 → datetime(数値)
  • 表示 → 文字列変換
  • ロジック → サーバー時間 or GMTで統一

2. datetimeの基本操作(取得・表示・変換)

2.1 現在時間の取得(TimeCurrent / TimeLocal)

MQL5で現在時刻を取得するには、主に以下の2つを使います。

  • TimeCurrent()サーバー時間(推奨)
  • TimeLocal():PCのローカル時間
datetime server_time = TimeCurrent();
datetime local_time  = TimeLocal();

Print("Server Time: ", server_time);
Print("Local Time: ", local_time);

実務での使い分け

  • EAのロジック → TimeCurrent(必須)
  • ログ表示・デバッグ → TimeLocal(場合による)

理由:

  • トレードはサーバー時間基準で処理されるため
  • ローカル時間はPC環境に依存し再現性が低い

注意点

  • VPSとローカルで時間がズレることがある
  • バックテストではサーバー時間ベースで動く

2.2 datetime → 文字列変換(TimeToString)

datetimeはそのままでは数値なので、人間が読める形式に変換する必要があります。

datetime now = TimeCurrent();

// 日時(デフォルト)
string str1 = TimeToString(now);

// 日付のみ
string str2 = TimeToString(now, TIME_DATE);

// 時刻のみ
string str3 = TimeToString(now, TIME_SECONDS);

Print(str1);
Print(str2);
Print(str3);

主なフォーマット指定

  • TIME_DATE → YYYY.MM.DD
  • TIME_SECONDS → HH:MM:SS
  • TIME_MINUTES → HH:MM

実務ポイント

  • ログ出力では必須
  • デバッグ効率が大きく変わる

2.3 文字列 → datetime変換(StringToTime)

外部入力や設定値からdatetimeを生成する場合に使用します。

string time_str = "2024.01.01 12:00";
datetime dt = StringToTime(time_str);

Print(dt);

フォーマットのルール

  • "YYYY.MM.DD HH:MM" 形式が基本
  • 秒も指定可能 "YYYY.MM.DD HH:MM:SS"

失敗しやすいポイント

  • "2024-01-01" → NG(区切りは「.」)
  • "01/01/2024" → NG(形式違い)

2.4 よくある失敗

失敗①:サーバー時間とローカル時間の混在

// NGパターン
if(TimeLocal() - last_time > 60)

→ ロジックが環境依存になり再現性が崩壊


失敗②:フォーマット不一致

datetime dt = StringToTime("2024-01-01"); // 失敗する可能性あり

→ 正しい形式を使う


失敗③:バックテストと実運用のズレ

  • バックテスト → 固定されたサーバー時間
  • 実運用 → ブローカー依存

→ 時間条件ロジックがズレる


失敗④:datetimeを直接比較して誤動作

if(TimeCurrent() == target_time)

→ ほぼ成立しない(秒単位のズレ)

→ 正しくは:

if(TimeCurrent() >= target_time)

2.5 実務での基本テンプレ(推奨)

datetime now = TimeCurrent();

// 表示用
string now_str = TimeToString(now, TIME_SECONDS);
Print("Current Time: ", now_str);

// 比較用
if(now - last_time > 60)
{
   Print("1分経過");
   last_time = now;
}

2.6 注意点まとめ

  • ロジックは必ず TimeCurrent基準
  • 表示は TimeToString
  • 外部入力は StringToTime
  • 比較は「差分」で行う(==は避ける)

3. バー時間(iTime / CopyTime)と現在時間の違い

3.1 iTimeとは何か(ローソク足の開始時刻)

iTime()は、指定したシンボル・時間足・バー位置におけるローソク足の「開始時刻」を取得する関数です。

datetime bar_time = iTime(_Symbol, PERIOD_M5, 0);
Print(TimeToString(bar_time, TIME_SECONDS));

引数の意味

  • _Symbol → 通貨ペア(現在チャート)
  • PERIOD_M5 → 時間足(5分足)
  • 0 → 最新バー(現在進行中)

重要な理解

  • iTime(..., 0)未確定バーの開始時刻
  • iTime(..., 1)直前の確定バー

3.2 TimeCurrentとの違い(最重要ポイント)

この違いを理解していないと、EAはほぼ確実にバグります。

項目TimeCurrent()iTime()
意味現在時刻バーの開始時刻
更新タイミング常時変化バー更新時のみ
用途時間制御シグナル判定

実例

datetime now = TimeCurrent();
datetime bar = iTime(_Symbol, PERIOD_M5, 0);

Print("Now: ", TimeToString(now));
Print("Bar: ", TimeToString(bar));

nowは秒単位で変化
barは5分ごとにしか変わらない


3.3 よくある致命的ミス

ミス①:未確定バーでシグナル判定

// NG例
datetime bar_time = iTime(_Symbol, PERIOD_M5, 0);

→ 未確定バーを使うと:

  • 条件が変わる
  • ダマシシグナル増加
  • バックテストと乖離

正解

datetime bar_time = iTime(_Symbol, PERIOD_M5, 1);

→ 確定バーを使う


ミス②:TimeCurrentでバー判定

// NG例
if(TimeCurrent() - last_bar_time > 300)

→ バー更新と一致しない

正解

datetime current_bar = iTime(_Symbol, PERIOD_M5, 0);

if(current_bar != last_bar_time)
{
   Print("新しいバーが形成された");
   last_bar_time = current_bar;
}

3.4 CopyTimeの使いどころ(複数バー取得)

CopyTime()は、複数のバー時間をまとめて取得する関数です。

datetime times[];

int copied = CopyTime(_Symbol, PERIOD_M5, 0, 3, times);

if(copied > 0)
{
   for(int i=0; i<copied; i++)
   {
      Print(TimeToString(times[i]));
   }
}

特徴

  • 配列で複数取得できる
  • インジケータや分析系で有用
  • iTimeより高速(まとめ取得)

3.5 実務での基本パターン(超重要)

新バー検知テンプレ

static datetime last_bar_time = 0;

datetime current_bar = iTime(_Symbol, PERIOD_M5, 0);

if(current_bar != last_bar_time)
{
   last_bar_time = current_bar;

   // ここにロジックを書く(1バー1回だけ実行)
   Print("New Bar Detected");
}

メリット

  • 無駄な処理を削減
  • 再現性が高い
  • バックテストと一致しやすい

3.6 注意点まとめ

  • シグナル判定は 確定バー(shift=1)
  • 新バー検知は shift=0で比較
  • TimeCurrentはバー判定に使わない
  • CopyTimeは複数処理で有効

3.7 つまずきポイント

  • 「現在時間」と「バー時間」を混同する
  • 未確定バーでロジックを組む
  • バー更新検知をTimeCurrentでやる

→ すべて実運用で破綻する典型パターン

4. datetimeの分解(年・月・日・時刻の取得)

4.1 TimeToStructとは何か(datetimeを分解する基本手法)

datetimeは数値(秒)なので、そのままでは「何時・何曜日か」を判断できません。
そのため、TimeToStruct()を使って「構造体(MqlDateTime)」に分解します。

datetime now = TimeCurrent();

MqlDateTime dt;
TimeToStruct(now, dt);

Print("Year: ", dt.year);
Print("Month: ", dt.mon);
Print("Day: ", dt.day);
Print("Hour: ", dt.hour);
Print("Minute: ", dt.min);
Print("Second: ", dt.sec);

MqlDateTimeの主なフィールド

  • year → 年
  • mon → 月
  • day → 日
  • hour → 時
  • min → 分
  • sec → 秒
  • day_of_week → 曜日(0=日曜)

4.2 実務での使い方(時間フィルター)

EA開発で最も多い用途は「時間条件によるフィルタリング」です。

特定時間のみエントリー

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

if(dt.hour >= 9 && dt.hour < 15)
{
   Print("東京時間帯");
}

曜日フィルター

if(dt.day_of_week == 1) // 月曜日
{
   Print("Monday");
}

4.3 よくある失敗

失敗①:TimeToStructを使わずに無理やり判定

// NG例
if(TimeCurrent() % 86400 < 3600)

→ 可読性が低く、ミスの温床


失敗②:曜日の数値を誤認

// NG認識:1=日曜日と思っている

正しくは:

  • 0 = 日曜
  • 1 = 月曜
  • 2 = 火曜

→ ここを間違えるとロジックがズレる


失敗③:サーバー時間を考慮しない

// 日本時間9時のつもり
if(dt.hour == 9)

→ サーバー時間がGMT+2ならズレる


4.4 実務での重要テクニック(時間帯補正)

ブローカー時間と日本時間がズレている場合、補正が必要です。

例:GMT+2 → 日本時間(+9)に変換

datetime now = TimeCurrent();

// +7時間(日本との差)
datetime jst_time = now + 7 * 3600;

MqlDateTime dt;
TimeToStruct(jst_time, dt);

Print("JST Hour: ", dt.hour);

ポイント

  • 秒単位なので「3600」で時間変換
  • ブローカーごとに差が異なる

4.5 実務テンプレ(時間フィルター)

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 平日かつ指定時間のみ
if(dt.day_of_week >= 1 && dt.day_of_week <= 5)
{
   if(dt.hour >= 9 && dt.hour < 17)
   {
      Print("Trade Allowed");
   }
}

4.6 注意点まとめ

  • datetimeは必ず分解して使う(TimeToStruct)
  • 曜日は「0=日曜」に注意
  • 時間はサーバー基準
  • 必要なら自前でタイムゾーン補正

4.7 つまずきポイント

  • 時間ズレに気づかない
  • 曜日番号を誤解する
  • 秒→時間変換を忘れる
  • ローカル時間で判定してしまう

→ 実運用で再現性が崩れる原因

5. datetimeの計算(加算・減算・差分処理)

5.1 datetimeは「秒の整数」なので計算できる

datetimeは内部的に「秒単位の整数(long)」なので、通常の数値と同じように加算・減算が可能です。

datetime now = TimeCurrent();

// 1時間後
datetime after_1h = now + 3600;

// 5分前
datetime before_5m = now - (5 * 60);

Print(TimeToString(after_1h));
Print(TimeToString(before_5m));

基本換算

  • 1分 = 60秒
  • 1時間 = 3600秒
  • 1日 = 86400秒

5.2 差分を使った時間制御(最重要パターン)

datetimeの真価は「差分」での制御にあります。

一定時間経過後に処理

static datetime last_time = 0;

datetime now = TimeCurrent();

if(now - last_time > 60) // 60秒経過
{
   Print("1分経過");
   last_time = now;
}

実務メリット

  • 再現性が高い
  • シンプルでバグりにくい
  • VPS / 本番環境でも安定

5.3 よくある致命的ミス

ミス①:完全一致比較

// NG例
if(TimeCurrent() == target_time)

→ ほぼ成立しない(秒単位でズレる)

正解

if(TimeCurrent() >= target_time)

ミス②:分・時間単位でそのまま比較

// NG例
if(now - last_time > 1) // 1分のつもり

→ 実際は「1秒」

正解

if(now - last_time > 60)

ミス③:datetimeを直接インクリメント

// NG例
now++;

→ 動くが意味が不明確(1秒進む)

→ 可読性が低くバグの原因


5.4 実務で使う典型パターン

クールダウン(連続エントリー防止)

static datetime last_trade_time = 0;

datetime now = TimeCurrent();

// 5分間は再エントリー禁止
if(now - last_trade_time < 300)
{
   return;
}

// エントリー処理
last_trade_time = now;

指定時間後に実行

datetime start_time = TimeCurrent();
datetime execute_time = start_time + 600; // 10分後

if(TimeCurrent() >= execute_time)
{
   Print("10分経過");
}

日跨ぎ判定

MqlDateTime dt1, dt2;

TimeToStruct(TimeCurrent(), dt1);
TimeToStruct(last_time, dt2);

if(dt1.day != dt2.day)
{
   Print("日付が変わった");
}

5.5 CopyTimeと組み合わせた時間計算

複数バーの時間差を扱う場合:

datetime times[];

CopyTime(_Symbol, PERIOD_M5, 0, 2, times);

// バー間の時間差(通常300秒)
int diff = (int)(times[0] - times[1]);

Print("Bar Interval: ", diff);

→ データ異常検知などに応用可能


5.6 注意点まとめ

  • datetimeは「秒」で扱う
  • 比較は「差分」で行う
  • 完全一致は使わない
  • 可読性のため明示的な秒換算を書く

5.7 つまずきポイント

  • 秒と分の混同
  • == 比較の使用
  • クールダウンロジックの不備
  • VPSとローカルで挙動がズレる

→ すべて実運用で損失に直結するポイント

6. 実践:時間制御ロジック(エントリー時間制限・ニュース回避)

6.1 時間制御の基本設計(ロジックとしての考え方)

時間制御は「条件分岐」ではなく、リスク制御の中核ロジックです。
単純に時間を制限するだけでなく、以下の目的で使います。

  • ボラティリティの低い時間帯を避ける
  • スプレッド拡大時間(ロールオーバー)を回避
  • 指標発表時の異常値動きを避ける
  • EAの過剰稼働を抑制

つまり、時間制御は期待値を安定させるためのフィルターです。


6.2 時間帯フィルター(最も基本)

指定時間のみエントリー

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 9:00〜17:00のみ許可
if(dt.hour < 9 || dt.hour >= 17)
{
   return; // エントリー禁止
}

実務ポイント

  • 「許可条件」より「禁止条件」で書く方が安全
  • ロジックの最上流に配置する

6.3 曜日フィルター(週末回避)

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 土日を除外
if(dt.day_of_week == 0 || dt.day_of_week == 6)
{
   return;
}

補足

  • 0 = 日曜
  • 6 = 土曜

6.4 ロールオーバー回避(重要)

多くのブローカーで以下の時間帯は危険です:

  • スプレッド拡大
  • 約定不安定
  • サーバー負荷増加

例:サーバー時間で23:50〜00:10を回避

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

if((dt.hour == 23 && dt.min >= 50) || (dt.hour == 0 && dt.min < 10))
{
   return;
}

6.5 ニュース回避ロジック(簡易版)

本格的には経済カレンダー連携が必要ですが、簡易的には時間ベースで回避可能です。

例:重要指標(21:30前後)を回避

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 21:25〜21:40を回避
if(dt.hour == 21 && dt.min >= 25 && dt.min <= 40)
{
   return;
}

注意

  • ブローカー時間に依存
  • 夏時間(DST)でズレる

6.6 複合フィルター(実務テンプレ)

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 土日除外
if(dt.day_of_week == 0 || dt.day_of_week == 6)
   return;

// ロールオーバー回避
if((dt.hour == 23 && dt.min >= 50) || (dt.hour == 0 && dt.min < 10))
   return;

// 時間帯制限(9:00〜17:00)
if(dt.hour < 9 || dt.hour >= 17)
   return;

// エントリー許可
Print("Trading Allowed");

6.7 よくある失敗

失敗①:時間条件の優先順位ミス

// 条件が曖昧で意図通り動かない
if(dt.hour >= 9 && dt.hour < 17 || dt.day_of_week == 1)

→ 括弧を明確にする


失敗②:サーバー時間を無視

  • JST基準で書いたつもりがズレる
  • ブローカー変更で崩壊

失敗③:バックテストだけ最適化

  • 特定時間だけ最適化 → 過学習
  • 実運用で崩れる

6.8 注意点(実務・期待値視点)

  • 時間制御は「利益を増やす」より「損失を減らす」役割
  • フィルターを増やしすぎると機会損失が増える
  • 必ずバックテスト+フォワードで検証する

6.9 つまずきポイント

  • 時間ズレ(サーバー vs JST)
  • DST(夏時間)未対応
  • 条件分岐の論理ミス
  • 過剰フィルタリング

→ EAの期待値を大きく歪める原因

7. datetime処理の最適化と設計パターン(再現性・高速化)

7.1 なぜ最適化が必要か(実務的な前提)

datetime処理は軽量ですが、OnTick内で毎回無駄に処理するとパフォーマンスと再現性が崩れます。

特に問題になるケース:

  • 毎TickでTimeToStructを実行
  • 毎Tickで複雑な時間判定
  • バー単位で十分なのにTick単位で処理

→ 無駄なCPU消費+ロジックのブレ


7.2 基本戦略:バー単位で処理する

最も重要な設計は「Tickではなくバーで動かす」です。

NGパターン(毎Tick実行)

MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);

// 毎Tickで実行される
if(dt.hour >= 9)
{
   // ロジック
}

推奨パターン(バー単位)

static datetime last_bar_time = 0;

datetime current_bar = iTime(_Symbol, PERIOD_M5, 0);

if(current_bar != last_bar_time)
{
   last_bar_time = current_bar;

   MqlDateTime dt;
   TimeToStruct(current_bar, dt);

   // 1バー1回のみ実行
   if(dt.hour >= 9)
   {
      Print("New bar logic");
   }
}

メリット

  • 計算量削減
  • バックテストと一致
  • ロジックの安定化

7.3 キャッシュ戦略(再計算を避ける)

datetime関連処理は「使い回す」ことが重要です。

NG例

TimeToStruct(TimeCurrent(), dt1);
TimeToStruct(TimeCurrent(), dt2);

→ 無駄に2回実行

改善

datetime now = TimeCurrent();

MqlDateTime dt;
TimeToStruct(now, dt);

7.4 タイムゾーン設計(固定化する)

時間ズレ問題を防ぐには「基準時間を統一」します。

推奨パターン

#define JST_OFFSET 7 * 3600  // 例:サーバー→JST

datetime now = TimeCurrent();
datetime jst = now + JST_OFFSET;

実務ポイント

  • 全ロジックをJST基準に統一
  • またはGMT基準に統一
  • 混在させない

7.5 時間判定の関数化(再利用性)

時間ロジックは関数化すると安定します。

bool IsTradingTime(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);

   if(dt.day_of_week == 0 || dt.day_of_week == 6)
      return false;

   if(dt.hour < 9 || dt.hour >= 17)
      return false;

   return true;
}

使用例:

if(!IsTradingTime(TimeCurrent()))
   return;

7.6 テスト再現性を高める設計

datetimeは「環境依存」になりやすいため、再現性設計が重要です。

NG

  • TimeLocal使用
  • 外部時間依存
  • 曖昧な時間条件

OK

  • TimeCurrent統一
  • 秒単位で明示
  • バー基準ロジック

7.7 よくある失敗

失敗①:Tick依存ロジック

→ VPS性能・スプレッドで挙動が変わる


失敗②:時間処理の重複

→ 無駄な計算+バグ増加


失敗③:タイムゾーン混在

→ バックテストと本番が一致しない


7.8 注意点まとめ

  • 基本は「バー単位処理」
  • datetimeはキャッシュして使う
  • タイムゾーンは統一
  • 関数化で再利用

7.9 つまずきポイント

  • Tickとバーの混同
  • 無駄な再計算
  • 時間基準のブレ
  • テストと本番の不一致

→ EAの信頼性を大きく損なう原因

8. まとめ+FAQ(datetimeでよくある疑問の解決)

8.1 まとめ(実務で押さえるべき核心)

MQL5のdatetimeは単なる日時ではなく、EAの挙動を決定する基盤ロジックです。
特に重要なポイントは以下に集約されます。

  • datetimeは「秒単位の整数」
  • ロジックは必ず TimeCurrent基準
  • シグナル判定は 確定バー(iTime shift=1)
  • 時間制御は「差分」で行う(==は使わない)
  • タイムゾーンは統一(JST or GMT)
  • Tickではなく「バー単位」で処理する

これらを守るだけで、再現性・安定性・期待値のブレが大幅に改善されます。


8.2 よくある失敗の総括

実務で頻発するミスを整理すると、ほぼ以下に集約されます。

  • サーバー時間を無視
  • 未確定バーを使用
  • == 比較による誤判定
  • 秒・分の混同
  • TimeLocal使用による環境依存
  • 過剰な時間フィルター

→ これらはすべて「再現性崩壊」に直結します。


8.3 FAQ(よくある質問)

Q1. TimeCurrentとTimeLocalはどちらを使うべき?

A. EAロジックは必ずTimeCurrentを使用します。TimeLocalはデバッグ用途に限定してください。


Q2. iTimeのshiftは0と1どちらを使うべき?

A.

  • シグナル判定 → shift=1(確定バー)
  • 新バー検知 → shift=0

Q3. datetimeの比較で「==」は使えますか?

A. ほぼ使えません。秒単位のズレが発生するため、必ず「>=」や差分で判定します。


Q4. 日本時間で制御したい場合はどうすればいい?

A. サーバー時間との差分(秒)を加算して補正します。ブローカーごとに異なるため固定値は要確認です。


Q5. バックテストと実運用で時間がズレるのはなぜ?

A.

  • ブローカーのサーバー時間差
  • DST(夏時間)
  • ローカル時間依存

が主な原因です。


Q6. 時間フィルターは多いほど良いですか?

A. いいえ。フィルター過多は機会損失を増やし、過学習の原因になります。


Q7. CopyTimeとiTimeはどちらを使うべき?

A.

  • 単発取得 → iTime
  • 複数取得 → CopyTime

Q8. datetimeの計算は重いですか?

A. 軽量です。ただしTimeToStructなどの変換は毎Tick実行を避け、必要最小限にしてください。

This website stores cookies on your computer. These cookies are used to provide a more personalized experience and to track your whereabouts around our website in compliance with the European General Data Protection Regulation. If you decide to to opt-out of any future tracking, a cookie will be setup in your browser to remember this choice for one year.

Accept or Deny