- 1 1. MQL5の日時(datetime)とは何か
- 2 2. datetimeの基本操作(取得・表示・変換)
- 3 3. バー時間(iTime / CopyTime)と現在時間の違い
- 4 4. datetimeの分解(年・月・日・時刻の取得)
- 5 5. datetimeの計算(加算・減算・差分処理)
- 6 6. 実践:時間制御ロジック(エントリー時間制限・ニュース回避)
- 7 7. datetime処理の最適化と設計パターン(再現性・高速化)
- 8 8. まとめ+FAQ(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.DDTIME_SECONDS→ HH:MM:SSTIME_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実行を避け、必要最小限にしてください。