MT4のEAやインジケーターのコピー防止方法【MQL言語】

EAやインジケーターのコピー対策
yuki
yuki
EAやインジケーターを開発した時に問題になるのが不正コピーや二次配布です
カオチャイ
カオチャイ
何の対策もしないまま作成したツールを配布すると、悪意のある人がヤフオクで転売や、自分が開発したかのように振る舞ってSNSで配布される問題が多々起こっています
yuki
yuki
そこでこの記事では、制作したインジケーターが不正利用されないように、複数の対策方法をご紹介していきます

EAやインジケーターには必ずコピー対策を!

EAやインジケーターのコピー対策

せっかく苦労して作ったEAやインジケーターですが、見た目のセンスがあったり、勝率が高かったりと、良くできたツールほどコピーな転売などの悪質業者に狙われてしまいます。

実際に見かけるのが名前を変えてヤフオクでの販売や、クローズドなSNSグループでの二次配布など。

こんなことをされてしまうとEAやインジケーターを販売しても売上は安定しません。

しかし現実問題として、自分で作ったツールがコピーされたとしても殆どの場合で泣き寝入りというパターンが多く、この界隈では犯人がきちんと検挙されたという話は殆ど聞いたことがありません。

ですので、開発者さんたちは自己防衛するしか方法がないのが現状ですが、今回は配布後の利便性・実装の簡単さ・セキュリティの高さ毎に評価して、複数のコピー防止方法をご紹介していきたいとおもいます。

yuki
yuki
なお、MT4とMT5両方で利用できるようにコードを書いてみましたが、ワンタイムパスワード方式は共通にできませんので分けて書いてあります

 

パスワードで縛りをつける

パスワード縛り【MQL言語】

こちらはMT4のチャートにEAを適用するたびにパスワードを入力して貰う方法です。

利用者さんは毎回パスワードを入力してもらう手間はありますが、実装側としてはお手軽な方法なので人気があります。

ただし殆どの場合、EAの配布とパスワードはセットなので、悪意のある利用者が手に入れると何の意味もなくなります。

配布後の利便性 

実装の簡単さ 

セキュリティの高さ 

 

パスワード方式のコード例

#define password "MyPassword1234"
input string p_password = "";//パスワード

int OnInit() {
   if(p_password != password) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }
   
   return(INIT_SUCCEEDED);
}

MT4&MT5どちらでも利用できます

コードに口座番号を書き込んで口座縛りする方法

次は「口座番号縛り」という方法です。

口座番号はMT4のサーバーから直接取得するので偽装することはできません。その特性から確実に利用者を限定することができます。

ただしデモ口座でも動かせるサインインジケーターなどはデモ口座の口座番号をパスワードを共有されると複数人での使用は防げません。

また、口座番号を書き換えてコンパイルする必要があるので利用者が増えてくるとそれだけ手間がかかります。

特にEA等をアップデートする時は、配布する分全てに書き換え作業が発生します。

配布後の利便性 

実装の簡単さ 

セキュリティの高さ 

 

コードに書き込む口座縛りのコード例

#define AccountNumber 12345678 //利用者の口座番号

int OnInit() {
   if(AccountInfoInteger(ACCOUNT_LOGIN) != AccountNumber) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }
   
   return(INIT_SUCCEEDED);
}

 

デモ口座では動かないようにしたバージョン

こちらはデモ口座を複数人で共有されることを防げる書き方。ただし、リアル口座を複数人で共有するようなケースにはもちろん意味はありません。

#define AccountNumber 12345678 //利用者の口座番号

int OnInit() {
   if(AccountInfoInteger(ACCOUNT_TRADE_MODE) == ACCOUNT_TRADE_MODE_DEMO) {
      Alert("デモ口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   } else if(AccountInfoInteger(ACCOUNT_LOGIN) != AccountNumber) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

ソースコード内に直接口座番号を記入する形式(複数の口座番号書き込みタイプ)

こちらは複数の利用者に配る場合に、1回コンパイルしたら済むように変えてみたバージョンです。

これならアップデートしたときも1回コンパイルすれば同じファイルを複数人に配ることができます。

ただし列挙する口座番号が増えるので、書き間違えのリスクは増えます。

int g_AccountNumbers[] = {12345678, 23456789, 34567890};//ここに口座番号を複数記入する

int OnInit() {
   bool isCorrect = false;
   for(int count = 0; count < ArraySize(g_AccountNumbers); count++) {
      if(g_AccountNumbers[count] == AccountInfoInteger(ACCOUNT_LOGIN)) {
         isCorrect = true;
         break;
      }
   }
   if(!isCorrect) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

 

ワンタイムパスワード

口座番号から自動的にパスワードを生成するワンタイムパスワード方式です。

この方式は利用者別にコンパイルしなくても全員違うパスワードにすることができる利点があります。

ただし、パスワードの生成規則を解析されると意味のないパスワード縛りになるので、生成する式は複雑にすることを推奨です。

生成する式はいろんなアイデアが考えられますので色々とご紹介していきます。

配布後の利便性 

実装の簡単さ 

セキュリティの高さ  ~ 

 

口座番号を5倍した値の下6桁をパスワードとする方法

下桁はいくつでも大丈夫ですが、今回はとりあえず6桁で。

例えば口座番号が12345678なら5倍すると61,728,390になるのでパスワードは下6桁の「728390」になります。

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   long oneTimePassword = AccountInfoInteger(ACCOUNT_LOGIN) * 5 % 1000000;//口座番号を5倍した値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

 

口座番号の全ての桁の和をパスワードにする

こちらは口座番号の全ての桁の和をパスワードにする方法です。

例えば口座番号が12345678ならパスワードは「36」になります。

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   if(p_oneTimePassword != GenerateOneTimePassword(AccountInfoInteger(ACCOUNT_LOGIN))) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}
//口座番号の全ての桁の和を求める関数
int GenerateOneTimePassword(long accountNumber) {
   int oneTimePassword = 0;
   for(int count = GetDigits(AccountInfoInteger(ACCOUNT_LOGIN)); count > 0; count--) {
      oneTimePassword += Number(AccountInfoInteger(ACCOUNT_LOGIN), count);
   }
   return oneTimePassword;
}
//整数の任意の桁の数値を求める関数
int Number(long n, int d) {
   return (int)(n / (long)MathPow(10, d - 1) % 10);
}
//整数の桁数を求める関数
int GetDigits(long num) {
   return (num == 0) ? 1 : ((int)MathLog10(num) + 1);
}

 

毎月変わるワンタイムパスワード

先程のワンタイムパスワードの亜種ですが、月額課金などの用途で使えるアイデアです。

口座番号と月を計算に含めることで生成します。

この例では口座番号×月の値の下6桁をパスワードとしている。例えば口座番号が12345678で4月に使うときのパスワードは 12345678 * 4 = 49,382,712なので「382712」となります。

ただしこの方法では毎年同じ月に同じパスワードになります。年や月を使うときのソースコードはMT4とMT5で違うので2つのパターンをご紹介しておきます。

 

MT4のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   long oneTimePassword = AccountInfoInteger(ACCOUNT_LOGIN) * TimeMonth(TimeLocal()) % 1000000;//口座番号に月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

MT5のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   MqlDateTime time;
   TimeLocal(time);

   long oneTimePassword = AccountInfoInteger(ACCOUNT_LOGIN) * time.mon % 1000000;//口座番号に月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

毎月変わるワンタイムパスワード(改良版①)

こちらは毎年同じ月に同じパスワードにならないように改良したバージョンです。

生成に年を含めることで解決しています。

MT4のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   long oneTimePassword = AccountInfoInteger(ACCOUNT_LOGIN) * TimeYear(TimeLocal()) * TimeMonth(TimeLocal()) % 1000000; //口座番号に年と月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

MT5のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   MqlDateTime time;
   TimeLocal(time);

   long oneTimePassword = AccountInfoInteger(ACCOUNT_LOGIN) * time.year * time.mon % 1000000;//口座番号に月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

 

毎月変わるワンタイムパスワード(改良版②)

年と月を掛けただけでは解析されやすいので、さらに生成を複雑にしたものです。

口座番号の平方根に年と月を掛けています。口座番号は整数であり、MQLで整数の平方根を求めるとdouble型が返ってきますが、それをint型にキャストすると小数点が切り捨てられるということに注意してください。

MT4のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   long oneTimePassword = (int)MathSqrt(AccountInfoInteger(ACCOUNT_LOGIN)) * TimeYear(TimeLocal()) * TimeMonth(TimeLocal()) % 1000000; //口座番号の平方根に年と月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

MT5のコード

input int p_oneTimePassword = 0;//ワンタイムパスワード

int OnInit() {
   MqlDateTime time;
   TimeLocal(time);
   
   long oneTimePassword = (int)MathSqrt(AccountInfoInteger(ACCOUNT_LOGIN)) * time.year * time.mon % 1000000; //口座番号の平方根に年と月を掛けた値の下6桁

   if(p_oneTimePassword != oneTimePassword) {
      Alert("この口座では使用できません。EAを終了します。");
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

 

サーバー認証(MQLAuth)で口座を縛る方法

サーバー認証のしくみ

最後にご紹介するのはMT4の口座番号をサーバー経由で読み取って管理するサーバー認証方式です。

セキュリティは今回ご紹介した中で一番高く、コンパイルをし直す手間なども発生せず、遠隔で利用停止等もできるので一番オススメできる方法です。

ただし、個人でサーバーを借りて導入する方法はコスト面からみると現実的ではないので、MQLAuthサービスを使うのがおすすめです。

こちらはコードが長くなりますので別記事でご紹介しています。

 

配布後の利便性 

実装の簡単さ 

セキュリティの高さ 

 

EAやインジケーターに認証をつける方法
MT4のEAやインジケーターに口座番号認証を実装する方法MQLAuthを使って、EAやインジケーターに口座番号認証を実装する方法を紹介しています。 従来のコピー防止だけでなく、遠隔での停止や決済機能の自動化なども可能になる画期的な認証方法です。...

 

MT4のEAやインジケーターのコピー防止方法【MQL言語】まとめ

yuki
yuki
以上がMT4&MT5で利用できるコピー防止方法のご紹介でした
カオチャイ
カオチャイ
本格的に配布していくならばやはりサーバー認証が最強ですが、初めて実装するという開発者さんはワンタイムパスワードなどを実装してみると色々と理解が深まりおすすめですよ!
EAインジケーターに口座番号認証を実装
EAやインジケーターに口座縛りをつけてコピー対策 ※MQLAuth(エム・キュー・エル・オースと呼びます) MQLAuthのイメージ こちらは口座番号を登録したり...