
MQL5での暗号化の探索:ステップごとのアプローチ
はじめに
アルゴリズム取引の進化し続ける環境において、金融とテクノロジーの融合はトレーダーと開発者に新たな可能性を開きました。自動取引システムの限界を押し広げ続ける中で、特に注目を集めている分野の一つが、取引アルゴリズムへの暗号化の組み込みです。かつては安全な通信やデータ保護のために用いられていた暗号化技術が、今では取引戦略や機密データの保護において重要な役割を果たし、その価値を認識した賢明なトレーダーたちによって活用されています。
この記事では、MQL5プログラミング環境における暗号化の世界を深く掘り下げていきます。アルゴリズム取引とMQL5プログラミングの基礎知識を踏まえ、暗号化関数がどのように取引アルゴリズムのセキュリティと機能性を向上させるのかを解説します。MQL5で利用可能な主要な暗号化手法を分析し、その応用例を理解し、取引戦略にどのように効果的に実装するかを示します。
以下の内容を取り上げます。
- アルゴリズム取引における暗号化
- MQL5における暗号化手法
- CryptEncode関数およびCryptDecode関数
- 実用的なアプリケーションと例
- 電子メールによる安全なシグナル伝送
- 高度なテクニックとベストプラクティス
- 結論
この記事を読み終える頃には、MQL5における暗号化の活用方法をしっかりと理解し、取引アルゴリズムのセキュリティ強化や機密データの保護を実現し、市場での競争優位性を高めるための知識を得られるでしょう。
アルゴリズム取引における暗号化
技術的な側面に進む前に、アルゴリズム取引において暗号化がなぜ重要なのかを理解することが大切です。暗号化は本質的に、情報を保護する科学であり、データの機密性、完全性、そして真正性を確保することを目的としています。取引アルゴリズムの文脈において、暗号化は次のような複数の目的を果たします。
- 知的財産の保護:あなたの取引アルゴリズムは貴重な資産です。コードや特定のコンポーネントを暗号化することで、不正アクセスやリバースエンジニアリングを防ぐことができます。
- データ転送のセキュリティ:アルゴリズムが外部サービスと通信する際、暗号化によってAPIキーやアカウント情報といった機密データを安全に保護します。
- データの整合性の検証:ハッシュ関数を使うことで、データが改ざんされていないことを確認でき、信号やデータフィードの信頼性を保証します。
数ミリ秒が結果に大きな影響を与え、独自の戦略が厳重に保護されるべき秘密である環境では、暗号化を組み込むことがゲームチェンジャーとなり得ます。
MQL5における暗号化手法
MQL5は、開発者が暗号化、ハッシュ、およびデータ圧縮を実装できる一連の暗号化関数を提供します。これらのメソッドを理解することは、暗号化を取引アルゴリズムに効果的に統合するために非常に重要です。
利用可能なメソッドの概要:MQL5の暗号化関数は主に、CryptEncodeとCryptDecodeの2つの操作を中心に構築されています。これらの関数は、ENUM_CRYPT_METHOD列挙型で定義されたさまざまな方法をサポートしています。これらの方法を詳しく見てみましょう。-
暗号化方法
- DES(Data Encryption Standard、データ暗号化規格):56ビットのキーを使用する古い対称鍵アルゴリズムです。歴史的には重要でしたが、現在では安全性が低いとされています。
- AES(Advanced Encryption Standard、高度暗号化標準)
- AES128:128ビットのキーを使用します。
- AES256:256ビットのキーを使用します。より長いキー長により、より高いセキュリティを提供します。
-
ハッシュ方法
- MD5 (Message-Digest Algorithm 5):128ビットのハッシュ値を生成します。広く使用されていますが、衝突攻撃に対して脆弱であると考えられています。
- SHA1 (Secure Hash Algorithm 1):160ビットのハッシュ値を生成しますが、脆弱性があるため、現在は安全性が低いと見なされています。
- SHA256:SHA-2ファミリーに属し、256ビットのハッシュを生成します。現在のところ、ほとんどのアプリケーションで安全とされています。
-
データのエンコードと圧縮
- Base64:バイナリデータをASCII文字にエンコードします。バイナリデータをテキスト形式に埋め込む場合に便利です。
- ZIP圧縮(Deflate):データをdeflateアルゴリズムを使用して圧縮します。データサイズを削減するのに役立ちます。
対称暗号化と非対称暗号化:非対称暗号化: MQL5の組み込み関数は対称暗号化方式をサポートしていることを理解することが重要です。対称暗号化では、同じキーを使ってデータを暗号化および復号化します。これは、公開鍵でデータを暗号化し、秘密鍵で復号化する非対称暗号化とは異なります。
対称暗号化は高速でリソースの消費も少なくなりますが、キーを機密に保つ必要があるため、キー管理が非常に重要です。取引アプリケーションでは、通常、アプリケーション内でキーを安全に保存するか、外部ソースからキーを安全に取得する必要があります。
CryptEncode関数およびCryptDecode関数
MQL5における暗号化の中心は、CryptEncode関数とCryptDecode関数です。これらの関数を使用すると、前述の方法を用いてデータを変換することができます。
CryptEncode関数int CryptEncode( ENUM_CRYPT_METHOD method, const uchar &data[], const uchar &key[], uchar &result[] );
- method:使用する暗号化方式
- data:変換する元のデータ
- key:暗号化方法に使用するキー(ハッシュやBase64の場合は空にできる)
- result:変換後のデータが格納される配列
重要なポイント
- 暗号化方法:特定の長さのキーが必要です(例:AES128の場合は16バイト)。
- ハッシュ方法:キーは必要ありません。
- Base64および圧縮:これらもキーは必要ありませんが、キーパラメータを使ってオプションを指定することができます。
int CryptDecode( ENUM_CRYPT_METHOD method, const uchar &data[], const uchar &key[], uchar &result[] );
- method:逆暗号化に使用する方式
- data:デコードされる変換済みデータ
- key:暗号化時に使用したキー(一致する必要がある)
- result:元のデータが復元される配列
重要なポイント
- 対称暗号化:エンコードとデコードの両方に同じキーを使用する必要があります。
- 不可逆的な方法:ハッシュ関数はデコードできません。
- キー管理:キーを安全に保存・管理することは非常に重要です。追加の保護がない限り、キーをハードコーディングすることはリスクを伴います。
- エラー処理:これらの関数の戻り値は常に確認してください。戻り値が0の場合、エラーが発生したことを示します。
- データ型:データはバイト配列(uchar)で処理されます。文字列とバイト配列の変換時は、特に文字エンコードに注意を払ってください。
簡単な例
理解を深めるために、MQL5スクリプト内でこれらの関数を使用する方法の実践的な例を見てみましょう。
例1:AESによるメッセージの暗号化と復号化
機密メッセージをファイルに保存したりネットワーク経由で送信する前に暗号化するとします。
暗号化スクリプト
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { string message = "Confidential Trade Parameters"; uchar key[16]; uchar data[]; uchar encrypted[]; // Generate a 16-byte key (In practice, use a secure key) for(int i = 0; i < 16; i++) key[i] = (uchar)(i + 1); // Convert message to byte array StringToCharArray(message, data, 0, StringLen(message), CP_UTF8); // Encrypt the data if(CryptEncode(CRYPT_AES128, data, key, encrypted) > 0) { Print("Encryption successful."); // Save or transmit 'encrypted' array } else { Print("Encryption failed. Error code: ", GetLastError()); } }
復号スクリプト
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { uchar key[16]; uchar encrypted[]; // Load encrypted data uchar decrypted[]; // Generate the same 16-byte key for(int i = 0; i < 16; i++) key[i] = (uchar)(i + 1); // Decrypt the data if(CryptDecode(CRYPT_AES128, encrypted, key, decrypted) > 0) { string message = CharArrayToString(decrypted, 0, -1, CP_UTF8); Print("Decryption successful: ", message); } else { Print("Decryption failed. Error code: ", GetLastError()); } }
説明
- キー生成:デモンストレーションのために、単純なキーを生成します。実際のシナリオでは、安全なランダムキーを使用します。
- データ変換:暗号化のために文字列メッセージをバイト配列に変換します。
- エラーチェッキング:暗号化/復号化が成功したかどうかを確認します。
ハッシュは、元のコンテンツを明らかにせずにデータの整合性を検証するのに役立ちます。
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { string dataToHash = "VerifyThisData"; uchar data[]; uchar hash[]; // Convert string to byte array StringToCharArray(dataToHash, data, 0, StringLen(dataToHash), CP_UTF8); // Compute SHA256 hash if(CryptEncode(CRYPT_HASH_SHA256, data, NULL, hash) > 0) { // Convert hash to hexadecimal string for display string hashString = ""; for(int i = 0; i < ArraySize(hash); i++) hashString += StringFormat("%02X", hash[i]); Print("SHA256 Hash: ", hashString); } else { Print("Hashing failed. Error code: ", GetLastError()); } }
説明
- キーは不要:ハッシュ関数にはキーは必要ありません。
- ハッシュ表示:読みやすくするために、ハッシュバイト配列を16進文字列に変換します。
例3:Base64によるデータのエンコード
Base64エンコーディングは、JSONやXMLなどのテキストベースの形式でバイナリデータを含める必要がある場合に便利です。
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { string binaryData = "BinaryDataExample"; uchar data[]; uchar base64[]; // Convert string to byte array StringToCharArray(binaryData, data, 0, StringLen(binaryData), CP_UTF8); // Encode with Base64 if(CryptEncode(CRYPT_BASE64, data, NULL, base64) > 0) { string base64String = CharArrayToString(base64, 0, -1, CP_UTF8); Print("Base64 Encoded Data: ", base64String); } else { Print("Base64 Encoding failed. Error code: ", GetLastError()); } }
説明
- テキスト表現:Base64はバイナリデータをASCII文字列に変換します。
- 一般的な使用例:HTMLに画像を埋め込んだり、テキストベースのプロトコルでバイナリデータを送信したりします。
電子メールによる安全なシグナル伝送
このセクションでは、トレーダーが取引シグナルを電子メールで安全に共有するための詳細な方法について解説します。電子メール通信は本質的に安全性が低く、送信中に機密情報が傍受されたり、改ざんされたりするリスクがあります。信号の機密性と整合性を守るために、MQL5の暗号化およびハッシュ技術を活用した方法を紹介します。
シナリオの概要あなたが、選ばれたクライアントに対して取引シグナルを提供しているプロのトレーダーだとします。これらのシグナルには、エントリーポイントやストップロスレベル、テイクプロフィット目標などの機密情報が含まれており、電子メールで送信されます。不正アクセスを防ぎ、クライアントのみがシグナルを読むことができるようにするためには、メッセージの暗号化が必須です。さらに、送信中にシグナルが改ざんされないように、ハッシュを使ったデジタル署名を付加する必要があります。
目的- 機密保持:取引シグナルを暗号化して、許可されたクライアントだけが復号化して読むことができるようにします。
- 整合性の確保:メッセージにハッシュを追加し、改ざんの検出が可能となるようにします。
- 認証:送信されたシグナルが本当にあなたからのものであることを保証し、なりすましを防ぎます。
ソリューションの概要
ここでは、AES256暗号化を用いてメッセージの内容を保護し、SHA256ハッシュを使ってデジタル署名を作成します。このプロセスには、以下の手順が含まれます。
- 安全なキーの生成:強力な暗号化キーを生成し、事前にクライアントと安全に共有します。
- シグナルの暗号化:送信前に、AES256アルゴリズムを使ってシグナルメッセージを暗号化します。
- ハッシュの作成:暗号化されたメッセージのSHA256ハッシュを計算します。
- メールの送信:暗号化されたメッセージとハッシュを、クライアントに電子メールで送信します。
- クライアントによる復号化:クライアントは、事前に共有されたキーを使ってメッセージを復号化し、ハッシュを検証することで整合性を確認します。
暗号化キーの生成と共有 - キー生成スクリプト(KeyGenerator.mq5):
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { uchar key[32]; // Generate a secure random key for(int i = 0; i < 32; i++) key[i] = (uchar)MathRand(); // Display the key in hexadecimal format string keyHex = ""; for(int i = 0; i < 32; i++) keyHex += StringFormat("%02X", key[i]); Print("Generated Key (Hex): ", keyHex); // Save the key to a file (securely store this file) int fileHandle = FileOpen("encryption_key.bin", FILE_BIN|FILE_WRITE); if(fileHandle != INVALID_HANDLE) { FileWriteArray(fileHandle, key, 0, ArraySize(key)); FileClose(fileHandle); Print("Key saved to encryption_key.bin"); } else { Print("Failed to save the key. Error: ", GetLastError()); } }
重要な注意事項:キー管理は非常に重要です。キーは安全に生成され、安全なチャネル(例:対面での会議や安全なメッセージングアプリ)を通じてクライアントと共有する必要があります。決してキーを電子メールで送信しないでください。
説明
- ランダムキー生成:MathRand() を使用して32バイトのキーを生成します。より高いランダム性を確保するためには、暗号学的に安全な乱数生成器の使用を検討してください。
- キーの表示:記録として保存するために、キーを16進数形式で表示します。
- キーの保存:キーはバイナリファイル「encryption_key.bin」として保存されます。このファイルは安全に保管し、認可されたクライアントのみと共有するようにします。
実用的なヒント
- 安全な乱数生成:可能であれば、暗号学的に安全な乱数生成器を使用してください。
- キーの配布:キーは必ず安全に共有してください。安全でないチャネル(例:電子メール)での送信は避けてください。
取引シグナルの暗号化 - シグナル暗号化スクリプト(SignalSender.mq5):
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { string signal = "BUY EURUSD at 1.12345\nSL: 1.12000\nTP: 1.13000"; uchar data[]; uchar key[32]; uchar encrypted[]; uchar hash[]; uchar nullKey[]; // Load the encryption key int fileHandle = FileOpen("encryption_key.bin", FILE_BIN|FILE_READ); if(fileHandle != INVALID_HANDLE) { FileReadArray(fileHandle, key, 0, 32); FileClose(fileHandle); } else { Print("Failed to load the encryption key. Error: ", GetLastError()); return; } // Convert the signal to a byte array StringToCharArray(signal, data, 0, StringLen(signal), CP_UTF8); // Encrypt the signal int result = CryptEncode(CRYPT_AES256, data, key, encrypted); if(result <= 0) { Print("Encryption failed. Error code: ", GetLastError()); return; } // Compute the hash of the encrypted signal result = CryptEncode(CRYPT_HASH_SHA256, encrypted, nullKey, hash); if(result <= 0) { Print("Hashing failed. Error code: ", GetLastError()); return; } // Convert encrypted data and hash to Base64 for email transmission uchar base64Encrypted[], base64Hash[]; CryptEncode(CRYPT_BASE64, encrypted, nullKey, base64Encrypted); CryptEncode(CRYPT_BASE64, hash, nullKey, base64Hash); string base64EncryptedStr = CharArrayToString(base64Encrypted, 0, WHOLE_ARRAY, CP_UTF8); string base64HashStr = CharArrayToString(base64Hash, 0, WHOLE_ARRAY, CP_UTF8); // Prepare the email content string emailSubject = "Encrypted Trading Signal"; string emailBody = "Encrypted Signal (Base64):\n" + base64EncryptedStr + "\n\nHash (SHA256, Base64):\n" + base64HashStr; // Send the email (Assuming email settings are configured in MetaTrader) bool emailSent = SendMail(emailSubject, emailBody); if(emailSent) { Print("Email sent successfully."); } else { Print("Failed to send email. Error code: ", GetLastError()); } }
説明
- キーの読み込み:暗号化キーはencryption_key.binファイルから読み込みます。
- シグナルの変換:取引シグナルはバイト配列に変換されます。
- 暗号化:CRYPT_AES256を使用して、キーを用いて信号を暗号化します。
- ハッシュ:整合性を確保するために、暗号化されたデータのSHA256ハッシュを計算します。
- Base64エンコーディング:暗号化されたデータとハッシュは、電子メールに適した形式にするためにBase64でエンコードされます。
- メールの準備:暗号化された信号とハッシュを電子メールの本文に含めます。
- メールの送信:SendMailを使用して電子メールを送信します。MetaTraderの電子メール設定が正しく構成されていることを確認してください。
実用的なヒント
- エラー処理:暗号化関数の戻り値は必ず確認し、エラーが発生した場合には適切に処理してください。
- 電子メール設定:MetaTraderでSMTP設定が適切に構成されていることを確認し、電子メール機能が正常に動作するようにします。
- Base64エンコーディング:電子メールなどのテキストベースのプロトコルを介してバイナリデータを送信する場合、Base64エンコーディングは不可欠です。
クライアントサイド:シグナルの復号化 -クライアント復号化スクリプト(SignalReceiver.mq5):
//+------------------------------------------------------------------+ //| SignalReceiver.mq5 | //| Sahil Bagdi | //| https://www.mql5.com/ja/users/sahilbagdi | //+------------------------------------------------------------------+ #property copyright "Sahil Bagdi" #property link "https://www.mql5.com/ja/users/sahilbagdi" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // Received Base64-encoded encrypted signal and hash from email string base64EncryptedStr = "Received encrypted signal in Base64"; string base64HashStr = "Received hash in Base64"; uchar key[32]; uchar encrypted[]; uchar hash[]; uchar computedHash[]; uchar decrypted[]; uchar nullKey[]; // Load the encryption key int fileHandle = FileOpen("encryption_key.bin", FILE_BIN|FILE_READ); if(fileHandle != INVALID_HANDLE) { FileReadArray(fileHandle, key, 0, 32); FileClose(fileHandle); } else { Print("Failed to load the encryption key. Error: ", GetLastError()); return; } // Convert Base64 strings back to byte arrays uchar base64Encrypted[], base64Hash[]; StringToCharArray(base64EncryptedStr, base64Encrypted, 0, WHOLE_ARRAY, CP_UTF8); StringToCharArray(base64HashStr, base64Hash, 0, WHOLE_ARRAY, CP_UTF8); // Decode Base64 to get encrypted data and hash CryptDecode(CRYPT_BASE64, base64Encrypted, nullKey, encrypted); CryptDecode(CRYPT_BASE64, base64Hash, nullKey, hash); // Compute hash of the encrypted data int result = CryptEncode(CRYPT_HASH_SHA256, encrypted, nullKey, computedHash); if(result <= 0) { Print("Hashing failed. Error code: ", GetLastError()); return; } // Compare the computed hash with the received hash if(ArrayCompare(hash, computedHash) != 0) { Print("Signal integrity compromised!"); return; } else { Print("Signal integrity verified."); } // Decrypt the signal result = CryptDecode(CRYPT_AES256, encrypted, key, decrypted); if(result <= 0) { Print("Decryption failed. Error code: ", GetLastError()); return; } // Convert decrypted data back to string string signal = CharArrayToString(decrypted, 0, result, CP_UTF8); Print("Decrypted Trading Signal:\n", signal); // Now you can act upon the trading signal }
説明
- キーの読み込み:クライアントは、送信者と同じ暗号化キーを読み込みます。
- Base64デコード:Base64でエンコードされた暗号化信号とハッシュは、バイト配列に戻されます。
- ハッシュ検証:暗号化されたデータのハッシュを計算し、受信したハッシュと比較して整合性を確認します。
- 復号化:ハッシュが一致した場合、CryptDecodeを使用して信号の復号化を行います。
- 信号取得:復号化されたデータは文字列に変換され、使用できる状態になります。
注意すべき点
重要なメッセージを、従来の紙の封筒ではなく、デジタルチャネルで送信していると仮定してみましょう。詮索好きな第三者からメッセージを守るために、暗号化とハッシュ化は強力な防護手段として機能します。暗号化は、メッセージの内容を暗号化して傍受されても読み取れなくします。一方、ハッシュ化は、受信者がメッセージが途中で変更されていないかを確認できるようにし、まるで本物であることを示す独自の証明書のような役割を果たします。
では、実際にこれがどのように機能するか、具体的なシナリオを見てみましょう。
-
安全なキーストレージ:金庫の鍵を家の玄関マットの下に置いておくのは、あまり安全とは言えません。暗号化キーも同様で、不正なアクセスを防ぐためには、安全に保管する必要があります。簡単に手に入る場所に置いておくのは、まるでドアの鍵をかけないで出かけるようなものです。
-
ハッシュ検証:重要な荷物を宅配便で送る際に、受取人が確認できる追跡コードを使うのと同じように、ハッシュ検証によってデータが転送中に改ざんされていないかを確認できます。もし途中でデータに変更があった場合、ハッシュがそれを示して、改ざんの兆候を知らせてくれます。
-
キー管理:家の鍵を友達に郵送するのは危険なアイデアです。暗号化でも同じことが言えます。暗号化キーは、暗号化通信を安全に行うためには、必ず安全なチャネルを使って送信しなければなりません。
-
定期的なキー更新:数年同じ鍵を使い続けることは、ドアの鍵を一度も交換しないのと同じです。セキュリティを最適化するために、暗号化キーを定期的に更新し、リスクを軽減することが重要です。
さらに、セキュリティを強化するために、非対称暗号化(デジタル署名など)を利用することで、メッセージの真正性を証明できます。MQL5ではこの機能はネイティブではサポートされていませんが、外部ライブラリを使うことで実現することが可能です。
高度なテクニックとベストプラクティス
ここでは、MQL5での暗号化使用に関する高度なヒントとベストプラクティスをいくつか紹介します。
-
キーの安全な管理:効果的なキー管理は、セキュリティにおいて非常に重要です。以下の点を考慮してください。
- 安全な保管:キーをコード内にハードコーディングするのは避けましょう。代わりに、暗号化されたファイルに保存したり、安全なソースから取得することをおすすめします。
- ダイナミックキー:実行時に安全な乱数生成器を使用してキーを動的に生成します。
- 定期的なキーローテーション:定期的にキーをローテーションすることで、侵害のリスクを最小限に抑えます。
-
暗号化方式の組み合わせ:暗号化方法を組み合わせることで、セキュリティを強化できます。以下が例です。
- 暗号化とハッシュ:暗号化後に、暗号文のハッシュを計算して、復号化時にデータの整合性を確認できるようにします。
- 暗号化前の圧縮:データを暗号化する前に圧縮することで、データサイズを小さくし、複雑さをさらに増します。
-
エラー処理とデバッグ:暗号化機能はさまざまな理由で失敗する可能性があるため、堅牢なエラー処理が非常に重要です。
- 無効なパラメータ:キーが正しい長さであること、データ配列が適切に初期化されていることを確認します。
- メモリ不足:大きなデータ配列はメモリの問題を引き起こす可能性があります。
- GetLastError()の使用:GetLastError()を使用してエラーコードを確認し、問題を効果的にトラブルシューティングします。
-
パフォーマンス分析:暗号化プロセスは計算資源を大量に消費することがあるため、セキュリティと効率性のバランスを取ることが重要です。
- 処理オーバーヘッド:暗号化とハッシュ化には計算能力が必要ですので、機密データのみを保護するようにします。
- アルゴリズムの選択:より高速なアルゴリズム(例:AES128 vs. AES256)を選択することで、セキュリティとパフォーマンスのバランスを取ります。
これらの方法は堅牢なロックのように機能し、暗号化はデータを不正アクセスから守る強力な盾となります。
結論
暗号化はアルゴリズム取引の分野で強力なツールであり、データのセキュリティ、整合性、機密性を守るための重要な手段です。暗号化技術をMQL5プログラムに統合することで、取引アルゴリズムを保護し、機密データを守り、取引システムの信頼性を高めることができます。
この記事では、MQL5で利用できるさまざまな暗号化関数を紹介し、実際の使用例を深掘りし、高度なテクニックとベストプラクティスについて解説しました。主なポイントは次の通りです。
- 暗号化方式の理解:様々なアルゴリズムの特長と用途を理解する
- CryptEncodeとCryptDecodeの実装:これらの関数を効果的に使用してデータを安全に変換する方法
- 安全なキー管理:暗号鍵を保護する重要性を認識する
- 実用的なアプリケーション:実際の取引シナリオにおける暗号化の適用方法でセキュリティを強化する
アルゴリズム取引が進化する中で、暗号化技術の重要性はますます高まっています。これらのツールを活用することで、トレーダーや開発者はデータセキュリティの課題に上手に対処し、市場での競争優位を維持することができるでしょう。
コーディングと取引をお楽しみください。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16238





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索