yuki
yuki
EAやインジケーターを特定の口座だけで動かしたいときは、MQLから現在の口座番号を取得し、許可した番号と照合します。未接続の状態で判定すると正しい利用者まで認証エラーになる可能性があるため、接続状態も確認しておきます。
カオチャイ
カオチャイ
口座番号を比較するだけでなく、取引サーバーへ接続したあとに認証するのがポイントですね。
この記事でわかること
  • MQL4・MQL5で口座番号を取得する方法
  • 口座番号とサーバー名を照合する方法
  • EAとインジケーターへ口座縛りを追加する流れ
  • 未接続時に誤判定しないための注意点
  • コード内認証とサーバー認証の違い

MQLの口座縛りとは?

口座縛りとは、EAやインジケーターを、あらかじめ許可した取引口座だけで動かすための簡易認証です。

MQL4・MQL5では、現在ログインしている口座番号を次のコードで取得できます。

long login = AccountInfoInteger(ACCOUNT_LOGIN);

取得した口座番号と、コード内に保存した許可番号を比較し、一致した場合だけ売買処理やインジケーターの計算処理へ進みます。

外部サーバーを用意せずに実装できるため、自分用のツールや少人数へ配布するEA・インジケーターに使いやすい方法です。

ただし、口座番号の照合だけでコピーや改変を完全に防げるわけではありません。

口座縛りを使う前に確認したいこと

  • 配布時は原則としてコンパイル済みのex4・ex5を使用する
  • mq4・mq5を渡すと認証部分を書き換えられる可能性がある
  • 利用口座を変更する場合はコードの修正や再コンパイルが必要
  • 販売数が増えると顧客ごとのファイル管理が煩雑になる

mq4・mq5とex4・ex5の違いについては、次の記事で解説しています。

ex4ファイルは逆コンパイルできる?mq4との違いと注意点

口座番号とサーバー名を取得する

口座番号は、AccountInfoInteger(ACCOUNT_LOGIN)で取得します。

戻り値に合わせ、許可する口座番号もlong型で保存します。

const long ALLOWED_LOGIN = 12345678;

取引サーバー名は、AccountInfoString(ACCOUNT_SERVER)で取得できます。

string server = AccountInfoString(ACCOUNT_SERVER);

口座番号とサーバー名の両方を照合する場合は、次のように関数へまとめます。

const long   ALLOWED_LOGIN  = 12345678;
const string ALLOWED_SERVER = "Broker-Demo";

bool IsAllowedAccount()
{
   long login    = AccountInfoInteger(ACCOUNT_LOGIN);
   string server = AccountInfoString(ACCOUNT_SERVER);

   return(login == ALLOWED_LOGIN &&
          server == ALLOWED_SERVER);
}

ALLOWED_SERVERには、実際の口座で取得したサーバー名を正確に入力します。

大文字・小文字、空白、末尾の「-Demo」「-Live」などが異なると一致しません。設定前にログへ出して確認しておくと確実です。

Print("Login: ", AccountInfoInteger(ACCOUNT_LOGIN));
Print("Server: ", AccountInfoString(ACCOUNT_SERVER));

サーバーを限定しない場合は、口座番号だけを比較します。

const long ALLOWED_LOGIN = 12345678;

bool IsAllowedAccount()
{
   return(AccountInfoInteger(ACCOUNT_LOGIN) == ALLOWED_LOGIN);
}

未接続の状態では口座認証を行わない

EAやインジケーターを読み込んだ直後に、取引サーバーへの接続が完了しているとは限りません。

接続状態は、次のコードで確認できます。

bool connected =
   (bool)TerminalInfoInteger(TERMINAL_CONNECTED);

未接続の状態では認証結果を確定せず、接続後に再確認します。

そのため、ここではOnInit()でタイマーを開始し、OnTimer()から接続状態を確認する方法を使います。

EAへ口座縛りを実装する方法

EAでは、認証に成功するまでOnTick()内の売買処理へ進まないようにします。

const long   ALLOWED_LOGIN  = 12345678;
const string ALLOWED_SERVER = "Broker-Demo";

bool g_authorized = false;
bool g_authChecked = false;

bool IsAllowedAccount()
{
   long login    = AccountInfoInteger(ACCOUNT_LOGIN);
   string server = AccountInfoString(ACCOUNT_SERVER);

   return(login == ALLOWED_LOGIN &&
          server == ALLOWED_SERVER);
}

void CheckAuthorization()
{
   //未接続なら認証結果を確定しない
   if(!TerminalInfoInteger(TERMINAL_CONNECTED))
      return;

   long login = AccountInfoInteger(ACCOUNT_LOGIN);

   if(login <= 0)
      return;

   g_authChecked = true;
   g_authorized = IsAllowedAccount();

   EventKillTimer();

   if(g_authorized)
      Print("口座認証に成功しました。");
   else
      Alert("この口座ではEAを使用できません。");
}

int OnInit()
{
   if(!EventSetTimer(1))
      return(INIT_FAILED);

   CheckAuthorization();

   //起動時点で不許可と確認できた場合
   if(g_authChecked && !g_authorized)
      return(INIT_FAILED);

   return(INIT_SUCCEEDED);
}

void OnTimer()
{
   if(!g_authChecked)
      CheckAuthorization();
}

void OnDeinit(const int reason)
{
   EventKillTimer();
}

void OnTick()
{
   //認証が終わるまでは売買処理を行わない
   if(!g_authorized)
      return;

   //ここからEAの売買処理
}

すでに接続済みの場合は、OnInit()からすぐに認証します。

未接続の場合は、OnTimer()が接続状態を再確認します。認証結果が確定するまでは、OnTick()内の売買処理へ進みません。
接続後に不許可口座と判定された場合、このコードではEAはチャートに残りますが、g_authorizedfalseのため売買処理には進みません。
不許可口座と確認できた状態で初期化中の場合は、INIT_FAILEDを返します。

既存EAへ追加する場合は、新しいOnInit()OnTimer()を別に作らず、すでにあるイベント関数へ認証処理を組み込んでください。

インジケーターへ口座縛りを実装する方法

インジケーターでも、接続確認と口座番号の取得方法はEAと同じです。

ただし、OnCalculate()は価格データの更新に応じて繰り返し呼ばれます。

OnCalculate()内で毎回口座認証とAlert()を実行すると、認証エラーが繰り返し表示される可能性があります。

認証はOnInit()OnTimer()で行い、OnCalculate()では認証結果だけを確認します。

const long   ALLOWED_LOGIN  = 12345678;
const string ALLOWED_SERVER = "Broker-Demo";

bool g_authorized = false;
bool g_authChecked = false;

bool IsAllowedAccount()
{
   long login    = AccountInfoInteger(ACCOUNT_LOGIN);
   string server = AccountInfoString(ACCOUNT_SERVER);

   return(login == ALLOWED_LOGIN &&
          server == ALLOWED_SERVER);
}

void CheckAuthorization()
{
   //未接続なら認証結果を確定しない
   if(!TerminalInfoInteger(TERMINAL_CONNECTED))
      return;

   long login = AccountInfoInteger(ACCOUNT_LOGIN);

   if(login <= 0)
      return;

   g_authChecked = true;
   g_authorized = IsAllowedAccount();

   EventKillTimer();

   if(g_authorized)
      Print("口座認証に成功しました。");
   else
      Alert("この口座ではインジケーターを使用できません。");
}

int OnInit()
{
   if(!EventSetTimer(1))
      return(INIT_FAILED);

   CheckAuthorization();

   //起動時点で不許可と確認できた場合
   if(g_authChecked && !g_authorized)
      return(INIT_FAILED);

   return(INIT_SUCCEEDED);
}

void OnTimer()
{
   if(!g_authChecked)
      CheckAuthorization();
}

void OnDeinit(const int reason)
{
   EventKillTimer();
}

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[])
{
   //認証が終わるまでは計算しない
   if(!g_authorized)
      return(0);

   //ここからインジケーターの計算処理

   return(rates_total);
}

認証前または不許可口座では、インジケーターの計算処理へ進みません。

ただし、OnCalculate()で0を返しただけでは、インジケーターそのものをチャートから削除する処理にはなりません。

認証前にラベルやラインなどのオブジェクトを作成している場合は、認証失敗時に別途削除処理が必要です。

既存インジケーターへ追加する際の注意点

  • 既存のOnInit・OnTimer・OnCalculateへ処理を組み込む
  • 同じイベント関数を二つ作らない
  • OnCalculate内で毎回Alertを表示しない
  • 認証前はバッファーやオブジェクトの計算へ進まない
  • 認証失敗時に残るオブジェクトがないか確認する

複数の口座番号を許可する方法

複数口座を許可する場合は、口座番号を配列へ保存します。

const long ALLOWED_LOGINS[] ={
   12345678,
   23456789,
   34567890
};

bool IsAllowedAccount()
{
   long currentLogin =
      AccountInfoInteger(ACCOUNT_LOGIN);

   for(int i = 0; i < ArraySize(ALLOWED_LOGINS); i++)
   {
      if(currentLogin == ALLOWED_LOGINS[i])
         return(true);
   }

   return(false);
}

現在の口座番号が配列内にあればtrueを返します。

この関数を、単一口座用のIsAllowedAccount()と差し替えれば、複数口座へ対応できます。

ただし、許可口座を増やすたびに、配列の修正と再コンパイルが必要です。

口座縛りを実装したあとの確認項目

  • 許可口座でEA・インジケーターが正常に動く
  • 不許可口座では売買や計算処理へ進まない
  • 未接続で起動してもすぐに認証エラーにならない
  • 口座番号とサーバー名が正しく取得できている
  • デモ口座と本番口座を意図どおり区別できている
  • 口座を切り替えたあとも認証結果を確認できる

納品前には、許可口座だけでなく、不許可口座でも動作を確認してください。

認証できない場合は、最初に口座番号とサーバー名をログへ出し、設定値と一致しているか確認します。

口座縛りだけでは完全なコピー防止にならない

コード内の口座縛りは、指定口座以外でEAやインジケーターを動かしにくくする簡易認証です。

認証コードを含むmq4・mq5を渡した場合、ソースコードを編集できる利用者に認証部分を変更される可能性があります。

配布時は、原則としてMetaEditorでコンパイルしたex4・ex5を使用します。

  • 口座変更のたびに再コンパイルが必要
  • 顧客ごとに異なるファイルを管理する必要がある
  • 配布後に許可口座を遠隔で変更できない
  • 利用期限や契約状態の管理には追加実装が必要
  • ファイルの解析や改変を完全に防げるわけではない

少人数への配布なら扱いやすい方法ですが、利用者が増えるほど管理の負担も大きくなります。

販売数が増えたらサーバー認証を検討する

サーバー認証は、許可する口座番号や利用期限を販売者側のサーバーで管理する方法です。

コード内へ顧客ごとの口座番号を書き込まないため、許可口座の変更や利用停止をサーバー側で管理できます。

一方で、通信処理、サーバーの維持、通信障害時の扱いなどを決める必要があります。

  • 自分用や少人数への配布はコード内の口座照合
  • 複数口座を許可する場合は口座番号の配列
  • ブローカーも限定する場合はサーバー名を照合
  • 利用者や更新回数が増えた場合はサーバー認証を検討

MQL4・MQL5で口座縛りを実装する方法まとめ

yuki
yuki
少人数への配布であれば、口座番号とサーバー名を照合する方法が扱いやすいです。実装時は、未接続の状態で認証を確定しないことと、ソースファイルをそのまま配布しないことを確認しています。
カオチャイ
カオチャイ
販売数が増えて個別の再コンパイルが負担になった段階で、サーバー認証へ移行するのが現実的ですね。

今回の要点

  • 口座番号はAccountInfoIntegerで取得する
  • 許可口座番号はlong型で保存する
  • 未接続時は認証を確定せず接続後に再確認する
  • インジケーターではOnCalculate内で毎回認証しない
  • 配布規模が大きくなったらサーバー認証を検討する
ABOUT ME
YUKI
執筆・検証担当:YUKI FX自動売買(EA)・バイナリーオプション自動売買ツールの 開発および検証を担当。 MQL(MT4 / MT5)やC#によるプログラミングを専門とし、 ロジック設計から実装・検証まで一貫して行っています。 ▶ 執筆者・検証方針はこちら