見出し画像

【ソースコード付き】EAのマジックナンバー重複問題とその解決策

いつも記事をお読みいただき、ありがとうございます。

今回は、MT4のEA(エキスパート・アドバイザー)の開発・運用でよく遭遇しうる「マジックナンバーの重複」問題について、Win32 APIを活用した解決策を詳しく解説します。

⏬この記事は、以下のような方に特に参考になるかと思います:

✅複数のEAを同時運用している、もしくは検討している方
✅MT4でのEA開発に携わっているプログラマーの方
✅Win32 APIを使ったMQL4プログラミングに興味がある方
✅より安全なEA運用を目指している方

一見些細な問題に思えるかもしれませんが、複数のEAを運用する際には重要な検討ポイントとなります
ぜひ最後までお付き合いください。


⚠️ 免責事項と利用上の注意

本記事で提供するソースコードは、MITライセンスの下で提供されています
以下にライセンス条項の原文を示します:

The MIT Licence

Copyright (c) 2024,  HANDY SYSTEMS

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

The MIT License(原文):https://opensource.org/license/mit

引き続き、日本語参考訳を示します:

MIT ライセンス

Copyright (c) 2024, HANDY SYSTEMS

以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。
これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じことを許可する権利も無制限に含まれます。

上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとします。

ソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証もなく提供されます。
ここでいう保証とは、商品性、特定の目的への適合性、および権利非侵害についての保証も含みますが、それに限定されるものではありません。
作者または著作権者は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切の請求、損害、その他の義務について何らの責任も負わないものとします。

MITライセンス 日本語参考訳:https://licenses.opensource.jp/MIT/MIT.html

上記の通り、「著作権表示」と「MIT ライセンスの全文掲載(原文への URL でも可)」をクリアしていれば利用条件を満たしたことになります。
また、ウェブページ内に「著作権表示」と「MIT ライセンスの全文」を掲載し、その URL をソースコードに掲載するという方法でも問題ありません。

また、本実装には以下のようなリスクが伴います:

  1. Win32 APIの使用によるリスク

    • MT4の動作環境やWindowsバージョンの違いによる予期せぬ動作

    • APIの仕様変更による影響

    • システムリソースへのアクセスに関するセキュリティリスク

    • ファイルシステムへのアクセス権限の問題

  2. EAの運用に関するリスク

    • 重複チェックの失敗による意図しない取引の発生

    • システムクラッシュ時のファイルハンドル開放の問題

    • MT4の異常終了時のリソース解放の問題

    • 複数のMT4ターミナルインスタンス間での競合

  3. システムリソースに関するリスク

    • 一時ファイルの残留の可能性

    • メモリリークの可能性

    • ファイルハンドルの上限への到達

これらのリスクを理解した上で、自己責任での利用をお願いいたします。
本コードの使用によって生じたいかなる損害についても、作者は一切の責任を負いません。

✅はじめに

MT4での自動売買において、一つのMT4ターミナル内で複数のEAを同時に動作させて運用することは一般的な手法です。
しかし、その際に見落としがちなのが、マジックナンバーの重複問題です。
一見些細に思えるこの問題が、予期せぬ重大なトラブルの原因となることがあります

本記事では、この問題に対する効果的な解決策を提案します。

従来の手法の限界を理解し、より確実な方法でマジックナンバーの重複をチェックする方法について解説していきます。

✅マジックナンバー重複がもたらす問題

マジックナンバーの重複は、予想以上に深刻な問題を引き起こす可能性があります。

例えば、複数の移動平均線を使用したトレード戦略を実装したEAを、異なる時間足で運用しているケースを考えてみましょう。

同じマジックナンバーを持つEAが存在すると、一方のEAが出した注文を他方のEAが自分の注文と誤認識してしまう危険性があります
その結果、意図しないタイミングでの決済や、ストップロス・利確位置の予期せぬ変更が発生する可能性があります

特に注意が必要なのは、バックテスト時には気づきにくい問題だという点です。単体でのバックテストでは問題なく動作するEAでも、実運用時に他のEAと組み合わせた際に初めて問題が顕在化することがあります

✅従来の解決策とその限界

これまで、この問題に対する一般的な解決策として、グローバル変数を使用する方法がよく採用されてきました。
例えば、以下のようなコードで実装されることが多いでしょう:

if (GlobalVariableGet(StringFormat("%d", MagicNumber), value))
{
    Alert("Magic number is duplicated");
    return INIT_FAILED;
}
GlobalVariableSet(StringFormat("%d", MagicNumber), 1);

しかし、この方法には重大な技術的制約があります。

最も大きな問題は、MT4のOnDeinit関数に2.5秒という厳格なタイムアウト制限が存在することです。

この制限により、EAの終了時にグローバル変数を確実に削除することができません

Deinit #
グローバル変数が初期化解除されプログラム(エキスパートアドバイザーまたはカスタム指標)がアンロードされる前に、クライアント端末はプログラムに Deinit イベントを送信します。Deinit はまた、クライアント端末の閉鎖時、チャートの閉鎖時、セキュリティ及び/または時間軸の変更直前、プログラムの再コンパイル成功時、入力パラメータの変更時、及び口座変更時に生成されます。

初期化解除の理由OnDeinit() 関数に渡されたパラメータによって取得出来ます。OnDeinit() 関数の実行は 2.5 秒に制限されています。この間に完了しない場合、この関数は強制的に終了されます。Deinit イベントはスクリプトでは生成されません。

MQL5 リファレンス:https://www.mql5.com/ja/docs/runtime/event_fire

さらに、MT4のグローバル変数は4週間という長期間保持されるという性質があります。

グローバル変数は最後のアクセスから4週間はクライアントターミナルに保存されています。
グローバル変数へのアクセスは、変数の変更だけでなく読み取りもアクセスとして判断します。

MT4でEA自作しちゃお~:https://yukifx.web.fc2.com/sub/reference/18_globalvari_terminal/cone/global_root.html

そのため、一度設定されたグローバル変数が必要以上に長く残り続け、誤検出の原因となってしまいます。

✅技術解説:Win32 APIを活用した実装

Win32 APIとは

Win32 APIは、Windowsオペレーティングシステムの基本的なシステムサービスにアクセスするためのインターフェースです。
MQL4では通常アクセスできない低レベルのシステム機能を利用することができ、今回はこれを活用してより確実なマジックナンバーの重複チェックを実現します。

✅MQL4からWin32 APIを利用するための準備

MQL4では、#importディレクティブを使用することで、Windowsのシステムライブラリ(DLL)の関数を直接呼び出すことができます。

#import "kernel32.dll"
   int  CreateFileW(string lpFileName, int dwDesiredAccess, int dwShareMode,
                    int lpSecurityAttributes, int dwCreationDisposition,
                   int dwFlagsAndAttributes, int hTemplateFile);
   bool CloseHandle(int hObject);
   int  GetTempPathW(int nBufferLength, string& lpBuffer);
   int  GetCurrentProcessId();
#import

#import "ntdll.dll"
   int RtlGetLastWin32Error();
#import

ここでは2つのシステムライブラリから関数をインポートしています:

  1. kernel32.dll

    • Windowsの基本的なシステム機能を提供するライブラリ

    • ファイル操作やプロセス管理などの基本機能を含む

  2. ntdll.dll

    • Windowsの低レベルなシステム機能を提供するライブラリ

    • RtlGetLastWin32Error関数は、最後に発生したWin32 APIのエラーコードを取得するために使用

    • MT4環境では標準のGetLastError関数がMQL4のエラーを返すため、Win32 APIのエラーを取得するにはRtlGetLastWin32Errorが必要

これらの#import文により、通常のMQL4関数では実現できない、より高度なシステム制御が可能になります。

注意点:

  • DLLのインポートはMT4の設定で許可されている必要があります

  • システムの核となる機能にアクセスするため、慎重な利用が求められます

  • Windows環境に依存するため、異なるバージョンのWindowsでは動作が異なる可能性があります

この設定は、MT4のメニュー[ツール] → [オプション] → [エキスパートアドバイザー]から行うことができ、「DLLの使用を許可する」にチェックを入れる必要があります。
ただし、この設定はシステムのセキュリティに影響を与える可能性があるため、信頼できるソースのEAを使用する場合にのみ有効にすることをお勧めします。

✅重要なWin32 API関数

本実装で使用する主なWin32 API関数は以下の通りです:

#import "kernel32.dll"
   int  CreateFileW(string lpFileName, int dwDesiredAccess, int dwShareMode,
                   int lpSecurityAttributes, int dwCreationDisposition,
                   int dwFlagsAndAttributes, int hTemplateFile);
   bool CloseHandle(int hObject);
   int  GetTempPathW(int nBufferLength, string& lpBuffer);
   int  GetCurrentProcessId();
#import

CreateFileW関数の詳細

CreateFileW関数は、ファイルを作成したり開くための非常に強力な関数です。以下のパラメータが特に重要です:

1. アクセス権(dwDesiredAccess)

#define GENERIC_WRITE    0x40000000
#define GENERIC_READ      0x80000000

これらのフラグにより、ファイルへの読み書き権限を制御します。

2. 共有モード(dwShareMode)

#define FILE_SHARE_DELETE  0x00000004

他のプロセスからのファイルアクセス方法を制御します。

3. 作成方法(dwCreationDisposition)

#define CREATE_NEW          1
#define OPEN_EXISTING       3

ファイルが既に存在する場合の動作を制御します。

4. ファイル属性(dwFlagsAndAttributes)

#define FILE_ATTRIBUTE_TEMPORARY    0x00000100
#define FILE_FLAG_DELETE_ON_CLOSE   0x04000000

これらは本実装の核となる重要なフラグです:

  • FILE_ATTRIBUTE_TEMPORARY:一時ファイルとして最適化します

  • FILE_FLAG_DELETE_ON_CLOSE:プロセス終了時に自動的に削除します

✅一時ファイルによる排他制御の仕組み

本実装では、以下の方法で排他制御を実現しています:

1. ファイル名の生成

string GetLockFileName()
{
   return tempPath + StringFormat("MT4_Magic_%d_%s_%d.lock", 
      MagicNumber, 
      Symbol(),
      GetCurrentProcessId()
   );
}

2. 一時ファイルの作成

lockFileHandle = CreateFileW(
   fileName,
   (int)GENERIC_WRITE,
   0,
   0,
   CREATE_NEW,
   (int)(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
   0
);

この方法には以下の利点があります:

  • プロセスIDによって一時ファイルの完全な一意性が確保されます

  • OSによって確実に一時ファイルが削除されます

  • システムリソースが効率的に利用できます

✅実装の処理解説

1. 初期化処理

OnInit関数では、以下の処理を行います:

  1. TEMPパスの取得

  2. マジックナンバーの重複チェック

  3. 重複が見つかった場合のユーザーへの確認画面の表示

  4. ロックファイルの作成

2. 定期的なチェック

OnTick関数では、指定された間隔で以下のチェックを実行します:

  1. 他のEAによる同一マジックナンバーの使用確認

  2. 重複が見つかった場合の警告画面の表示

3. 終了処理

OnDeinit関数では、以下のクリーンアップを実施:

  1. ロックファイルハンドルのクローズ

  2. エラー発生時のログ出力

✅運用上の注意点

システムの初期設定について

マジックナンバー重複チェッカーを導入する際は、まず適切な初期設定が重要です。
マジックナンバーの選定は、他のEAとの重複を避けるため、一定のルールに基づいて慎重に行う必要があります
例えば、EA開発者やストラテジーごとに番号の範囲を決めておくなどの工夫が有効です。

チェック間隔の設定も、取引スタイルに合わせて適切に調整しましょう
高頻度取引を行う場合は、より短いインターバルでのチェックが推奨されますが、システムへの負荷とのバランスを考慮する必要があります。
スキャルピングなどの短期取引では60秒程度、中長期の取引では300秒以上の間隔設定が妥当であると考えられます。

システム異常時の対応方法

1. MT4クラッシュ時の挙動

MT4が予期せず終了した場合でも、Win32 APIのFILE_FLAG_DELETE_ON_CLOSEフラグによって、一時ファイルは自動的に削除されます。
これはOSレベルで保証される動作であり、次回MT4起動時に問題が引き継がれることはありません。

2. ファイルアクセスエラーへの対処

ファイルアクセスに関するエラーが発生した場合は、まずエラーコードを確認してください。
代表的なエラーコードとその対処方法は以下の通りです:

  • エラー32(共有違反):他のプロセスがファイルを使用中

  • エラー87(無効なパラメータ):APIの呼び出しパラメータに問題

  • エラー80(ファイルが存在する):マジックナンバーの重複の可能性あり

これらのエラーが継続する場合は、一度MT4を再起動することで解決することが多いです。

3. 重複検出時のベストプラクティス

重複が検出された場合は、まず以下の確認を行うことをお勧めします:

  1. 同一通貨ペアで他のEAが動作していないか

  2. MT4のグローバル変数に古い情報が残っていないか

  3. 他のMT4ターミナルインスタンスのEAで同じマジックナンバーを使用していないか

✅システムパフォーマンスの最適化

本システムは、できるだけ軽量に動作するよう設計されていますが、長期運用時には以下の点に注意が必要です:

メモリ使用量の管理
定期的なチェックによるメモリ使用量は通常わずかですが、大量のEAを同時運用する場合は、チェック間隔を適切に調整することでシステムへの負荷を抑えることができます。

ログ出力の最適化
デバッグ情報の出力は、問題解決に役立つ一方で、長期運用時にログファイルが肥大化する原因となります。
本番環境では必要最小限のログ出力に留めることをお勧めします。

リソースの監視
長期運用時は、Windowsのタスクマネージャーでメモリ使用量やファイルハンドル数を定期的に確認することをお勧めします。
異常が見られた場合は、早めに対処することで安定した運用が可能です。

💡まとめ

Win32 APIを活用することで、より確実なマジックナンバーの重複チェックを実現できるようになりました。
本実装の特長は以下の通りです:

  1. 高い信頼性

    • OSレベルでの排他制御

    • プロセスIDによる確実なMT4の識別

    • 自動的なリソースの解放

  2. 運用の柔軟性

    • ユーザーによる継続/中止の選択が可能

    • 定期的な監視機能

    • 詳細なエラー情報の提供

  3. システムリソースの効率的な利用

    • 一時ファイルの最適な管理

    • 自動的なクリーンアップ

    • 最小限のリソース消費

📄ソースコード

本実装のソースコードを以下に記述します。
ご自身の環境に合わせて適切にカスタマイズしてご利用ください。

//+------------------------------------------------------------------+
//|                                  MagicNumberDuplicateChecker.mq4 |
//|                                Copyright (c) 2024, HANDY SYSTEMS |
//+------------------------------------------------------------------+
/*
   The MIT License

   Copyright (c) 2024, HANDY SYSTEMS

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in all
   copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   SOFTWARE.
*/

#property copyright     "Copyright (c) 2024, HANDY SYSTEMS"
#property version       "1.0"

#property description   "Magic Number Duplicate Checker"

#property strict

//+------------------------------------------------------------------+
//| Debug mode                                                       |
//+------------------------------------------------------------------+
#define DEBUG_MODE false

//+------------------------------------------------------------------+
//| Win32 API functions                                              |
//+------------------------------------------------------------------+
#import "kernel32.dll"
int  CreateFileW(string lpFileName, int dwDesiredAccess, int dwShareMode,
                 int lpSecurityAttributes, int dwCreationDisposition,
                 int dwFlagsAndAttributes, int hTemplateFile);
bool CloseHandle(int hObject);
int  GetTempPathW(int nBufferLength, string& lpBuffer);
int  GetCurrentProcessId();
#import

#import "ntdll.dll"
int  RtlGetLastWin32Error();
#import

//+------------------------------------------------------------------+
//| Win32 API constants                                              |
//+------------------------------------------------------------------+
#define GENERIC_WRITE                  0x40000000
#define GENERIC_READ                   0x80000000
#define FILE_SHARE_DELETE              0x00000004
#define CREATE_NEW                     1
#define CREATE_ALWAYS                  2
#define OPEN_EXISTING                  3
#define FILE_ATTRIBUTE_TEMPORARY       0x00000100
#define FILE_FLAG_DELETE_ON_CLOSE      0x04000000
#define INVALID_HANDLE_VALUE           -1
#define ERROR_FILE_EXISTS              80

//+------------------------------------------------------------------+
//| Message constants                                                |
//+------------------------------------------------------------------+
#define MSG_DUPLICATE_WARNING    "WARNING: Magic number %d is already in use.\n\n" + \
                                 "There may already be an EA running with the\n" + \
                                 "same magic number for the same currency pair.\n\n" + \
                                 "Do you want to continue running it?"
#define MSG_SYSTEM_ERROR         "System error occurred: %d"
#define MSG_TEMP_PATH_ERROR      "Failed to get temporary path"

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input int  MagicNumber    = 12345;     // Magic Number: Unique identifier for EA
input bool CheckDuplicate = true;      // Check Duplicate: Enable/disable duplicate checking
input int  CheckInterval  = 300;       // Check Interval: Time between checks (seconds)

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
datetime   lastCheckTime  = 0;                     // Last check timestamp
int        lockFileHandle = INVALID_HANDLE_VALUE;  // Lock file handle
string     tempPath       = "";                    // Temporary file path

//+------------------------------------------------------------------+
//| Custom logging function                                           |
//+------------------------------------------------------------------+
void LogDebug(const string message)
{
   if (DEBUG_MODE) Print("[DEBUG] ", message);
}
//+------------------------------------------------------------------+
//| Get Windows temporary path                                       |
//+------------------------------------------------------------------+
bool GetTempPath()
{
   string buffer = "                                                  ";  // 50 chars buffer
   int result = GetTempPathW(StringLen(buffer), buffer);
   
   if (result > 0)
   {
      tempPath = StringSubstr(buffer, 0, result);
      LogDebug("Temp path: " + tempPath);
      return true;
   }
   
   Print(MSG_TEMP_PATH_ERROR);
   return false;
}
//+------------------------------------------------------------------+
//| Generate lock file name                                          |
//+------------------------------------------------------------------+
string GetLockFileName()
{
   string fileName = StringFormat("MT4_Magic_%d_%s_%d.lock", 
      MagicNumber, 
      Symbol(),
      GetCurrentProcessId()
   );
   
   LogDebug("Lock file name: " + fileName);
   return tempPath + fileName;
}
//+------------------------------------------------------------------+
//| Check for magic number duplication                               |
//+------------------------------------------------------------------+
bool CheckDuplicateMagicNumber()
{
   string fileName = GetLockFileName();
   
   int fileHandle = CreateFileW(
      fileName,
      (int)GENERIC_READ,
      0,
      0,
      OPEN_EXISTING,
      0,
      0
   );
   
   if (fileHandle != INVALID_HANDLE_VALUE)
   {
      if (fileHandle != lockFileHandle)
      {
         CloseHandle(fileHandle);
         return true;   // Duplicate found
      }
      CloseHandle(fileHandle);
   }
   
   return false;        // No duplicate
}
//+------------------------------------------------------------------+
//| Register magic number                                            |
//+------------------------------------------------------------------+
bool RegisterMagicNumber()
{
   string fileName = GetLockFileName();
   LogDebug("Attempting to register magic number: " + IntegerToString(MagicNumber));
   
   lockFileHandle = CreateFileW(
      fileName,
      (int)GENERIC_WRITE,
      0,
      0,
      CREATE_NEW,
      (int)(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
      0
   );
   
   if (lockFileHandle == INVALID_HANDLE_VALUE)
   {
      int error = RtlGetLastWin32Error();
      
      if (error == ERROR_FILE_EXISTS)
      {
         // Show warning and ask user whether to continue
         string message = StringFormat(MSG_DUPLICATE_WARNING, MagicNumber);
         int response = MessageBox(
            message,
            "Magic Number Duplicate Warning",
            MB_ICONWARNING | MB_OKCANCEL
         );
         
         if (response == IDCANCEL)
         {
            LogDebug("User canceled operation");
            return false;
         }
         
         LogDebug("User chose to continue despite duplicate");
         return true;  // User accepted the risk
      }
      else
      {
         // Handle other system errors
         string errorMsg = StringFormat(MSG_SYSTEM_ERROR, error);
         MessageBox(
            errorMsg,
            "System Error",
            MB_ICONERROR | MB_OK
         );
         Print(errorMsg);
         return false;
      }
   }
   
   LogDebug("Successfully registered magic number");
   return true;
}
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Get temporary path
   if (!GetTempPath())
   {
      return INIT_FAILED;
   }
   
   // Perform duplicate checking if enabled
   if (CheckDuplicate)
   {
      if (!RegisterMagicNumber())
      {
         return INIT_FAILED;
      }
   }
   
   lastCheckTime = TimeCurrent();
   LogDebug("Initialization completed successfully");
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if (lockFileHandle != INVALID_HANDLE_VALUE)
   {
      LogDebug("Closing lock file handle");
      if (!CloseHandle(lockFileHandle))
      {
         int error = RtlGetLastWin32Error();
         Print("Failed to close handle: ", error);
      }
      lockFileHandle = INVALID_HANDLE_VALUE;
   }
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if (!CheckDuplicate) return;
   
   // Perform periodic checks
   if (TimeCurrent() - lastCheckTime >= CheckInterval)
   {
      if (CheckDuplicateMagicNumber())
      {
         Print("Warning: Duplicate magic number detected during runtime check");
         Alert("Warning: Duplicate magic number detected\nMagic Number: ", MagicNumber);
      }
      
      lastCheckTime = TimeCurrent();
   }
}
//+------------------------------------------------------------------+

最後までお読みいただき、ありがとうございました

スキ・コメント・フォローなどいただけけましたら嬉しいです。

✅作者ホームページ

#MT4 #MQL #プログラミング #コーディング #ソースコード #オープンソース #Win32 #FX自動売買 #FX #EA #EA開発 #EA運用 #マジックナンバー #MagicNumber


いいなと思ったら応援しよう!