MQL5のイベントハンドラ関数を完全解説。OnInit・OnDeinit・OnTick・OnTimer・OnChartEvent・OnTrade・OnTester・OnBookEventなど、EA・インジケーター・スクリプトの動作を制御するすべてのイベント関数をコード付きで解説します。
MQL5のイベント駆動モデル
MQL5のプログラムはイベント駆動型です。特定のイベントが発生すると、対応するハンドラ関数が自動的に呼び出されます。C言語のmain()のような「最初から最後まで順に実行」するモデルとは異なります。
| イベントハンドラ | 呼び出しタイミング | EA | インジ | スクリプト |
|---|---|---|---|---|
| OnInit() | プログラム起動時 | ○ | ○ | × |
| OnDeinit() | プログラム終了時 | ○ | ○ | × |
| OnTick() | 新しいティック受信時 | ○ | × | × |
| OnCalculate() | 価格データ更新時 | × | ○ | × |
| OnTimer() | タイマーイベント発生時 | ○ | ○ | × |
| OnChartEvent() | チャート操作時 | ○ | ○ | × |
| OnTrade() | 取引イベント発生時 | ○ | × | × |
| OnTradeTransaction() | 取引トランザクション時 | ○ | × | × |
| OnTester() | バックテスト終了時 | ○ | × | × |
| OnTesterInit() | 最適化開始時 | ○ | × | × |
| OnTesterPass() | 最適化の各パス完了時 | ○ | × | × |
| OnTesterDeinit() | 最適化終了時 | ○ | × | × |
| OnBookEvent() | 板情報更新時 | ○ | ○ | × |
| OnStart() | スクリプト実行時 | × | × | ○ |
OnInit — 初期化
OnInit()はEA/インジケーターがチャートに適用されたとき、時間足が変更されたとき、inputパラメータが変更されたとき、リコンパイル時に呼び出されます。インジケーターハンドルの取得、変数の初期化、タイマー設定などを行います。
// 戻り値の型
int OnInit()
{
// INIT_SUCCEEDED (0) — 初期化成功
// INIT_FAILED — 初期化失敗(EAがチャートから削除される)
// INIT_PARAMETERS_INCORRECT — パラメータが不正
// INIT_AGENT_NOT_SUITABLE — テストエージェントが不適合
return INIT_SUCCEEDED;
}実践的なOnInit
#include <Trade\Trade.mqh>
CTrade trade;
int maHandle, rsiHandle;
input int MA_Period = 20;
input int RSI_Period = 14;
input double Lot = 0.1;
input long MagicNumber = 12345;
int OnInit()
{
// 1. パラメータの検証
if(MA_Period < 1 || RSI_Period < 1)
{
Print("エラー: 期間は1以上を指定してください");
return INIT_PARAMETERS_INCORRECT;
}
if(Lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
{
Print("エラー: ロット数が最小ロット未満です");
return INIT_PARAMETERS_INCORRECT;
}
// 2. インジケーターハンドルの取得
maHandle = iMA(_Symbol, PERIOD_CURRENT, MA_Period, 0, MODE_EMA, PRICE_CLOSE);
rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
if(maHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE)
{
Print("インジケーター作成失敗");
return INIT_FAILED;
}
// 3. トレードオブジェクトの設定
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
// 4. タイマー設定(必要な場合)
EventSetTimer(60); // 60秒ごと
Print("EA初期化完了: ", _Symbol, " ", EnumToString(_Period));
return INIT_SUCCEEDED;
}OnInit()でINIT_FAILEDを返すとEAはチャートから削除されます。致命的なエラー以外ではINIT_SUCCEEDEDを返し、OnTick()内でエラー処理するのが一般的です。
OnDeinit — 終了処理
OnDeinit()はEA/インジケーターが終了する直前に呼ばれます。リソースの解放、タイマーの停止、ファイルのクローズなどを行います。引数のreasonで終了理由がわかります。
void OnDeinit(const int reason)
{
// インジケーターハンドルの解放
if(maHandle != INVALID_HANDLE) IndicatorRelease(maHandle);
if(rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle);
// タイマー停止
EventKillTimer();
// チャートオブジェクト削除(必要な場合)
ObjectsDeleteAll(0, "EA_"); // "EA_"で始まるオブジェクトを全削除
// 終了理由をログに出力
string reasons[] = {
"プログラム削除", // 0: REASON_PROGRAM
"手動削除", // 1: REASON_REMOVE
"リコンパイル", // 2: REASON_RECOMPILE
"時間足/銘柄変更", // 3: REASON_CHARTCHANGE
"チャート閉鎖", // 4: REASON_CHARTCLOSE
"パラメータ変更", // 5: REASON_PARAMETERS
"別アカウント接続", // 6: REASON_ACCOUNT
"テンプレート適用", // 7: REASON_TEMPLATE
"OnInit失敗", // 8: REASON_INITFAILED
"ターミナル終了" // 9: REASON_CLOSE
};
if(reason >= 0 && reason < ArraySize(reasons))
Print("EA終了: ", reasons[reason]);
else
Print("EA終了: reason=", reason);
}OnDeinit reason定数一覧
| 定数 | 値 | 意味 |
|---|---|---|
| REASON_PROGRAM | 0 | ExpertRemove()で削除 |
| REASON_REMOVE | 1 | チャートから手動削除 |
| REASON_RECOMPILE | 2 | リコンパイル |
| REASON_CHARTCHANGE | 3 | 時間足または銘柄の変更 |
| REASON_CHARTCLOSE | 4 | チャートが閉じられた |
| REASON_PARAMETERS | 5 | inputパラメータの変更 |
| REASON_ACCOUNT | 6 | 別アカウントに接続 |
| REASON_TEMPLATE | 7 | テンプレート適用 |
| REASON_INITFAILED | 8 | OnInit()が0以外を返した |
| REASON_CLOSE | 9 | ターミナル終了 |
OnTick — ティックイベント
OnTick()はEA専用のイベントハンドラで、新しいティック(価格変動)が受信されるたびに呼び出されます。EAのメインロジック(エントリー判定、ポジション管理等)はすべてこの中に書きます。
void OnTick()
{
// 1. 新しいバーの検出(バー確定時のみ処理する場合)
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime == lastBarTime)
return; // 同じバー内の2回目以降のティックは無視
lastBarTime = currentBarTime;
// 2. インジケーター値の取得
double ma[];
ArraySetAsSeries(ma, true);
if(CopyBuffer(maHandle, 0, 0, 3, ma) < 3) return;
// 3. エントリー条件の判定
// ...
// 4. ポジション管理
// ...
}OnTick()はティックが来ない限り呼ばれません。週末やメンテナンス中は一切実行されません。時間に基づく処理が必要な場合はOnTimer()を併用してください。
新しいバーの検出パターン
// 方法1: iTime()で比較(シンプル)
bool IsNewBar()
{
static datetime lastBar = 0;
datetime current = iTime(_Symbol, PERIOD_CURRENT, 0);
if(current != lastBar)
{
lastBar = current;
return true;
}
return false;
}
// 方法2: iBars()で比較(より軽量)
bool IsNewBar2()
{
static int lastBars = 0;
int current = iBars(_Symbol, PERIOD_CURRENT);
if(current != lastBars)
{
lastBars = current;
return true;
}
return false;
}
// 使用例
void OnTick()
{
if(!IsNewBar()) return;
// ここはバー確定時のみ実行される
Print("新しいバー: ", iTime(_Symbol, PERIOD_CURRENT, 0));
}OnTimer — タイマーイベント
OnTimer()はタイマーイベントで定期的に呼び出されます。ティックに依存しない定期処理(パネル更新、ログ記録、ニュース監視等)に使用します。
int OnInit()
{
// 秒単位でタイマー設定
EventSetTimer(60); // 60秒ごと
// ミリ秒単位(より精密)
// EventSetMillisecondTimer(500); // 500msごと
return INIT_SUCCEEDED;
}
void OnTimer()
{
// 60秒ごとに呼ばれる
Print("Timer: ", TimeCurrent());
// ダッシュボード更新
UpdateDashboard();
// 日次レポート(特定時刻に処理)
MqlDateTime dt;
TimeCurrent(dt);
if(dt.hour == 23 && dt.min == 59)
GenerateDailyReport();
}
void OnDeinit(const int reason)
{
EventKillTimer(); // タイマー停止(必須)
}EventSetMillisecondTimer()を短い間隔で設定するとCPU負荷が高くなります。100ms以下は避けてください。また、タイマーは1つのプログラムにつき1つだけです。2回目のEventSetTimer()は前の設定を上書きします。
OnChartEvent — チャートイベント
OnChartEvent()はチャート上でユーザーが操作を行ったときに呼ばれます。ボタンクリック、キーボード入力、マウス移動、オブジェクトのドラッグ等を検知できます。EA上にGUIパネルを作る場合に必須です。
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
// ボタンクリック
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == "btnBuy")
{
Print("買いボタンがクリックされました");
// ボタン状態をリセット
ObjectSetInteger(0, "btnBuy", OBJPROP_STATE, false);
}
if(sparam == "btnSell")
{
Print("売りボタンがクリックされました");
ObjectSetInteger(0, "btnSell", OBJPROP_STATE, false);
}
}
// キーボード入力
if(id == CHARTEVENT_KEYDOWN)
{
// lparamにキーコードが入る
if(lparam == 'B')
Print("Bキーが押されました");
if(lparam == 27) // ESCキー
Print("ESCキーが押されました");
}
// マウスクリック
if(id == CHARTEVENT_CLICK)
{
int x = (int)lparam;
int y = (int)dparam;
Print("クリック位置: x=", x, " y=", y);
// ピクセル座標を時間・価格に変換
datetime time;
double price;
int window;
if(ChartXYToTimePrice(0, x, y, window, time, price))
Print("時間=", time, " 価格=", price);
}
// カスタムイベント(EventChartCustom()で送信)
if(id >= CHARTEVENT_CUSTOM)
{
int customId = id - CHARTEVENT_CUSTOM;
Print("カスタムイベント: id=", customId, " ", sparam);
}
}CHARTEVENT定数一覧
| 定数 | 意味 | lparam | dparam | sparam |
|---|---|---|---|---|
| CHARTEVENT_KEYDOWN | キー押下 | キーコード | 繰り返し回数 | キー状態 |
| CHARTEVENT_MOUSE_MOVE | マウス移動 | X座標 | Y座標 | フラグ |
| CHARTEVENT_OBJECT_CREATE | オブジェクト作成 | - | - | オブジェクト名 |
| CHARTEVENT_OBJECT_CHANGE | オブジェクト変更 | - | - | オブジェクト名 |
| CHARTEVENT_OBJECT_DELETE | オブジェクト削除 | - | - | オブジェクト名 |
| CHARTEVENT_CLICK | チャートクリック | X座標 | Y座標 | - |
| CHARTEVENT_OBJECT_CLICK | オブジェクトクリック | X座標 | Y座標 | オブジェクト名 |
| CHARTEVENT_OBJECT_DRAG | オブジェクト移動 | - | - | オブジェクト名 |
| CHARTEVENT_OBJECT_ENDEDIT | テキスト編集完了 | - | - | オブジェクト名 |
| CHARTEVENT_CHART_CHANGE | チャートプロパティ変更 | - | - | - |
| CHARTEVENT_CUSTOM | カスタムイベント | 任意 | 任意 | 任意 |
ボタン付きパネルの作成例
#include <Trade\Trade.mqh>
CTrade trade;
void CreatePanel()
{
// 買いボタン
ObjectCreate(0, "btnBuy", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "btnBuy", OBJPROP_XDISTANCE, 20);
ObjectSetInteger(0, "btnBuy", OBJPROP_YDISTANCE, 30);
ObjectSetInteger(0, "btnBuy", OBJPROP_XSIZE, 100);
ObjectSetInteger(0, "btnBuy", OBJPROP_YSIZE, 30);
ObjectSetString(0, "btnBuy", OBJPROP_TEXT, "BUY");
ObjectSetInteger(0, "btnBuy", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "btnBuy", OBJPROP_BGCOLOR, clrDodgerBlue);
ObjectSetInteger(0, "btnBuy", OBJPROP_FONTSIZE, 10);
// 売りボタン
ObjectCreate(0, "btnSell", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "btnSell", OBJPROP_XDISTANCE, 130);
ObjectSetInteger(0, "btnSell", OBJPROP_YDISTANCE, 30);
ObjectSetInteger(0, "btnSell", OBJPROP_XSIZE, 100);
ObjectSetInteger(0, "btnSell", OBJPROP_YSIZE, 30);
ObjectSetString(0, "btnSell", OBJPROP_TEXT, "SELL");
ObjectSetInteger(0, "btnSell", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "btnSell", OBJPROP_BGCOLOR, clrCrimson);
ObjectSetInteger(0, "btnSell", OBJPROP_FONTSIZE, 10);
// 全決済ボタン
ObjectCreate(0, "btnCloseAll", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "btnCloseAll", OBJPROP_XDISTANCE, 240);
ObjectSetInteger(0, "btnCloseAll", OBJPROP_YDISTANCE, 30);
ObjectSetInteger(0, "btnCloseAll", OBJPROP_XSIZE, 100);
ObjectSetInteger(0, "btnCloseAll", OBJPROP_YSIZE, 30);
ObjectSetString(0, "btnCloseAll", OBJPROP_TEXT, "CLOSE ALL");
ObjectSetInteger(0, "btnCloseAll", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "btnCloseAll", OBJPROP_BGCOLOR, clrDimGray);
ChartRedraw();
}
void OnChartEvent(const int id, const long &lparam,
const double &dparam, const string &sparam)
{
if(id != CHARTEVENT_OBJECT_CLICK) return;
if(sparam == "btnBuy")
{
trade.Buy(0.1, _Symbol);
ObjectSetInteger(0, "btnBuy", OBJPROP_STATE, false);
}
else if(sparam == "btnSell")
{
trade.Sell(0.1, _Symbol);
ObjectSetInteger(0, "btnSell", OBJPROP_STATE, false);
}
else if(sparam == "btnCloseAll")
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
trade.PositionClose(ticket);
}
ObjectSetInteger(0, "btnCloseAll", OBJPROP_STATE, false);
}
}OnTrade — 取引イベント
OnTrade()は注文の発注・変更・削除、ポジションのオープン・クローズ・変更など、取引に関するイベントが発生したときに呼ばれます。OnTradeTransaction()よりシンプルですが、詳細な情報は取得できません。
void OnTrade()
{
// ポジション数の変化を検知
static int lastPositions = 0;
int currentPositions = PositionsTotal();
if(currentPositions > lastPositions)
Print("新しいポジションがオープンしました 現在: ", currentPositions);
else if(currentPositions < lastPositions)
Print("ポジションがクローズされました 現在: ", currentPositions);
lastPositions = currentPositions;
// 待機注文の変化を検知
static int lastOrders = 0;
int currentOrders = OrdersTotal();
if(currentOrders != lastOrders)
{
Print("待機注文数が変化: ", lastOrders, " → ", currentOrders);
lastOrders = currentOrders;
}
}OnCalculate — インジケーター計算
OnCalculate()はカスタムインジケーター専用のイベントハンドラです。価格データが更新されるたびに呼ばれ、インジケーターバッファに値を格納します。2つのオーバーロードがあります。
// 形式1: OHLCVデータを受け取る(一般的)
int OnCalculate(const int rates_total, // バー数
const int prev_calculated, // 前回計算済みバー数
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// 最初の呼び出しではprev_calculated=0(全バー計算)
// 2回目以降はprev_calculated=rates_total-1(最新バーのみ再計算)
int start = (prev_calculated == 0) ? 0 : prev_calculated - 1;
for(int i = start; i < rates_total; i++)
{
// ここでインジケーターバッファに値をセット
// Buffer[i] = ... ;
}
return rates_total; // 次回のprev_calculatedに渡される
}
// 形式2: 単一のデータ配列を受け取る(他のインジケーターに適用する場合)
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin, // 有効データの開始位置
const double &price[]) // 適用価格データ
{
int start = (prev_calculated == 0) ? begin : prev_calculated - 1;
for(int i = start; i < rates_total; i++)
{
// price[i] は選択された適用価格
// Buffer[i] = ... ;
}
return rates_total;
}カスタムSMAインジケーターの完全例
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_label1 "My SMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
input int Period = 20;
double SMA_Buffer[];
int OnInit()
{
SetIndexBuffer(0, SMA_Buffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, Period - 1);
IndicatorSetString(INDICATOR_SHORTNAME, "MySMA(" + IntegerToString(Period) + ")");
return INIT_SUCCEEDED;
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
if(rates_total < Period) return 0;
int start;
if(prev_calculated == 0)
{
start = begin + Period - 1;
// 最初のSMAを計算
double sum = 0;
for(int i = begin; i < start; i++)
sum += price[i];
sum += price[start];
SMA_Buffer[start] = sum / Period;
start++;
}
else
start = prev_calculated - 1;
// 逐次計算(前のSMAから差分で計算、高速)
for(int i = start; i < rates_total; i++)
SMA_Buffer[i] = SMA_Buffer[i - 1] + (price[i] - price[i - Period]) / Period;
return rates_total;
}OnTester — バックテスト終了時
OnTester()はストラテジーテスターでのバックテストが完了した直後に呼ばれます。カスタム最適化基準を返すことができます。
double OnTester()
{
// テスト結果の統計を取得
double profit = TesterStatistics(STAT_PROFIT);
double maxDD = TesterStatistics(STAT_EQUITY_DDREL_PERCENT);
int totalTrades = (int)TesterStatistics(STAT_TRADES);
double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
double sharpe = TesterStatistics(STAT_SHARPE_RATIO);
Print("=== バックテスト結果 ===");
Print("純利益: ", profit);
Print("最大DD: ", maxDD, "%");
Print("取引数: ", totalTrades);
Print("PF: ", profitFactor);
Print("シャープ: ", sharpe);
// カスタム最適化基準を返す
// 例: 利益をDDで割ったリカバリーファクター
if(maxDD == 0) return 0;
if(totalTrades < 30) return 0; // 取引数が少なすぎる場合は0
double recoveryFactor = profit / (maxDD / 100.0 * TesterStatistics(STAT_INITIAL_DEPOSIT));
return recoveryFactor; // この値が最大化される
}OnTester()の戻り値は、最適化で「カスタム最大」を選択した場合の最適化基準になります。リカバリーファクター、シャープレシオ、プロフィットファクターなど、自分にとって重要な指標を返してください。
OnTesterInit / OnTesterPass / OnTesterDeinit — 最適化制御
最適化の前後に呼ばれるイベントハンドラです。最適化の全体管理、各パスの結果収集、フレーム処理に使用します。
// 最適化開始前(メインチャートのEAで呼ばれる)
void OnTesterInit()
{
Print("最適化を開始します...");
}
// 各最適化パスが完了するたびに呼ばれる
void OnTesterPass()
{
// FrameFirst() / FrameNext() でフレームデータを読み取る
ulong pass;
string name;
long id;
double value;
if(FrameFirst())
{
FrameInputs(name, pass);
FrameNext(pass, name, id, value);
PrintFormat("パス #%d: %s = %.2f", pass, name, value);
}
}
// 最適化完了後
void OnTesterDeinit()
{
Print("最適化が完了しました");
}
// テスターのOnTester()内でフレームを送信
double OnTester()
{
double result = TesterStatistics(STAT_PROFIT);
// フレームとしてメインチャートに送信
FrameAdd("Profit", 1, result, NULL);
return result;
}OnBookEvent — 板情報イベント
OnBookEvent()は板情報(Depth of Market / DOM)が更新されたときに呼ばれます。事前にMarketBookAdd()で購読を開始する必要があります。
int OnInit()
{
// 板情報の購読を開始
if(!MarketBookAdd(_Symbol))
Print("板情報の購読に失敗: ", GetLastError());
return INIT_SUCCEEDED;
}
void OnBookEvent(const string &symbol)
{
if(symbol != _Symbol) return;
MqlBookInfo book[];
if(MarketBookGet(_Symbol, book))
{
for(int i = 0; i < ArraySize(book); i++)
{
PrintFormat("Type=%s Price=%.5f Volume=%d",
(book[i].type == BOOK_TYPE_SELL) ? "SELL" : "BUY",
book[i].price, (int)book[i].volume);
}
}
}
void OnDeinit(const int reason)
{
MarketBookRelease(_Symbol); // 購読解除
}OnStart — スクリプト実行
OnStart()はスクリプト専用のイベントハンドラです。スクリプトはチャートに適用されるとOnStart()が1回だけ実行され、完了後に自動的に削除されます。
// 全ポジション一括決済スクリプト
#include <Trade\Trade.mqh>
void OnStart()
{
CTrade trade;
int closed = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(trade.PositionClose(ticket))
closed++;
else
Print("決済失敗: #", ticket, " Error=", GetLastError());
}
Print("決済完了: ", closed, "ポジション");
// スクリプトはここで自動的に終了・削除される
}カスタムイベント(EventChartCustom)
EventChartCustom()を使うと、自分でイベントを発生させてOnChartEvent()で受け取ることができます。EAの異なるモジュール間の通信や、非同期処理の通知に使えます。
// カスタムイベントIDの定義
#define EVENT_TRADE_OPENED 0
#define EVENT_TRADE_CLOSED 1
#define EVENT_SL_HIT 2
// イベントを発火
void NotifyTradeOpened(ulong ticket, double price)
{
EventChartCustom(0, // チャートID(0=自分のチャート)
EVENT_TRADE_OPENED, // カスタムイベントID
(long)ticket, // lparam
price, // dparam
"Trade Opened" // sparam
);
}
// イベントを受信
void OnChartEvent(const int id, const long &lparam,
const double &dparam, const string &sparam)
{
if(id == CHARTEVENT_CUSTOM + EVENT_TRADE_OPENED)
{
Print("取引オープン通知: ticket=", lparam, " price=", dparam);
// ここでGUI更新、ログ記録等を行う
}
else if(id == CHARTEVENT_CUSTOM + EVENT_TRADE_CLOSED)
{
Print("取引クローズ通知: ticket=", lparam, " PL=", dparam);
}
}イベントの実行順序
MQL5のイベントハンドラは以下の優先順位で実行されます。同時に複数のイベントがキューに入った場合、この順序で処理されます。
- OnInit() — 最初に1回だけ
- OnTick() / OnCalculate() — 最優先で処理
- OnTimer() — ティックイベントの次
- OnTrade() / OnTradeTransaction() — 取引イベント
- OnChartEvent() — ユーザー操作イベント
- OnBookEvent() — 板情報イベント
- OnDeinit() — 最後に1回だけ
すべてのイベントハンドラはシングルスレッドで実行されます。OnTick()の処理中にOnTimer()が呼ばれることはありません。OnTick()の処理に時間がかかると、その間に発生したタイマーイベントやチャートイベントはキューに溜まり、順番に処理されます。
プロが開発したEAをお探しの方は → シストレ.COM EA一覧





