
プロのプログラマーからのヒント(第2部): パラメータの保存とエキスパートアドバイザー、スクリプト、外部プログラム間での交換
内容
はじめに
本稿では、ターミナルの再起動(シャットダウン)後に復元できるパラメータについて説明します。すべての例は、私のCaymanプロジェクトからの実際に機能するコードセグメントです。
パラメータの保存場所
パラメータの例
- ゼロバー時間: たとえば、ローソク足パターンを検出する場合、特定の時間枠で新しいバーが出現した後に一度評価するのが論理的です。
- 取引レベルのパラメータ: たとえば、取引レベルを選択し、スクリプトを使用して、レベルブレイクアウトの場合に開かれる取引の時間とサイズを設定できます。スクリプトはパラメータをエキスパートアドバイザーに渡します。エキスパートアドバイザーは、レベルアナライザーを作成します。アナライザーは、指定された時間枠で新しいバーが出現した後にのみ「オン」になります。
- ユーザー環境設定: 色、取引ルール、描画方法、その他のパラメータが含まれます。明らかに、そのようなパラメータは、たとえばセットファイルに一度インストールする必要があります。
- ターミナルグローバル変数
- グラフィカルオブジェクト
- 注文コメント
- テキストファイル
ストレージ | 種類 | スコープ | 有効期限 |
---|---|---|---|
ターミナルグローバル変数 | double | すべてのチャート | 最後の電話から4週間後 |
グラフィカルオブジェクト | 任意。文字列(63文字以下) | 現在のチャート | チャートの有効期間 |
注文コメント | 文字列(23文字以下) | すべてのチャート | ターミナルの有効期間< |
テキストファイル | 任意。制限なし | すべてのチャート | ファイルの有効期間 |
ターミナルグローバル変数
ターミナルグローバル変数は、どのチャートからでも利用できます。スコープは、ChartId、Symbol、Periodなど、変数名に追加のコンポーネントを含めることで制限できます。変更できないのは変数の型です。テキストを保存することはできません。
整数値をパック/アンパックするというライフハックはあります。ご存知のように、doubleは8バイト(64ビット)を使用します。次の例を確認してください。この例では、1つの変数に複数の整数値を格納する方法を示しています。最も重要なことは、それらの最大値のビットサイズを決定することです。
// ----------------------------------------------------------------------------- // ビット演算を使用してグローバル変数と整数値の間を | // パック/アンパックする例 | // ----------------------------------------------------------------------------- void OnStart() { int value10 = 10; // 最大値 = 255 (8ビット) int value20 = 300; // 最大値 = 65535 (16ビット) bool value30 = true; // 最大値 = 1 (1ビット) // 値を25ビット(8+16+1)にパックする // 残り39ビット(64-25)が未使用のまま ulong packedValue = (value10 << 17) + // value20、value30用のスペース(16+1)を予約 (value20 << 1) + // value30用のスペース(1)を予約 value30; // グローバル変数を保存 string nameGVar = "temp"; GlobalVariableSet(nameGVar, packedValue); // グローバル変数を読み取る packedValue = (ulong)GlobalVariableGet(nameGVar); // 値をアンパック // 0xFF, 0xFFFF, 0x1 - 最大値のビットマスク int value11 = (int)((packedValue >> 17) & 0xFF); int value21 = (int)((packedValue >> 1) & 0xFFFF); bool value31 = (bool)(packedValue & 0x1); // 値を比較 if (value11 == value10 && value21 == value20 && value31 == value30) Print("OK"); else PrintFormat("0x%X / 0x%X /0x%X / 0x%X", packedValue, value11, value21, value31); }
グラフィカルオブジェクト
スクリプトパラメータはグラフィカルオブジェクトに保存できるでしょうか。やってみましょう。オブジェクトプロパティOBJPROP_PRICE = 0を設定します。この場合、オブジェクトは視覚的に「非表示」ですが、プログラム内でアクセスできます。信頼性のために、そのようなオブジェクトはチャートテンプレートに保存できます。パラメータアクセスロジックは、オブジェクトがある場合はパラメータを抽出し、オブジェクトがない場合はデフォルト値を設定することです。
注文コメント
注文コメントの最大長は23文字に制限されています。コメントには何を保存できるでしょうか。例ば「SOP/H1/SS/C2/Br/Br/Br」です。ここで(左から右)
- SOP — 注文送信者(SOP – SendOrderByPlanスクリプト)
- H1 — 注文生成の時間枠(H1)
- SS — 注文タイプ(SS –売りストップ)
- C2 — 注文決済アルゴリズム
- Br — D1トレンド(Br – 弱気)
- Br — H4トレンド(Br – 弱気)
- Br — 注文生成時間枠トレンド(Br – 弱気)
これは何故必要なのでしょうか。たとえば、このデータは取引の分析に使用できます。私の使用方法は次のとおりです。未決注文がトリガーされたら、決済アルゴリズムの値を抽出し、仮想ストップアナライザーAnalyserVirtSLを作成します。これにより、特定の条件下で取引が決済されます。
テキストファイル
これはおそらく、回復パラメータを保存するための最も信頼性が高く、普遍的な方法です。アクセスクラスを一度設定すれば、いつでもどこでも必要なときに使用できます。
アプリケーション設定
以下は、AppSettings.txt設定ファイルの一部です。
# ------------------------------------------------------------------- # エキスパートアドバイザーとスクリプトの設定 # ファイルエンコーディング= UCS-2 LE BOMあり(必須) // Unicode # ------------------------------------------------------------------- TimeEurWinter = 10:00 # 欧州セッション開始冬時間(サーバー時間) TimeEurSummer = 09:00 # 欧州セッション開始夏時間(サーバー時間) ColorSessionEur = 224,255,255 # 欧州セッションの色 ColorSessionUsd = 255,240,245 # 米国セッションの色 NumberColorDays = 10 # 強調表示された日数(セッション数)
AppSettings.mqhクラス
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #property strict #include <Cayman/Params.mqh> // アプリケーションパラメータ名 #define APP_TIME_EUR_SUMMER "TimeEurSummer" #define APP_TIME_EUR_WINTER "TimeEurWinter" #define APP_TIME_TRADE_ASIA "TimeTradeAsia" #define APP_COLOR_SESSION_EUR "ColorSessionEur" #define APP_COLOR_SESSION_USD "ColorSessionUsd" #define APP_NUMBER_COLOR_DAYS "NumberColorDays" // ----------------------------------------------------------------------------- // エキスパートアドバイザーとスクリプトの一般設定 | // ----------------------------------------------------------------------------- class AppSettings { private: Params *m_params; public: // AppSettings.txtファイルで設定 string TimeEurSummer; // 欧州セッション開始夏時間 string TimeEurWinter; // 欧州セッション開始冬時間 string TimeTradeAsia; // 亜州取引終了時間 color ColorSessionEur; // 欧州セッションの色 color ColorSessionUsd; // 米国セッションの色 int NumberColorDays; // 強調表示された日数 // プログラムで設定 string PeriodTrends; // トレンド計算期間(D1,H4) string TradePlan; // 取引方向(短期) bool IsValid; // パラメータの有効性 // メソッド AppSettings(); ~AppSettings() { delete m_params; }; void Dump(string sender); }; // ----------------------------------------------------------------------------- // コンストラクタ | // ----------------------------------------------------------------------------- AppSettings::AppSettings() { IsValid = true; m_params = new Params(); m_params.Load(PATH_APP_SETTINGS); if (m_params.Total() == 0) { PrintFormat("%s / ERROR: Invalid file / %s", __FUNCTION__, PATH_APP_SETTINGS); IsValid = false; return; } TimeEurWinter = m_params.GetValue(APP_TIME_EUR_WINTER); TimeEurSummer = m_params.GetValue(APP_TIME_EUR_SUMMER); TimeTradeAsia = m_params.GetValue(APP_TIME_TRADE_ASIA); ColorSessionEur = StringToColor(m_params.GetValue(APP_COLOR_SESSION_EUR)); ColorSessionUsd = StringToColor(m_params.GetValue(APP_COLOR_SESSION_USD)); NumberColorDays = (int)StringToInteger(m_params.GetValue(APP_NUMBER_COLOR_DAYS)); } // ----------------------------------------------------------------------------- // 出力設定パラメータ | // ----------------------------------------------------------------------------- void AppSettings::Dump(string sender) { PrintFormat("sender=%s / %s", sender, PATH_APP_SETTINGS); PrintFormat("%s = %s", APP_TIME_EUR_WINTER, TimeEurWinter); PrintFormat("%s = %s", APP_TIME_EUR_SUMMER, TimeEurSummer); PrintFormat("%s = %s", APP_TIME_TRADE_ASIA, TimeTradeAsia); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_EUR, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %s / %s", APP_COLOR_SESSION_USD, ColorToString(ColorSessionEur), ColorToString(ColorSessionEur, true)); PrintFormat("%s = %i", APP_NUMBER_COLOR_DAYS, NumberColorDays); }
機能
AppSettingsクラスの宣言は、#includeを介して任意のエキスパートアドバイザーおよびスクリプトに接続されているUterminal.mqhファイルにあります。
extern AppSettings *gAppSettings; // アプリケーション設定
このソリューションを使用すると、次のことができます。
- gAppSettingsを任意の場所で1回初期化する
- (パラメータとして渡す代わりに)任意のクラスインスタンスでgAppSettingsを使用する
アナライザーパラメータ
Caymanエキスパートアドバイザーでは、AnalyzerTrend、AnalyserLevel、AnalyserVirtSLなどのさまざまなアナライザーを管理します。各アナライザーは特定の時間枠にリンクされています。これは、指定された時間枠に新しいバーが出現したときにのみアナライザーが起動されることを意味します。アナライザーの例は、Key = Value文字列とともにテキストファイルに保存されます。たとえば、H4取引レベルアナライザーのパラメータはFiles\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txtファイルに保存されます。
- Cayman — プロジェクト名
- Params — アナライザパラメータを含むサブディレクトリ
- 128968168864101576 — チャートID // IntergerToString(ChartID())
- exp_05_Lev607A160E_H4.txt — アナライザーパラメータを含むファイルの名前—
- exp — プレフィックス
- 05 — アナライザータイプ
- Lev607A160E —アナライザー名(取引レベル)
- H4 — 追跡された時間枠
以下はコメント付きのファイルの内容です(実際のファイルにはコメントがありません)。
// 取引レベルパラメータ nameObj=Lev607A160E // 取引レベル名 kindLevel=1 // レベルタイプ(1 - 抵抗) riskValue=1.00 // レベルブレイクアウト時の取引量(1) riskUnit=1 // 取引量変更単位(1 - 証拠金の割合) algClose=2 // 取引成立アルゴリズム(2 – 2つの修正バー) ticketNew=0 // レベルブレイクアウト時に開かれた取引のチケット ticketOld=0 // レベルブレイクアウト時に取引を成立させるためのチケット profits=0 // ポイント単位の計画利益 losses=0 // ポイント単位の計画損失 // アナライザーパラメータ symbol=EURUSD // 銘柄名 period=16388 // アナライザー期間(H4) time0Bar=1618603200 // ゼロバー時間(秒) typeAnalyser=5 // アナライザータイプ colorAnalyser=16711935 // アナライザー結果の色 resultAnalyser=Lev607A160E, H4, 20:00, RS // アナライザーの結果
任意のアナライザーのパラメータを保存および復元できる基本クラスAnalyserがあります。エキスパートアナライザーが再起動されると(たとえば、時間枠を切り替えた後)、アナライザーで関連するテキストファイルからパラメータが復元されます。新しいバーがまだ登場していない場合、分析は再開されません。前のバーで計算されたアナライザーの結果(resultAnalyser、colorAnalyser)は、エキスパートアナライザーのコメントに表示されます。
エキスパートアドバイザーへのスクリプトパラメータの受け渡し
SetTradeLevelスクリプトを使用すると、取引レベルパラメータを設定できます。チャート上で1つのオブジェクト(直線、トレンドライン、または長方形)が選択されています。SetTradeLevelスクリプトは、選択されたオブジェクト(取引レベル)を見つけて、そのパラメータを設定します。
次に、スクリプトはパラメータをFiles\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txtに保存し、SendCommand関数を介してコマンドとファイルへのパスを送信します。
// ----------------------------------------------------------------------------- // レベルパラメータをエキスパートアドバイザーに送信する | // ----------------------------------------------------------------------------- NCommand SendCommand() { // レベルパラメータを読み込む(あれば) Params *params = new Params(); string speriod = UConvert::PeriodToStr(_Period); params.Load(PREFIX_EXPERT, anaLevel, gNameLev, speriod); // コマンドを定義 NCommand cmd = (gKindLevel == levUnknown) ? cmdDelete : (params.Total() > 0) ? cmdUpdate : cmdCreate; // パラメータを保存 params.Clear(); params.Add(PARAM_NAME_OBJ, gNameLev); params.Add(PARAM_TYPE_ANALYSER, IntegerToString(anaLevel)); params.Add(PARAM_PERIOD, IntegerToString(_Period)); params.Add(PARAM_KIND_LEVEL, IntegerToString(gKindLevel)); params.Add(PARAM_RISK_VALUE, DoubleToString(gRiskValue, 2)); params.Add(PARAM_RISK_UNIT, IntegerToString(gRiskUnit)); params.Add(PARAM_ALG_CLOSE, IntegerToString(gAlgClose)); params.Add(PARAM_TICKET_OLD, IntegerToString(gTicketOld)); params.Add(PARAM_PROFITS, IntegerToString(gProfits)); params.Add(PARAM_LOSSES, IntegerToString(gLosses)); params.Save(); // コマンドをエキスパートアドバイザーに送信 params.SendCommand(cmd); delete params; return cmd; }
params.SendCommand(cmd)関数は次のとおりです。
// ----------------------------------------------------------------------------- // コマンドをエキスパートアドバイザーに送信する | // ----------------------------------------------------------------------------- void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); }
ティックごとに(OnTick)、エキスパートアドバイザーはCheckExpernalCommand()関数を介してNAME_OBJECT_CMDという名前のオブジェクトの存在を確認します。存在する場合は、コマンドとアナライザーパラメータを含むファイルへのパスが読み取られ、オブジェクトはすぐに削除されます。次にエキスパートアドバイザーは、実行中のアナライザーをファイル名で検索します。cmd == cmdDeleteの場合、アナライザーは削除されます。cmd == cmdUpdateの場合、アナライザーのパラメータはファイルから更新されます。cmd == cmdNewの場合、ファイルのパラメータを使用して新しいアナライザーが作成されます。
以下は、パラメータファイル(Key = Value文字列)を操作するためのロジックをカプセル化するParamsクラス全体です。
#property copyright "Copyright 2020, Malik Arykov" #property link "malik.arykov@gmail.com" #include <Arrays/ArrayString.mqh> #include <Cayman/UConvert.mqh> #include <Cayman/UFile.mqh> // ----------------------------------------------------------------------------- // パラメータクラス(key=value文字列と#コメント) | // ----------------------------------------------------------------------------- class Params { private: string m_path; // パラメータファイルへのパス NCommand m_cmd; // エキスパートアドバイザーへのコマンド CArrayString *m_items; // {key=value}対の配列 int Find(string key); public: Params(); ~Params() { delete m_items; }; void Clear() { m_items.Clear(); }; int Total() { return m_items.Total(); }; string Path() { return m_path; }; CArrayString *Items() { return m_items; }; void Add(string line) { m_items.Add(line); }; bool Add(string key, string value); string GetValue(string key); void Load(string prefix, int typeAnalyser, string nameObj, string speriod); void Load(string path); void Save(); void SendCommand(NCommand cmd); NCommand TakeCommand(); void Dump(string sender); }; // ----------------------------------------------------------------------------- // デフォルトのコンストラクタ | // ----------------------------------------------------------------------------- Params::Params() { m_items = new CArrayString(); } // ----------------------------------------------------------------------------- // key=value対を追加する | // ----------------------------------------------------------------------------- bool Params::Add(string key, string value) { int j = Find(key); string line = key + "=" + value; if (j >= 0) { // update m_items.Update(j, line); return false; } else { // add m_items.Add(line); return true; } } // ----------------------------------------------------------------------------- // キーでパラメータの値を取得する | // ----------------------------------------------------------------------------- string Params::GetValue(string key) { // キーを見つける int j = Find(key); if (j < 0) return NULL; // no key // 区切り文字を確認 string line = m_items.At(j); j = StringFind(line, "="); if (j < 0) { // no = PrintFormat("%s / ERROR: Invalid string %s", __FUNCTION__, line); return NULL; } // 値を返す return UConvert::Trim(StringSubstr(line, j + 1)); } // ----------------------------------------------------------------------------- // キーでパラメータの値を見つける | // ----------------------------------------------------------------------------- int Params::Find(string key) { int index = -1; for (int j = 0; j < m_items.Total(); j++) { if (StringFind(m_items.At(j), key) == 0) { index = j; break; } } return index; } // ----------------------------------------------------------------------------- // パラメータを読み込む | // ----------------------------------------------------------------------------- void Params::Load(string prefix, int typeAnalyser, string nameObj, string speriod) { string nameFile = StringFormat("%s%02i_%s_%s.txt", prefix, typeAnalyser, nameObj, speriod); m_path = StringFormat("%s%s/%s", PATH_PARAMS, IntegerToString(ChartID()), nameFile); if (FileIsExist(m_path)) Load(m_path); } // ----------------------------------------------------------------------------- // パラメータを読み込む | // ----------------------------------------------------------------------------- void Params::Load(string path) { m_path = path; if (!FileIsExist(m_path)) return; //PrintFormat("%s / %s", __FUNCTION__, m_path); string text = UFile::LoadText(m_path); if (text == NULL) return; // テキストを行に分ける string line, lines[]; int numLines = StringSplit(text, DLM_LINE, lines); for (int j = 0; j < numLines; j++) { line = lines[j]; // コメントを削除 int k = StringFind(line, "#"); if (k == 0) continue; // the whole string is a comment if (k > 0) line = StringSubstr(line, 0, k); // 空以外の文字列を追加 if (line != "") m_items.Add(line); } } // ----------------------------------------------------------------------------- // パラメータを保存する | // ----------------------------------------------------------------------------- void Params::Save() { string text = ""; for (int j = 0; j < m_items.Total(); j++) { text += m_items.At(j) + "\n"; } // 既存のファイルを書き換える UFile::SaveText(text, m_path, true); } // ----------------------------------------------------------------------------- // コマンドをエキスパートアドバイザーに送信する | // ----------------------------------------------------------------------------- void Params::SendCommand(NCommand cmd) { string nameObj = NAME_OBJECT_CMD; ObjectCreate(0, nameObj, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, nameObj, OBJPROP_TEXT, m_path); ObjectSetInteger(0, nameObj, OBJPROP_ZORDER, cmd); ObjectSetInteger(0, nameObj, OBJPROP_TIMEFRAMES, 0); } // ----------------------------------------------------------------------------- // スクリプトからコマンドを受け取る | // ----------------------------------------------------------------------------- NCommand Params::TakeCommand() { string nameObj = NAME_OBJECT_CMD; if (ObjectFind(0, nameObj) < 0) return cmdUnknown; m_path = ObjectGetString(0, nameObj, OBJPROP_TEXT); m_cmd = (NCommand)ObjectGetInteger(0, nameObj, OBJPROP_ZORDER); ObjectDelete(0, nameObj); Load(m_path); return m_cmd; } // ----------------------------------------------------------------------------- // |パラメータを出力する | // ----------------------------------------------------------------------------- void Params::Dump(string sender) { for (int j = 0; j < m_items.Total(); j++) { PrintFormat("%s / %s", sender, m_items.At(j)); } }
MQL5ファンの方: m_itemの型をCHashMapに変更すると、Add、GetValue、Find関数のコードが大幅に削減されます。ただし、ParamsクラスはMQL4でも使用されます。さらに、この場合、ローカル変数を初期化するためにパラメータが1回読み取られるため、パラメータのアクセス速度は重要ではありません。MQL5のCHashMapのクラスを作り直さなかったのは、銀行で長く働いていたせいかもしれません。金融ソフトウェア開発者には非常に重要な原則があります。機能しているものには、触れないということです。;-)
外部プログラムへのパラメータの受け渡し
異なるシステム間のデータ交換ユニットは、事実上jsonファイルです。以前はxmlファイルでした。jsonファイルの主な利点は次のとおりです。
- 作成のしやすさ(生成/形式設定)
- すべてのハイレベル言語での優れたサポート
- 読みやすさ
たとえば、フィールドm_time、m_open、m_high、m_low、m_close、m_bodyを持つBarクラスがあるとします:。m_bodyはローソク足の色(白、黒、同事)です。Barクラスには、json文字列を生成するToJson()メソッドがあります。
string Bar::ToJson() { return "{" + "\n\t\"symbol\":\"" + _Symbol + "\"," + "\n\t\"period\":" + IntegerToString(_Period) + "," + "\n\t\"digits\":" + IntegerToString(_Digits) + "," + "\n\t\"timeBar\":\"" + TimeToStr(m_time) + "\"," + "\n\t\"open\":" + DoubleToString(m_open, _Digits) + "," + "\n\t\"high\":" + DoubleToString(m_high, _Digits) + "," + "\n\t\"low\":" + DoubleToString(m_low, _Digits) + "," + "\n\t\"close\":" + DoubleToString(m_close, _Digits) + "," + "\n\t\"body\":" + IntegerToString(m_body) + "," + "\n}"; }
代わりにStringFormatを使用することもできますが、値の再配置または削除中に問題が発生します。オンラインのjsonフォーマットサービス多数あるため、「\ n\t」のフォーマッティングは削除してもいいでしょう。JSONパーサーはその1つです。有効なjsonの受信を一度設定し、必要なときにいつでもbar.ToJson()関数を使用します。
C#アプリケーションなどの外部プログラムは、あらゆる複雑さのjsonファイルをオブジェクトに変換できます。MQLからjsonファイルを転送する方法これはとても簡単です。たとえば、jsonファイルをFiles/Jsonターミナルディレクトリにロード(保存)します。外部プログラムは、このディレクトリで新しいファイルを監視します。ファイルが見つかると、プログラムはそれを読み取り、オブジェクトに変換し、すぐにファイルを削除するか、アーカイブに移動します(統計用)。
外部プログラムからのパラメータの受信
jsonライブラリをMQLプログラムに接続する(ホイールを再発明する)と、余分な問題が発生します。より良い解決策は、Key = Value文字列を含むテキストファイルを渡すことです。ファイルは、Paramsクラスを使用して処理できます(上記を参照)。エキスパートアドバイザーと指標は、外部プログラムまたはスクリプトからパラメータを受け取る候補です。たとえば、OnTickハンドラーでCheckExternalCommand()関数を呼び出す必要がると、Files/ExtCmdディレクトリ内のファイルの存在が確認されます。ファイルが見つかると、ファイルの読み取り、処理(パラメータの受け入れ)、および削除が行われます。
そこで、MQLと外部プログラムの間でパラメータを送受信する方法を検討しました。ここで、次のことを考えてみてください。MQLプログラムにDLLが必要なのは何故ですか。このようなプログラムは、MQLマーケットでは受け入れられていません。ここでの理由は「セキュリティ」です。DLLから何にでもアクセスできるためです。
スマートフォンへのパラメータの受け渡し
今後の操作については、AndroidアプリWirePusherを使用します。これは素晴らしいサービスです(無料で追加料金なし)。iPhone用に似たものがあるかどうかは知りません。iPhoneファンの方がお読みでしたら、コメントを共有してください。
サービスの使用を開始するには:、以下を実行します。
- スマートフォンにWirePusherをインストールします
- アプリケーションを起動しますメイン画面にIDが表示されます
- https://wirepusher.comをTerminal/Service/Settings/Experts/Allow WebRequestに追加します。
次に、スクリプトを起動します(id =“ ********”にアスタリスクの代わりに自分のIDを使用してください)。
void OnStart() { string id = "**********"; // WirePusherでのスマホID WirePusher("Profit $1000", "Deal", "Closed", id); } // ------------------------------------------------------------------------------------------------ // WirePusherWebサービスを介してスマートフォンに通知を送信 // https://wirepusher.comをTerminal/Service/Settings/Experts/Allow WebRequestに追加 // message - 通知のテキスト // title - 通知のタイトル(例: 注意/アラート/取引) // type - 通知タイプ(例: トリガーされた未決注文/レベルのブレークアウト/成立) // id - WirePusher Androidアプリからの一意のスマートフォンID // ------------------------------------------------------------------------------------------------ bool WirePusher(string message, string title, string type, string id) { char data[]; // HTTPメッセージ本文データ配列 char result[]; // Webサービス応答データ配列 string answer; // Webサービス応答ヘッダー string url = "https://wirepusher.com/send?id={id}&title={title}&message={message}&type={type}"; StringReplace(url, "{id}", id); StringReplace(url, "{type}", type); StringReplace(url, "{title}", title); StringReplace(url, "{message}", message); ResetLastError(); int rcode = WebRequest("GET", url, NULL, 3000, data, result, answer); if (rcode != 200) { PrintFormat("%s / error=%i / url=%s / answer=%s / %s", __FUNCTION__, GetLastError(), url, answer, CharArrayToString(result)); return false; } PrintFormat("%s / %s / %s", __FUNCTION__, title, message); return true; }
Cayman EAでは、WirePusher関数は次の場合にAnalyserTradeで呼び出されます。
- 未決注文がトリガーされる
- 取引レベルで価格がブレークする
- 取引が成立する
WirePusherの各通知タイプには個別の音声を割り当てることができます。以前は、利益をもって成立した取引には「ta-da」サウンド、損失で成立した取引には「bomb」サウンドがあったのですが、依頼、「bomb」サウンドにうんざりしました。
終わりに
パラメータを保存するための最も信頼性が高く便利な方法は、テキストファイルを使用することです。さらに、ファイル操作はすべてのオペレーティングシステム(アプリケーション)で完全にサポート/キャッシュされます。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/9327





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