MQL5で他の言語の実用的なモジュールを実装する(第3回):Pythonのscheduleモジュール、強化版OnTimerイベント
内容
- はじめに
- Pythonが提供するScheduleモジュールとは
- MQL5のScheduleクラス
- 特定の時間に関数を実行する
- タスクに引数を渡す
- 特定の時間までタスクを実行する
- スケジュールに関係なくすべてのジョブを実行する
- スケジュールの管理
- タイムゾーンの扱い
- 取引アプリでのscheduleモジュールの応用
- ScheduleとOnTimerの比較
- 結論
はじめに
プログラミングは、私たちの生活をより簡単にするためにあります。多くの重要で、時には退屈で繰り返しの作業を、人間が介入せずともコンピュータに自動化させることができるのです。たとえば、多くのテキストエディタにある自動保存機能がそうです。新しい単語を書くたびに保存を気にする必要はなく、エディタが自動で保存してくれるため、書くことに集中でき、予期せぬトラブルで作業が失われる心配もありません。
これは取引の世界でも同じで、取引を支援する多くの繰り返し作業やタスクを、コードで自動化したいことがあります。

MQL5プログラミング言語にはOnTimer関数があり、プログラマが設定した特定の時間間隔で特定の関数やコードを実行するのに便利です。
以下は、10秒ごとにOnTimer関数を実行する簡単な例です。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(10); //Creates a timer with 10 seconds period //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); //Destroy the timer after completing the work } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer(void) { Print("Ontimer called: ",TimeLocal()); //This line of code will be run after every 10 seconds }
出力結果は以下のとおりです。
MN 0 10:16:39.455 Schedule test (XAUUSD,D1) Ontimer called: 2025.07.21 10:16:39 CD 0 10:16:49.459 Schedule test (XAUUSD,D1) Ontimer called: 2025.07.21 10:16:49
この関数は基本的には機能しますが、複数の異なるスケジュールを同時に運用する柔軟性には欠けます。
OnTimerイベントハンドラをエキスパートアドバイザー(EA)やインジケーターで設定すると、1つの「タイマースケジュール」にしか依存できません。これは、プログラム内で異なるタスクを異なる時間間隔で実行したい場合には制約となります。
たとえば、ユーザーに日次、週次、月次の取引レポートを出力する場合などです。
PythonにはOnTimer関数に似たモジュールがありますが、関数を正確なタイミングで実行する能力がはるかに優れています。本記事ではそれを紹介し、MQL5で類似モジュールを実装します。
Pythonが提供するScheduleモジュールとは
Python job scheduling for humans(人間のためのPythonジョブスケジューリング)
特定の時間や曜日にタスクを実行するための、人間にやさしいPythonモジュールです。簡単で軽量なので、Python開発者は知っておくべきモジュールです。
MQL5のOnTimer関数とは異なり、このscheduleモジュールは単に特定の時間間隔でタスクを実行するだけでなく、特定のタスク(関数)をいつ、どのように実行するかをより詳細に指定する柔軟性があります。
以下はこのモジュールが提供する関数の一部です。
import schedule | 関数 | 説明 |
|---|---|
schedule.every(10).minutes.do(job) | OnTimerイベントと同様、10分ごとに job関数を実行 |
schedule.every().hour.do(job) | スクリプトの開始から1時間ごとにjob関数を実行 |
schedule.every().day.at("10:30").do(job) | 毎日特定のローカル時間10:30(24時間制)にjob関数を実行 |
schedule.every().monday.do(job) | 毎週月曜日に、スクリプト実行時刻に基づきjob関数を実行 |
schedule.every().wednesday.at("13:15").do(job) | 毎週水曜日の13:15にjob関数を実行 |
schedule.every().day.at("12:42", "Europe/Amsterdam").do(job) | 毎日12:42(ヨーロッパ/アムステルダム時間)にjob関数を実行 |
schedule.every().minute.at(":17").do(job) | 毎分17秒にjob関数を実行 |
これらはモジュールの基本的かつ重要な機能です。次に、MQL5で似たクラスを実装してみましょう。
MQL5のScheduleクラス
Pythonのscheduleモジュールは、特定の間隔で実行する各タスクに対して、別々の関数を用意する設計になっています。ここで使われるdo関数は、scheduleクラスの関数チェーンの終点として機能します。
schedule.every(10).minutes.do 同じような構文をMQL5で実現するには、CSchedule クラス内のいくつかの関数がクラス全体のインスタンスを返すようにする必要があります。ただし、dOという名前の関数だけはチェーンの終点として扱います。
class CSchedule { private: int m_period; //the number of seconds, minutes, etc to use time_intervals_enum m_unit; //time interval: minutes, hours, etc int m_time_seconds; //datetime in seconds JobFunction m_func; //The function to run for the current schedule public: int m_fixed_time; // time from midnight in seconds CSchedule(void); ~CSchedule(void); CSchedule* every(int period = 1); CSchedule* seconds(); CSchedule* minutes(); CSchedule* hours(); CSchedule* days(); CSchedule* weeks(); CSchedule* months(); CSchedule* years(); void dO(JobFunction func); }
この構文により、Pythonのscheduleモジュールと同様のフルーエントインターフェースを実現できます。
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { schedule.every().minutes().dO(runthis); }
everyという関数は、特定の時間枠ごとの間隔を設定するために非常に重要です。以下はその例です。
schedule.every(10).minutes()
は、「10分ごとに、dOという関数で指定された特定の関数を実行する」という意味になります。
この関数の核心部分では、与えられた期間(period)をm_periodという変数に代入し、クラス内部で保持します。
CSchedule* CSchedule::every(int period = 1) { m_period = period; return GetPointer(this); }
また、seconds、minutes、hoursなどの関数は、列挙型 time_intervals_enumで定義された全ての時間間隔オプションに応じて、時間枠変数を設定します。
enum time_intervals_enum
{
SECONDS,
MINUTES,
HOURS,
DAYS,
WEEKS,
MONTHS,
YEARS
}; CSchedule* CSchedule::seconds()
{
m_unit = SECONDS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::minutes()
{
m_unit = MINUTES;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::hours()
{
m_unit = HOURS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::days()
{
m_unit = DAYS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::weeks(void)
{
m_unit = WEEKS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::months(void)
{
m_unit = MONTHS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::years(void)
{
m_unit = YEARS;
return GetPointer(this);
} dOという名前の関数(すべてのスケジューリング関数の終点)を理解する前に、まず各ジョブ(関数)がどのように扱われ、schedule.mqhファイル内で保存されているかを理解しましょう。
//+------------------------------------------------------------------+ //| Handling and storing every job (task) used in the class CSchedule| //+------------------------------------------------------------------+ typedef void (*JobFunction)(); // For global functions struct jobs_struct { int prev_run; int next_run; int interval; JobFunction func; // Store the function pointer }; jobs_struct m_jobs[]; // Global job list void jobs_add(jobs_struct &jobs_array[], const jobs_struct &job) { uint size = ArraySize(jobs_array); ArrayResize(jobs_array, size + 1); jobs_array[size] = job; }
CScheduleクラスには自身を参照する関数があるため、クラス内で各ジョブオブジェクトを扱うのは複雑で混乱しやすく、エラーが発生しやすくなります。
そこで、m_jobsというグローバル配列を用意することで、クラス内で使用されるすべてのジョブを統一的に保存し、管理する方法を提供しています。この点については後ほど詳しく説明します。
dOという関数は、受け取った関数を指定された「スケジュール」に従って繰り返し実行します。また、関数が最後に実行された時刻や、次に実行される予定の時刻を計算します。
受け取った関数は、jobs_structという構造体に格納され、最後に実行された時刻や次回実行予定時刻など、他のジョブのプロパティと一緒に管理されます。
そして、この構造体の情報はすべて、jobs_struct型の配列であるm_jobsに格納されます。
jobs_struct m_jobs[]; // Global job list void CSchedule::dO(JobFunction func) { m_func = func; jobs_struct job; job.func = m_func; datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above job.next_run = job.prev_run + timedelta(m_period, m_unit); if (MQLInfoInteger(MQL_DEBUG)) Print("The first function run is schedule at: ", TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
ジョブが適切な配列に格納された後は、それを監視し、スケジュールされた時刻に関数を実行するための汎用関数が必要になります。
void CSchedule::run_pending() { int now = (int)TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if(now >= m_jobs[i].next_run) { if(m_jobs[i].func != NULL) m_jobs[i].func(); m_jobs[i].prev_run = m_jobs[i].next_run; // Recalculate next_run m_jobs[i].next_run += m_jobs[i].interval; if (MQLInfoInteger(MQL_DEBUG)) printf("Prev run: %s Next run: %s", TimeToString((datetime)m_jobs[i].prev_run, TIME_DATE|TIME_SECONDS), TimeToString((datetime)m_jobs[i].next_run, TIME_DATE|TIME_SECONDS)); } } }
このクラスを使って、最初のジョブをスケジュールしてみましょう。
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(10).seconds().dO(runthis); while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis() { Print(__FUNCTION__," called at: ",TimeLocal()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+
関数runthisを10秒ごとに呼び出します。スクリプトを停止するまで実行を続けるため、無限ループを使用します。
以下は、デバッグモードでスクリプトを実行した際のログ出力例です。
NO 0 14:51:56.301 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 14:52:06 GS 0 14:52:06.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:06 PJ 0 14:52:06.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:06 Next run: 2025.07.21 14:52:16 QH 0 14:52:16.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:16 GR 0 14:52:16.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:16 Next run: 2025.07.21 14:52:26 KP 0 14:52:26.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:26 FJ 0 14:52:26.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:26 Next run: 2025.07.21 14:52:36
同じクラスを使用して複数のスケジュールを設定できます。
void OnStart() { //--- schedule.every(10).seconds().dO(runthis); //run after every 10 seconds schedule.every().minute().dO(runthis2); //run on every minute while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis() { Print(__FUNCTION__," called at: ",TimeLocal()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis2() { Print("Hello world!, This function is called after a minute has passed"); }
出力結果は以下のとおりです。
FK 0 15:00:55.079 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 15:01:05 IL 0 15:00:55.079 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 15:01:55 ER 0 15:01:05.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:05 RK 0 15:01:05.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:05 Next run: 2025.07.21 15:01:15 OK 0 15:01:15.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:15 ES 0 15:01:15.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:15 Next run: 2025.07.21 15:01:25 IS 0 15:01:25.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:25 LJ 0 15:01:25.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:25 Next run: 2025.07.21 15:01:35 CK 0 15:01:35.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:35 KR 0 15:01:35.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:35 Next run: 2025.07.21 15:01:45 MP 0 15:01:45.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:45 FJ 0 15:01:45.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:45 Next run: 2025.07.21 15:01:55 GH 0 15:01:55.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:55 NM 0 15:01:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:55 Next run: 2025.07.21 15:02:05 KR 0 15:01:55.000 schedule test (XAUUSD,D1) Hello world!, This function is called after a minute has passed MH 0 15:01:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:55 Next run: 2025.07.21 15:02:55 NJ 0 15:02:05.001 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:05 HP 0 15:02:05.001 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:05 Next run: 2025.07.21 15:02:15 GR 0 15:02:15.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:15 LK 0 15:02:15.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:15 Next run: 2025.07.21 15:02:25 RK 0 15:02:25.001 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:25 FS 0 15:02:25.001 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:25 Next run: 2025.07.21 15:02:35 OS 0 15:02:35.004 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:35 JK 0 15:02:35.004 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:35 Next run: 2025.07.21 15:02:45 EK 0 15:02:45.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:45 KR 0 15:02:45.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:45 Next run: 2025.07.21 15:02:55 OP 0 15:02:55.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:55 EJ 0 15:02:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:55 Next run: 2025.07.21 15:03:05 FK 0 15:02:55.000 schedule test (XAUUSD,D1) Hello world!, This function is called after a minute has passed
特定の時間に関数を実行する
私たちはしばしば、関数(ジョブ)を非常に特定の時刻に実行したい場合があります。たとえば、取引セッションに応じて取引を開始する関数を、現地時間の19:00に実行する、といったケースです。
CScheduleクラスでは、atという名前の関数群が、この指定された正当な時刻にジョブを実行する役割を持っています。
例:
schedule.every().day().at(19, 10).dO(job);
これは、jobという関数を毎日19:10に実行するスケジュールを設定します。
MQL5でこの関数を実装するのはやや複雑です。なぜなら、秒、分、時、日、週など、各時間単位ごとに別々のクラスを返す設計にする必要があるからです。
class CSchedule { private: int m_period; //the number of seconds, minutes, etc to use time_intervals_enum m_unit; //time interval: minutes, hours, etc int m_time_seconds; //datetime in seconds JobFunction m_func; //The function to run for the current schedule bool has_fixed_time() const { return m_fixed_time > 0; } datetime TodaysDate(datetime dt) { // Extract year, month, day — and build a new datetime at 00:00:00 MqlDateTime tm; TimeToStruct(dt, tm); tm.hour = 0; tm.min = 0; tm.sec = 0; return StructToTime(tm); } public: int m_fixed_time; // time from midnight in seconds CSchedule(void); ~CSchedule(void); CSchedule* every(int period = 1); CSchedule* seconds(); CSchedule* minutes(); CSchedule* hours(); CSchedule* days(); CSchedule* weeks(); MinuteScheduleBuilder* minute(); HourScheduleBuilder* hour(); DayScheduleBuilder* day(); WeekScheduleBuilder* week();
すべての「Builder」クラス(クラス名がBuilderで終わるもの)には、atという関数があり、特定の時間間隔(時刻)を設定する役割を持っています。
また、dOという関数も存在し、これはCScheduleクラスから継承された同名関数です。
以下はWeekScheduleBuilderクラスの例です。
class CSchedule; //forward declaration | VERY IMPORTANT class WeekScheduleBuilder { protected: CSchedule *m_schedule; public: WeekScheduleBuilder(CSchedule *schedule_) { m_schedule = schedule_; } ~WeekScheduleBuilder(void) {}; CSchedule* at(ENUM_DAY_OF_WEEK dayofweek, uint hour=0, uint minutes = 0, uint seconds = 0) { if (CheckPointer(m_schedule) == POINTER_INVALID || m_schedule == NULL) return NULL; datetime now = TimeLocal(); MqlDateTime tm; TimeToStruct(now, tm); int today_dow = tm.day_of_week; //--- Compute days until target day (next week if it's the same day or already passed) int days_ahead = (int)dayofweek - today_dow; if (days_ahead < 0) days_ahead += 7; // ensure it's next week datetime next_target_date = now + timedelta(days_ahead, DAYS); MqlDateTime target_tm; TimeToStruct(next_target_date, target_tm); //--- setting the correct time target_tm.hour = (int)hour; target_tm.min = (int)minutes; target_tm.sec = (int)seconds; m_schedule.m_fixed_time = (int)StructToTime(target_tm); return m_schedule; } void dO(JobFunction func) { m_schedule.dO(func); } };
すべてのat関数は、引数として渡された特定の時刻(初回実行までの時間を含む)を受け取り、その時刻を秒単位に変換してm_fixed_timeという変数に代入します。
dO関数の内部では、受け取った時間が固定時刻(例:19:00)なのか、次回実行までの秒数・分数などのスケジュール時間なのかを判定する条件を設けています。これは、両者で処理方法が若干異なるためです。
void CSchedule::dO(JobFunction func) { m_func = func; jobs_struct job; job.func = m_func; datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above if (has_fixed_time()) { datetime scheduled_time = (datetime)m_fixed_time; //we add today's date to the fixed_time calculated //Add interval repeatedly until scheduled_time >= now while (scheduled_time <= now) scheduled_time += job.interval; //Schedule for the next time if the current time has passed job.next_run = (int)scheduled_time; } else { job.next_run = job.prev_run + job.interval; } if (MQLInfoInteger(MQL_DEBUG)) Print("The first function run is schedule at: ", TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
以下は、複数のスケジュールを指定した時間に繰り返し実行するように設定する方法です。
void OnStart() { //--- schedule.every().minute().at(10).dO(job); //Runs on every minute at the 10th second schedule.every().hour().at(10).dO(job); //runs on every hour at the 10th minute schedule.every().day().at(19, 10).dO(job); //runs every day at 19:10 hours schedule.every().week().at(MONDAY).dO(job); //Runs every week on Monday at 00:00 (by default) while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void job() { Print(__FUNCTION__," run at: ",TimeLocal()); }
タスクに引数を渡す
前の出力ログの例でもわかるように、特に複数のスケジュール関数が同時に実行されている場合、ジョブの進行状況を特定し、追跡するのは困難です。これを解決するために、dO関数に任意の変数jobs_nameを追加します。この変数は、すべてのスケジュールされたタスクにラベルを付けるのに役立ちます。
void CSchedule::dO(JobFunction func, const string jobs_name="") { jobs_struct job; job.func = func; //Assigns the function to job's sturucture job.name = jobs_name; //Assigns the name to job's structure datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above if (has_fixed_time()) { datetime today_midnight = TodaysDate(now); //Get todays date at 00:00 datetime scheduled_time = today_midnight + m_fixed_time; //we add today's date to the fixed_time calculated //Add interval repeatedly until scheduled_time >= now while (scheduled_time <= now) scheduled_time += job.interval; //Schedule for the next time job.next_run = (int)scheduled_time; } else { job.next_run = job.prev_run + job.interval; } if (MQLInfoInteger(MQL_DEBUG)) printf("Job: %s -> first run schedule at: [%s]",job.name, TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
これにより、各ジョブの進行状況をより効果的に追跡できるようになります。
void OnStart() { //--- schedule.every().minute().at(10).dO(Greet, "Jacob"); schedule.every().hour().at(10).dO(Greet, "Anne"); schedule.every().day().at(08, 10).dO(Greet, "Chriss"); schedule.every().week().at(MONDAY).dO(Greet, "Nobody"); while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
出力結果は以下のとおりです。
JI 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Jacob -> first run schedule at: [2025.07.22 06:58:10] MH 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Anne -> first run schedule at: [2025.07.22 07:10:00] FL 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Chriss -> first run schedule at: [2025.07.22 08:10:00] JO 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Nobody -> first run schedule at: [2025.07.28 00:00:00] GN 0 06:58:10.014 schedule test (XAUUSD,D1) Hello there! LF 0 06:58:10.014 schedule test (XAUUSD,D1) Job: Jacob -> Prev run: [2025.07.22 06:58:10] Next run: [2025.07.22 06:59:10]
特定の時間までタスクを実行する
場合によっては、スケジュールされたタスクを永久に実行させたくないことがあります。このような場合、ジョブに期限を設定すると便利です。
ここで、untilという関数を導入しましょう。 この関数はPythonのscheduleモジュールにあるものと似たものです。
CSchedule* CSchedule::until(datetime expiry_date) { m_expiry_date = expiry_date; return GetPointer(this); }
dO関数の内部では、until関数から受け取った有効期限を取得し、それをジョブの構造体に割り当てます。
void CSchedule::dO(JobFunction func, const string jobs_name="") { jobs_struct job; job.func = func; //Assigns the function to job's sturucture job.name = jobs_name; //Assigns the name to job's structure datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above job.expiry_date = m_expiry_date; //Other lines of code }
run_pending関数内でジョブを実行する前に、まずそのジョブが有効期限を過ぎていないかを確認する必要があります。
void CSchedule::run_pending() { int now = (int)TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if (now >= (int)m_jobs[i].expiry_date && expiry_date != 0) //Check if the job hasn't expired { if (MQLInfoInteger(MQL_DEBUG)) printf("Job: %s -> Expired",m_jobs[i].name); continue; //skip all expired jobs } //... other checks }
最後に、タスクを実行し、有効期限を現在の時刻から5分後に設定します。
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(1).minutes().until(D'22.7.2025 10:15').dO(Greet, "Greet"); //The current time was 10:10, in the same date while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
出力結果は以下のとおりです。
EM 0 10:10:02.849 schedule test (XAUUSD,D1) Job: Greet -> first run schedule at: [2025.07.22 10:11:02] CI 0 10:11:02.864 schedule test (XAUUSD,D1) Hello there! NS 0 10:11:02.864 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:11:02] Next run: [2025.07.22 10:12:02] FR 0 10:12:02.873 schedule test (XAUUSD,D1) Hello there! EJ 0 10:12:02.873 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:12:02] Next run: [2025.07.22 10:13:02] ND 0 10:13:02.861 schedule test (XAUUSD,D1) Hello there! OL 0 10:13:02.861 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:13:02] Next run: [2025.07.22 10:14:02] GM 0 10:14:02.922 schedule test (XAUUSD,D1) Hello there! PG 0 10:14:02.922 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:14:02] Next run: [2025.07.22 10:15:02] LH 0 10:15:00.945 schedule test (XAUUSD,D1) Job: Greet -> Expired
スケジュールに関係なくすべてのジョブを実行する
場合によっては、スケジュールに関係なくすべての関数を即座に実行したいことがあります。通常はテスト時や、プログラム起動時など、すべてのスケジュールされた操作を一度に実行したい場合です。
このような状況ではrun_all関数が便利です。
void CSchedule::run_all(void) { datetime now = TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if(m_jobs[i].func != NULL) m_jobs[i].func(); m_jobs[i].prev_run = m_jobs[i].next_run; // Recalculate next_run m_jobs[i].next_run += m_jobs[i].interval; if (MQLInfoInteger(MQL_DEBUG)) printf("%s run at: %s",m_jobs[i].name, TimeToString(now, TIME_DATE|TIME_SECONDS)); } if (MQLInfoInteger(MQL_DEBUG)) printf("%s -> All %I64u Jobs have been executed!",__FUNCTION__, m_jobs.Size()); }
使用例
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(4).minutes().dO(Greet, "Greet every minute"); schedule.every(4).hours().dO(Greet, "Greet hourly"); schedule.every(4).days().dO(Greet, "Greet daily"); schedule.every(4).weeks().dO(Greet, "Greet weekly"); schedule.run_all(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
出力結果は以下のとおりです。
CM 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet every minute -> first run schedule at: [2025.07.22 11:08:06] RR 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet hourly -> first run schedule at: [2025.07.22 15:04:06] HR 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet daily -> first run schedule at: [2025.07.26 11:04:06] MN 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet weekly -> first run schedule at: [2025.08.19 11:04:06] NI 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! QL 0 11:04:06.695 schedule test (XAUUSD,D1) Greet every minute run at: 2025.07.22 11:04:06 RN 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! GF 0 11:04:06.695 schedule test (XAUUSD,D1) Greet hourly run at: 2025.07.22 11:04:06 RL 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! KJ 0 11:04:06.695 schedule test (XAUUSD,D1) Greet daily run at: 2025.07.22 11:04:06 DR 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! QK 0 11:04:06.695 schedule test (XAUUSD,D1) Greet weekly run at: 2025.07.22 11:04:06 FL 0 11:04:06.695 schedule test (XAUUSD,D1) CSchedule::run_all -> All 4 Jobs have been executed!
これら4つの関数を、それぞれ4回目の時間枠間隔後に実行するよう設定していたにもかかわらず、すべてが同じ現在時刻に実行されてしまいました。
スケジュールの管理
時間の経過とともに不要になるスケジュールもあるため、プログラムから異なるスケジュールにアクセスしてキャンセルする方法が必要です。
すべてのジョブを取得する
| 関数 | 説明 |
|---|---|
void get_jobs(jobs_struct &jobs_struct_array[]) { ArrayResize(jobs_struct_array, m_jobs.Size()); for (uint i=0; i<m_jobs.Size(); i++) jobs_struct_array[i] = m_jobs[i]; } | 参照で返される配列の引数を提供し、すべてのジョブ/タスクのプロパティを格納した構造体を取得できます。 |
uint get_jobs() { return m_jobs.Size(); } | スケジュールされたジョブの数を返します。 |
スケジュールされたジョブをキャンセルする
| 関数 | 説明 |
|---|---|
bool Cancel(const string jobs_name); | 名前を使ってスケジュールされたジョブをキャンセルします。 |
bool Cancel(const uint jobs_index); | インデックス番号(0から順番に付けられる)を使ってジョブをキャンセルします。たとえば、最初にスケジュールされたジョブのインデックスは0です。 |
bool Clear() { return ArrayResize(m_jobs, 0)==-1?false:true; } | スケジュールされたすべてのジョブをメモリから削除します。以降はジョブが実行されなくなります。 |
使用例
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every().minute().at(0).dO(Greet, "EveryMin Greetings"); //Job is set at index 0 schedule.every().hour().at(20,10).dO(Greet, "Hourly Greetings"); //Job is set at index 1 schedule.every().day().at(13,20,10).dO(Greet, "Daily Greetings"); //JOb is set at index 2 schedule.every().week().at(MONDAY, 13, 56).dO(Greet, "Weekly Greetings"); //Job is set at index 3 schedule.Cancel(0); //Cancel the job at index 0, the first one Print("Jobs remaining: ",schedule.get_jobs()); schedule.Cancel("Hourly Greetings"); //Cancel the job with this name Print("Jobs remaining: ",schedule.get_jobs()); schedule.Clear(); //Clear all schedules Print("Jobs remaining: ",schedule.get_jobs()); while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
出力結果は以下のとおりです。
LK 0 16:01:02.017 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> first run schedule at: [2025.07.22 16:02:00] HH 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Hourly Greetings -> first run schedule at: [2025.07.22 17:20:10] EH 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Daily Greetings -> first run schedule at: [2025.07.23 13:20:10] PE 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Weekly Greetings -> first run schedule at: [2025.07.28 13:56:00] OS 0 16:01:02.017 schedule test (XAUUSD,D1) CSchedule::Cancel Job at index [0] removed HS 0 16:01:02.017 schedule test (XAUUSD,D1) Jobs remaining: 3 RG 0 16:01:02.018 schedule test (XAUUSD,D1) CSchedule::Cancel Job 'Hourly Greetings' removed DL 0 16:01:02.018 schedule test (XAUUSD,D1) Jobs remaining: 2 DD 0 16:01:02.018 schedule test (XAUUSD,D1) Jobs remaining: 0
タイムゾーンの扱い
これまでのライブラリの例やコード実装では、ローカル時間を使用していました。しかし、MQL5プログラミングでは複数の時間オプションを使えるため、これは制約になります。たとえば、取引操作をブローカーのサーバー時間やUTC時間に基づいてスケジュールしたい場合があります。
そこで、CScheduleクラスのコンストラクタ内にオプションの変数を追加し、開発者がすべてのスケジュール操作で使用する時間の種類を選択できるようにします。
CSchedule::CSchedule(TIME_SOURCE_ENUM time_source=TIME_SOURCE_LOCAL):
m_time_source(time_source)
{
if (MQLInfoInteger(MQL_DEBUG))
printf("Schedule class initialized using %s, current time -> %s",EnumToString(time_source), (string)GetTime(m_time_source));
} 以下は「時間ソースの列挙型」とそれに対応する関数です。
enum TIME_SOURCE_ENUM { TIME_SOURCE_LOCAL, // TimeLocal() TIME_SOURCE_CURRENT, // TimeCurrent() TIME_SOURCE_TRADE_SERVER, // TimeTradeServer() TIME_SOURCE_GMT // TimeGMT() }; datetime GetTime(TIME_SOURCE_ENUM source) { switch(source) { case TIME_SOURCE_LOCAL: return TimeLocal(); case TIME_SOURCE_CURRENT: return TimeCurrent(); case TIME_SOURCE_TRADE_SERVER: return TimeTradeServer(); case TIME_SOURCE_GMT: return TimeGMT(); default: return TimeLocal(); // Fallback } }
使用例
#include <schedule.mqh> CSchedule schedule(TIME_SOURCE_GMT); //Using GMT //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every().minute().at(0).dO(Greet, "EveryMin Greetings"); //Job is set at index 0 schedule.every().hour().at(20,10).dO(Greet, "Hourly Greetings"); //Job is set at index 1 schedule.every().day().at(13,20,10).dO(Greet, "Daily Greetings"); //JOb is set at index 2 schedule.every().week().at(MONDAY, 13, 56).dO(Greet, "Weekly Greetings"); //Job is set at index 3 while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
出力結果は以下のとおりです。
LP 0 16:57:34.961 schedule test (XAUUSD,D1) Schedule class initialized using TIME_SOURCE_GMT, current time -> 2025.07.22 13:57:34 QL 0 16:57:34.964 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> first run schedule at: [2025.07.22 13:58:00] RM 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Hourly Greetings -> first run schedule at: [2025.07.22 14:20:10] RE 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Daily Greetings -> first run schedule at: [2025.07.23 13:20:10] KK 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Weekly Greetings -> first run schedule at: [2025.07.28 13:56:00] HK 0 16:58:00.161 schedule test (XAUUSD,D1) Hello there! KO 0 16:58:00.161 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> Prev run: [2025.07.22 13:58:00] Next run: [2025.07.22 13:59:00]
取引アプリでのscheduleモジュールの応用
これまで、このモジュールを使ったシンプルな関数や、タイミング通りに関数を実行する例を見てきました。以下は、このライブラリを実際の取引アプリケーションで活用する具体例です。
より効果的なNewBarイベント処理
新しいバーの開始を正確に検知する関数を書くのは、必ずしも簡単ではありません。しかし、CScheduleクラスには非常に特定の時刻にタスクをスケジュールする方法が複数用意されているため、秒単位、分単位、時間単位、日単位などでバーの開始時に特定のアクションを実行することが可能です。
ファイルSchedule Testing EA.mq5内
#include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> CTrade m_trade; CSymbolInfo m_symbol; CPositionInfo m_position; //--- #include <schedule.mqh> CSchedule schedule(TIME_SOURCE_CURRENT); //Use the current broker's time //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input int magic_number = 22072025; input uint slippage = 100; input uint stoploss = 500; input uint takeprofit = 700; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- m_trade.SetExpertMagicNumber(magic_number); m_trade.SetTypeFillingBySymbol(Symbol()); m_trade.SetDeviationInPoints(slippage); if (!m_symbol.Name(Symbol())) { printf("%s -> Failed to select a symbol '%s'. Error = %d", __FUNCTION__,Symbol(),GetLastError()); return INIT_FAILED; } //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); //every hour when the minute == 0 and second == 0 //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- schedule.run_pending(); //Constanly monitor all the scheduled tasks } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool PosExists(ENUM_POSITION_TYPE type) { for (int i=PositionsTotal()-1; i>=0; i--) if (m_position.SelectByIndex(i)) if (m_position.Symbol()==Symbol() && m_position.Magic() == magic_number && m_position.PositionType()==type) return (true); return (false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CloseAllTrades() { for (int i = PositionsTotal() - 1; i >= 0; i--) if (m_position.SelectByIndex(i)) if (m_position.Magic() == magic_number && m_position.Symbol() == Symbol()) m_trade.PositionClose(m_position.Ticket(), slippage); } //+------------------------------------------------------------------+ //| The main function for opening trades and performing other | //| trading related tasks | //+------------------------------------------------------------------+ void MainTradingFunction() { printf("New bar detected!"); //--- if (!m_symbol.RefreshRates()) return; if (!PosExists(POSITION_TYPE_BUY)) m_trade.Buy(m_symbol.LotsMin(), Symbol(), m_symbol.Ask(), m_symbol.Ask()-stoploss*m_symbol.Point(), m_symbol.Ask()+takeprofit*m_symbol.Point() ); if (!PosExists(POSITION_TYPE_SELL)) m_trade.Sell(m_symbol.LotsMin(), Symbol(), m_symbol.Bid(), m_symbol.Bid()+stoploss*m_symbol.Point(), m_symbol.Bid()-takeprofit*m_symbol.Point() ); //--- }
以下は、ストラテジーテスターの出力です。

すべてのスケジュールは、EventSetTimerと同様に、OnInit関数内で明示的に設定する必要があります。このときdO関数を使用します。
schedule_pending関数は、スケジュールを常に監視する役割を持つため、EAではOnTick関数内、インジケーターではOnCalculate関数内、MQL5スクリプトでは無限ループ内で実行する必要があります。
日次取引レポートの送信
市場の終了直前(例:00:00の5分前)などを監視することで、日次取引レポートを出力またはユーザーに送信することが可能です。
int OnInit() { //... other lines of code //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); schedule.every().day().at(23, 55).dO(SendDailyTradingReport); //every day 5 minutes before market closing //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- schedule.run_pending(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SendDailyTradingReport() { string sdate = TimeToString (TimeCurrent(), TIME_DATE); datetime start = StringToTime(sdate); if (!HistorySelect(start, TimeCurrent())) { printf("%s, line %d failed to obtain closed deals from history error =%d",__FUNCTION__,__LINE__,GetLastError()); return; } Comment(""); //--- double pl = 0.0; int trades_count=0; string report_body = ""; for(int i = 0; i < HistoryDealsTotal(); i++) { if (m_deal.SelectByIndex(i)) if (m_deal.Entry() == DEAL_ENTRY_OUT && m_deal.Magic() == magic_number) { pl += m_deal.Profit(); trades_count++; report_body += StringFormat("Trade[%d] -> | ticket: %I64u | type: %s | entry: %.5f | volume: %.3f | commision: %.3f\n", trades_count, m_deal.Ticket(), EnumToString(m_deal.DealType()), m_deal.Entry(), m_deal.Volume(), m_deal.Commission() ); } } string report_header = StringFormat("<<< Daily Trading Report >>> \r\n\r\nAC Balance: %.3f\r\nAC Equity: %.3f\r\nPL: %.3f\r\nTotal Trades: %d \r\n\r\n", m_account.Balance(), m_account.Equity(), pl, trades_count ); //--- You might choose to send the reports instead of printing Comment(report_header+report_body); Print(report_header+report_body); }
以下は、ストラテジーテスターの出力です。
CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 <<< Daily Trading Report >>> CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 AC Balance: 2983.830 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 AC Equity: 2983.200 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 PL: -2.960 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Total Trades: 3 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[1] -> | ticket: 166 | type: DEAL_TYPE_SELL | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[2] -> | ticket: 168 | type: DEAL_TYPE_BUY | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[3] -> | ticket: 169 | type: DEAL_TYPE_SELL | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00
同じ考え方は、毎週金曜日の市場終了直前に週次レポートを生成する場合や、月次レポートの生成にも応用できます。
ScheduleとOnTimerの比較
この2つの方法を直接比較するのはやや不公平です。どちらもそれぞれのやり方で正しく動作します。しかし、両者の違いを理解し、どの状況で使うべきか、どの状況で使わないほうがよいかを知っておくことは有益です。
| OnTimer | Schedule |
|---|---|
| 1つのタイマーイベント(1つのスケジュール)しか設定できません。この組み込み機能は、単一のタスクをスケジュールしたい場合に有用です。 | 複数のタスクをそれぞれ異なる間隔で設定可能です。このカスタムライブラリは、複数のスケジュールを同時に実行したい場合に便利です。 |
| 高速で効果的です。 | 組み込みのOnTimerほど高速ではありません。効果はこれから検証される余地があります。 |
| EAとインジケーターでのみ利用可能です。 | すべてのMQL5プログラム(EA、インジケーター、スクリプト)で動作します。 |
24時間365日稼働します(常に信頼性あり)。 | 市場が開かれているときにのみトリガーされる取引関数(OnTickおよびOnCalculate)に依存します。 スクリプト内で無限ループを使用しない限り、run_pending関数による監視は、市場が開いている時間に依存するため、24時間/週5日未満での動作となります。 |
前述の比較表の最後の行で述べた通り、このライブラリを使用した際に生じる信頼性の問題を解決するには、CScheduleクラスをOnTimer関数内で実行する必要があります。
#include <schedule.mqh> CSchedule schedule(TIME_SOURCE_CURRENT); //Use the current broker's time //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //... other functions & lines of code //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); schedule.every().day().at(23, 55).dO(SendDailyTradingReport); //every day 5 minutes before market closing //--- Ontimer EventSetTimer(1); //Run the Ontimer function after every 1 second (pretty much always) return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); //Delete the timer } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- //schedule.run_pending(); //❎ } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer(void) { schedule.run_pending(); //✅ }
結論
特定のスケジュールを設定し、タスクを正確な時刻に実行できることは、アルゴリズムトレーダーにとって非常に重要です。多くの取引戦略は一日の正確な時刻に依存するだけでなく、日次取引レポートや月次更新など、繰り返し実行したい活動も数多く存在します。
今回実装したクラスは、Pythonのscheduleモジュールのように、MQL5プログラム内で繰り返しイベントを簡単に設定できる方法を提供します。
OnTimer関数も基本的な機能は備えていますが、人間にやさしいスケジュール設定の柔軟性には欠けます。そのため、OnTimerでは対応が難しい場面では、このライブラリを積極的に活用してください。
では、また。
添付ファイルの表
| ファイル名 | 説明と使用法 |
|---|---|
| Experts\Schedule testing EA.mq5 | 取引操作のスケジュール用EA |
| Include\schedule.mqh | 特定の時刻や間隔で関数を実行するスケジュール機能を提供するCScheduleクラス |
| Scripts\schedule test.mq5 | CScheduleクラステスト用スクリプト |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18913
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引システムの構築(第1回):定量的なアプローチ
初心者からエキスパートへ:MQL5を使用したアニメーションニュースヘッドライン(VII) - ニュース取引におけるポストインパクト戦略
知っておくべきMQL5ウィザードのテクニック(第77回):ゲーターオシレーターとA/Dオシレーターの使用
MQL5サービスからPythonアプリケーションへのMetaTraderティック情報アクセス(ソケット使用)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索