English Русский 中文 Español Deutsch Português
トレードオブジェクト: メタトレーダーのグラフィカルオブジェクトに基づいたトレードの自動化

トレードオブジェクト: メタトレーダーのグラフィカルオブジェクトに基づいたトレードの自動化

MetaTrader 5テスター | 19 10月 2017, 08:24
1 295 0
Stanislav Korotky
Stanislav Korotky
チャート分析は、何十年もの間、最も人気のあるトレードツールです。 高度な技術により、チャンネルや Fibo グリッドなどのサポートやレジスタンス、ヒストリカル価格レベル、パターンを適用することができるようになります。 アルゴリズムトレードのソフトウェアは、両方を使用して標準的なパターンとトレードを分析することができます。 MetaTrader には、プロセスをある程度自動化するアプリケーションもあります。ほとんどの場合、EA またはスクリプトを使用してチャートにオブジェクトを追加するだけで十分です。 このアプリケーションは、ポジションを開き、 追跡し、設定に応じて時間内に閉じます. このようなアプリケーションは、オンラインでのトレードだけでなく、テスターのビジュアルモードでスキルを磨くことができます。 このようなアプリケーションは、コードベース及びマーケットにあります。

しかし、すべてが、単純にいくという訳ではありません。 通常、コードベースアプリケーションの関数は簡略化されています。 しかも、めったに更新されず、時代遅れです。 (多くの場合、最新の MQL 言語とプラットフォームのバージョンとの互換性を失う) 一方、商用プロダクトはあまりにも高価です。

この記事では、中間の新しいツールを開発します。 できるだけ、十分な機会を提供します。 MetaTrader4とMetaTrader5の両方の互換性があります。 オープンソースコードのおかげで、ニーズに合わせて変更することができます。

MetaTrader4のサポートは、MetaTrader5テスターの制限にも重要です。 特に、MetaTrader5のビジュアルテスターは現在、このツールに必要な対話型のメソッド (追加、削除、プロパティの編集) でオブジェクトを操作することはできません。 唯一MetaTrader4のテスターは、ヒストリー上のオブジェクトを管理するスキルを磨くことができます。


要件の設定

グラフィカルなオブジェクトを含む自動化されたトレーディングシステムのメインの要件はシンプルです。 特別なパネルと複雑な構成ロジックなしで、グラフィカルオブジェクトの標準メタトレーダーインターフェイスとプロパティを使用する予定です。 様々なオプションに満ちた強力なシステムの利点は、しばしば、それぞれの特定の操作モードの開発と関連性の難しさによって平準化されます。 オブジェクトとそのプロパティの直感的な解釈の周知のメソッドだけを使用するように努力します。

トレード決定を行うための複数のタイプのグラフィカルオブジェクトの中で、最も一般的に使用されるものは次のとおりです。

  • trend line;
  • horizontal line;
  • vertical line;
  • equidistant channel;
  • Fibo。

最初にこれらのサポートをします。 このリストは MetaTrader5 で利用可能な新しいタイプによって拡張されている可能性がありますが、MetaTrader4との互換性が壊れています。 ユーザーは、このプロジェクトで実装されているオブジェクトを処理する基本的な原則を適用することによって、他のオブジェクトを追加できます。

上記の各オブジェクトは、チャートの2次元空間に論理的な境界を形成します。 その境界からのクロスまたはロールバックはシグナルになります。 この解釈は、通常、次のいずれかの状況に対応します。

  • サポート/レジスタンスラインからのブレイクダウンまたはロールバック;
  • ヒストリカルレベルからのブレイクダウンまたはロールバック;
  • 指定されたストップロスに到達する/テイクプロフィットを取る;
  • 指定された時間に到達。

選択した戦略に応じて、トレーダーは、買いまたは売り、保留中のオーダーを設定したり、イベントの結果としてポジションを閉じることができます。 このシステムは、すべてのアクションをサポートする必要があります。 したがって、基本的な関数のリストは次のようになります。

  • いくつかの通知 (アラート、プッシュ通知、メール);
  • 成行オーダーを開く;
  • 予約オーダーを置く (買いストップ、売りストップ、買いリミット、売りリミット);
  • ストップロスやテイクプロフィットなど、ポジションの完全または部分的なクローズ。

ユーザーインターフェイスの開発

多くの類似プロダクトでは、パネル、ダイアログ、ボタンなどのユーザーインターフェイス要素に多くの注目が払われています。 このプロジェクトには、どれもありません。 特別なグラフィカルインターフェイスの代わりに、MetaTrader によって提供されるデフォルトの要素を使用してみましょう。

各オブジェクトには、現在のタスクに適応できるプロパティの標準セットがあります。

最初に、チャートにプロットできる他のオブジェクトからの自動トレードを目的としたオブジェクトを区別する必要があります。 これを行うには、あらかじめ定義されたプレフィックスを使用してオブジェクト名を指定する必要があります。 プレフィックスが指定されていない場合、EA は、適切なプロパティを持つすべてのオブジェクトがアクティブであると見なします。 ただし、このようなモードは、ターミナルが独自のオブジェクト (たとえば、閉じたオーダー) を作成できるため、推奨されません。

2 番目に、さまざまな関数 (上記の関数の一覧) を実行するために、異なる実行スタイルを予約する必要があります。 たとえば、EA の設定で、保留中のオーダーと STYLE_SOLID を配置するための STYLE_DASH スタイルを設定します。 操作のスタイルは異なる必要があります。

3 番目に、トレード方向を指定する必要があります。 例えば、買いに青、売りに赤を使用できます。 相場参入/決済に関連しないアクションは、3番目の色 (たとえば、グレー) でマークされます。 たとえば、通知または保留中のオーダーを配置することがあります。 後者の場合は、"ニュートラル" カテゴリとして分類されるため、異なる指示待ちのオーダーのペアを設定することがよくあります。

4番目は、保留中のオーダーの種類を定義する必要があります。 現在の相場価格とラインカラーを基準としたオーダー明細行の相互配置によって行うことができます。 例えば、青いラインの上は買いのストップを意味し、その価格の下の青いラインは買いリミットを表します。

5 番目に、ほとんどの操作に対して追加のデータと属性が必要です。 特に、アラート (または複数) がトリガーされた場合、ユーザーは意味のあるメッセージを受信する可能性が高くなります。 まだ使用されていない説明フィールドは、適しています。 このフィールドでは、オーダーのロットサイズとテイクプロフィットを設定します。 EAの必要なオブジェクトの設定の利便性と最小化のデフォルト値でインプットパラメータを提供するため、すべてオプションです。 オブジェクトの設定に加えて、デフォルト値には、ストップロスと利益値の両方が含まれます。

特定のストップロスとテイクプロフィットが各ラインに設定されている場合は、複数ラインを組み合わせたオブジェクトを使用します。 たとえば、等距離チャネルには2つの線があります。 2つの行を通過する最初の1つは、トレードシグナルを形成します。 一方、平行線は (3 番目のポイントと) 損失をストップし、利益を取るために設定します。 扱っているレベルはラインの相互整理で、そして色によって定義し易い。 たとえば、メインの1つ上にある追加のラインは、赤いチャネルのストップロスを形成します。 もし低ければ、テイクプロフィットとして扱われるでしょう。

ストップロスの両方を設定して利益を得る場合、オブジェクトは少なくとも3ラインで構成する必要があります。 たとえば、Fibo レベルのグリッドは、適しています。 デフォルトでは、プロジェクトは標準レベル 38.2%(ストップロス)、61.8% (相場参入ポイント)、161.8%(テイクプロフィット) を適用します。 この記事では、この他にもより複雑なオブジェクトタイプをより柔軟に構成することにこだわることはありません。

価格のクロスの結果としてオブジェクトのいずれかをアクティブにすると、オブジェクトはアクティブとしてマークされます。 たとえば、OBJPROP_BACK "background" 属性を割り当てることによって行うことができます。 このようなオブジェクトの元の色の明るさは、ユーザーからの視覚的フィードバックに減少します。 たとえば、処理後に青色の線が濃い青色になります。

しかし、トレーダーのマークアップは、多くの場合、"強力な" ラインとレベルは、イベントが関連する-ロールバックの上昇率の補正中にサポートラインから-何度も発生する可能性があります。

ラインの厚みの助けを借りて、このような状況を考えてみましょう。 周知のように、メタトレーダーのスタイルは、1から5までの幅を設定することができます。 ラインをアクティブにするとき、その厚さを見て、1を超える場合、後続の処理から除外するのではなく、厚さを減少させます。 たとえば、チャート上で予想される複数のイベントを5までの繰り返し数で指定できます。

価格は一定の値の周りに変動するトレンドがあり、任意のラインは、短い期間にわたって何度もクロスさせることができます。 裁量トレーダーは、視力によって価格偏差のダイナミクスを分析し、すべての価格ノイズをソートします。 EA は、自動的に同じメカニズムを実装する必要があります。

この目的のため, クロスの "ホット領域" のサイズを決定するエントリーを紹介します。 すなわち、価格の動きの最小範囲とその期間,です。 言い換えれば、"ラインクロス" イベントは、価格を越える直後に発生しませんが、ポイントで指定された距離に後退した場合にのみ発生します。

同様に、同じラインを持つ2つの連続したイベント間の最小間隔を指定するパラメータを導入します (1 より大きい太さのラインのみ)。 ここで、新しいタスクが発生します。どこかのラインで、以前のイベントの時間を格納する必要があります。 OBJPROP_ZORDER オブジェクトプロパティを使用してみましょう。 これはlong 型の数値であり、datetime 値は完全にそこに配置されます。 ラインを表示するオーダーの変更は、チャートの視覚的な表現にほとんど影響しません。

システムを操作するためのラインを構成するには、次の操作を実行するだけで十分です。

  • オブジェクトのプロパティ] ダイアログボックスを開きます。
  • 選択したプレフィックスを名前に追加します。
  • 説明にパラメータを設定します。
    • 相場のロットおよび未決のオーダーのライン、部分的なオーダーの決済と同様です。
    • 保留中のオーダーのアクティブ化ラインの保留中のオーダーラインの名前
    • テイクプロフィットライン用の保留中のオーダーの失効ライン。
  • インジケーターとしての色 (デフォルト値は青色-買い、赤ー売り、灰色-ニュートラル);
  • 操作セレクタとしてのスタイル (アラート、相場参入、保留中のオーダーの配置、ポジションのクローズ);
  • イベントの繰り返しインジケーターとしての幅。

ロット0.02 および有効期限24バー (時間) を使用して、購い制限オーダー (青色ダッシュ) の水平線プロパティを構成します。

ロット0.02 および有効期限24バー (時間) を使用して、購い制限オーダー (青色ダッシュ) の水平線プロパティを構成します。

システムによって管理されているオーダーの一覧 (説明した要件を満たす) は、[詳細]-[オブジェクトの種類]、[説明]、および [状態] と共に、チャートのコメントに示されます。


実行メカニズムの開発

最初に、オブジェクト名の接頭辞、プロセスの色とスタイル、デフォルト値、および要件と一般を考慮したチャートイベントを生成する領域のサイズを渡すために使用されるエントリーから EA の実装を始めましょう。

input int Magic = 0;
input double Lot = 0.01 /*default lot*/;
input int Deviation = 10 /*tolerance to price changes during order execution*/;

input int DefaultTakeProfit = 0 /*points*/;
input int DefaultStopLoss = 0 /*points*/;
input int DefaultExpiration = 0 /*bars*/;

input string CommonPrefit = "exp" /*empty to handle all compatible objects*/;

input color BuyColor = clrBlue /*market and pending buy orders - open, close, sl, tp*/;
input color SellColor = clrRed /*market and pending sell orders - open, close, sl, tp*/;
input color ActivationColor = clrGray /*activation of pending orders placement, alert*/;

input ENUM_LINE_STYLE InstantType = STYLE_SOLID /*opens market trades*/;
input ENUM_LINE_STYLE PendingType = STYLE_DASH /*defines probable pending orders (requires activation)*/;
input ENUM_LINE_STYLE CloseStopLossTakeProfitType = STYLE_DOT /*applied to open positions*/;

input int EventHotSpot = 10 /*points*/;
input int EventTimeSpan = 10 /*seconds*/;
input int EventInterval = 10 /*bars*/;

EA 自体は TradeObjects クラス (TradeObjects mq4 および mq5) として実装されています。 その唯一のパブリック要素は、コンストラクタ、デストラクタ、および標準イベントの処理メソッドです。

class TradeObjects
{
  private:
    Expert *e;

  public:
    void handleInit()
    {
      detectLines();
    }
    
    void handleTick()
    {
      #ifdef__MQL4__  
      if(MQLInfoInteger(MQL_TESTER))
      {
        static datetime lastTick = 0;
        if(TimeCurrent() != lastTick)
        {
          handleTimer();
          lastTick = TimeCurrent();
        }
      }
      #endif
    
      e.trailStops();
    }
    
    void handleTimer()
    {
      static int counter = 0;
      
      detectLines();
      
      counter++;
      if(counter == EventTimeSpan) // EventTimeSpan のBidのヒストリー記録があるまで待つ
      {
        counter = 0;
        if(PreviousBid > 0) processLines();
        if(PreviousBid != Bid) PreviousBid = Bid;
      }
    }
    
    void handleChart(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
      if(id == CHARTEVENT_OBJECT_CREATE || id == CHARTEVENT_OBJECT_CHANGE)
      {
        if(checkObjectCompliance(sparam))
        {
          if(attachObject(sparam))
          {
            display();
            describe(sparam);
          }
        }
        else
        {
          detectLines();
        }
      }
      else if(id == CHARTEVENT_OBJECT_DELETE)
      {
        if(removeObject(sparam))
        {
          display();
          Print("Line deleted: ", sparam);
        }
      }
    }
    
    TradeObjects()
    {
      e = new Expert(Magic, Lot, Deviation);
    }
    
    ~TradeObjects()
    {
      delete e;
    }
};

クラスは静的に作成され、そのイベントハンドラーは適切なグローバル関数にバインドされます。

TradeObjects to;

void OnInit()
{
  ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
  EventSetTimer(1);
  to.handleInit();
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
  to.handleChart(id, lparam, dparam, sparam);
}
                 
void OnTimer()
{
  to.handleTimer();
}

void OnTick()
{
  to.handleTick();
}

すべてのトレードオペレーションは、外部エキスパートクラス (Expert01.mqh) に隠された別のエンジンに割り当てられます。 コンストラクターでそのインスタンスを作成し、TradeObjects クラスのデストラクターで削除します。 詳細については後述します。 TradeObjects は、多くの操作を委任します。

すべての handleInit、handleTick、handleTimer、handleChart イベントハンドラは、記述すべき detectLines メソッドを呼び出します。 このメソッドでは、オブジェクトが分析され、要件を満たすものが選択されます。 ユーザーは、EA の実行時にオブジェクトを作成、削除、変更することができます。 見つかったオブジェクトは、内部配列に保存されます。 その存在は、チャートの状態の変化を監視し、新しいオブジェクトの発見と古いもの除去をユーザーに通知することができます。

processLines メソッドは、タイマーによって定期的に呼び出されます。 ループ内の配列によってイベントの発生をチェックし、適切なアクションを実行します。

ご覧の通り、このメソッドは、オブジェクトの接頭辞だけでなく、そのスタイル、色、ステータス checkObjectCompliance メソッドを使用してチェックします(下記参照)。 適切なオブジェクトは、attachObject 関数を使用して内部配列に追加されますが、チャートから削除されたものは removeObject 関数を使用して配列から削除します。 オブジェクトのリストは、' display ' メソッドを使用して、チャートコメントとして表示されます。

この配列は、オブジェクト名とステータスを含むシンプルな構造体で構成されます。

  private:
    struct LineObject
    {
      string name;
      int status;
      void operator=(const LineObject &o)
      {
        name = o.name;
        status = o.status;
      }
    };
    
    LineObject objects[];

このステータスは、attachObject 関数を使用して新しいオブジェクトを配列に追加した直後など、既存のオブジェクトをマークするために主に使用されます。

  protected:
    bool attachObject(const string name)
    {
      bool found = false;
      int n = ArraySize(objects);
      for(int i = 0; i < n; i++)
      {
        if(objects[i].name == name)
        {
          objects[i].status = 1;
          found = true;
          break;
        }
      }
      
      if(!found)
      {
        ArrayResize(objects, n + 1);
        objects[n].name = name;
        objects[n].status = 1;
        return true;
      }
      
      return false;
    }

detectLines メソッドは、後続の時間に各オブジェクトの存在をチェックします。

    bool detectLines ()
    {
      startRefresh();
      int n = ObjectsTotal(ChartID(), 0);
      int count = 0;
      for(int i = 0; i < n; i++)
      {
        string obj = ObjectName(ChartID(), i, 0);
        if(checkObjectCompliance(obj))
        {
          if(attachObject(obj))
          {
            describe(obj);
            count++;
          }
        }
      }
      if(count > 0) Print("New lines: ", count);
      bool changes = stopRefresh() || (count > 0);
      
      if(changes)
      {
        display();
      }
      
      return changes;
    }

ここでは、startRefresh 補助関数が最初に呼び出されます。 その目的は、すべての array オブジェクトのステータスフラグを0にリセットすることです。 その後、タスクオブジェクトは attachObject を使用してループ内で再び1の状態を受け取ります。 stopRefresh の呼び出しは、最後に実行されます。 内部配列内の未使用のオブジェクトをゼロステータスで検索し、そのユーザーに通知します。

各オブジェクトは、checkObjectCompliance メソッドの要件に準拠しているかどうかをチェックします。

    bool checkObjectCompliance(const string obj)
    {
      if(CommonPrefit == "" || StringFind(obj, CommonPrefit) == 0)
      {
        if(_ln[ObjectGetInteger(0, obj, OBJPROP_TYPE)]
        & & _st [ObjectGetInteger(0, obj, OBJPROP_STYLE)]
        && _cc[(color)ObjectGetInteger(0, obj, OBJPROP_COLOR)])
        {
          return true;
        }
      }
      return false;
    }

名前プレフィックスとは別に、型、スタイル、およびオブジェクトの色を持つフラグセットもチェックされます。 Set 補助クラスは、次のように使用されます。

#include<Set.mqh>

Set<ENUM_OBJECT> _ln(OBJ_HLINE, OBJ_VLINE, OBJ_TREND, OBJ_CHANNEL, OBJ_FIBO);
Set<ENUM_LINE_STYLE> _st(InstantType, PendingType, CloseStopLossTakeProfitType);
Set<color> _cc(BuyColor, SellColor, ActivationColor);

main メソッド (processLines) について説明する時間があります。 メソッドのロールはサイズに影響します。 コード全体が添付ファイルに表示されます。 ここでは、最も明らかにフラグメントを表示します。

    void processLines()
    {
      int n = ArraySize(objects);
      for(int i = 0; i < n; i++)
      {
        string name = objects[i].name;
        if(ObjectGetInteger(ChartID(), name, OBJPROP_BACK)) continue;
        
        int style = (int)ObjectGetInteger(0, name, OBJPROP_STYLE);
        color clr = (color)ObjectGetInteger(0, name, OBJPROP_COLOR);
        string text = ObjectGetString(0, name, OBJPROP_TEXT);
        datetime last = (datetime)ObjectGetInteger(0, name, OBJPROP_ZORDER);
    
        double aux = 0, auxf = 0;
        double price = getCurrentPrice(name, aux, auxf);
        ...

ループ内では、既に使用されているもの (OBJPROP_BACK フラグを持つ) を除くすべてのオブジェクトに沿って渡します。 以下に示す getCurrentPrice 関数を使用すると、現在のオブジェクトの価格を調べることができます。 オブジェクトタイプは複数のラインで構成されるため、2つのパラメータを使用して追加の価格値を渡す必要があります。

        if(clr == ActivationColor)
        {
          if(style == InstantType)
          {
            if(checkActivation(price))
            {
              disableLine(i);
              if(StringFind(text, "Alert:") == 0) Alert(StringSubstr(text, 6));
              else if(StringFind(text, "Push:") == 0) SendNotification(StringSubstr(text, 5));
              else if(StringFind(text, "Mail:") == 0) SendMail("TradeObjects", StringSubstr(text, 5));
              else Print(text);
            }
          }

次に、オブジェクトスタイルをチェックして、イベントのタイプを決定し、その価格が0番目の足のBid価格とどのように関連付けられるかを確認する必要があります。 checkActivation 関数は、アラートの場合には、保留中のオーダーを配置します。 アクティベーションが発生した場合は、適切なアクション (アラートの場合は、メッセージを表示するか、ユーザーに送信する) を実行し、disableLine を使用してオブジェクトを無効としてマークします。

トレードオペレーションの活性化コードは、もちろん、より複雑になります。 相場価格とオープンショートポジションの簡略化されたオプションの例です:

        else if(clr == BuyColor)
        {
          if(style == InstantType)
          {
            int dir = checkMarket(price, last);
            if((dir == 0) && checkTime(name))
            {
              if(clr == BuyColor) dir = +1;
              else if(clr == SellColor) dir = -1;
            }
            if(dir > 0)
            {
              double lot = StringToDouble(ObjectGetString(0, name, OBJPROP_TEXT)); //ロット [%]
              if(lot == 0) lot = Lot;
    
              double sl = 0.0, tp = 0.0;
              if(aux != 0)
              {
                if(aux > Ask)
                {
                  tp = aux;
                  if(DefaultStopLoss != 0) sl = Bid - e.getPointsForLotAndRisk(DefaultStopLoss, lot) * _Point;
                }
                else
                {
                  sl = aux;
                  if(DefaultTakeProfit != 0) tp = Bid + e.getPointsForLotAndRisk(DefaultTakeProfit, lot) * _Point;
                }
              }
              else
              {
                if(DefaultStopLoss != 0) sl = Bid - e.getPointsForLotAndRisk(DefaultStopLoss, lot) * _Point;
                if(DefaultTakeProfit != 0) tp = Bid + e.getPointsForLotAndRisk(DefaultTakeProfit, lot) * _Point;
              }
              
              sl = NormalizeDouble(sl, _Digits);
              tp = NormalizeDouble(tp, _Digits);
            
              int ticket = e.placeMarketOrder(OP_BUY, lot, sl, tp);
              if(ticket != -1) //成功
              {
                disableLine(i);
              }
              else
              {
                showMessage("Market buy failed with '" + name + "'");
              }
            }
          }
          else if(style == CloseStopLossTakeProfitType) //売りポジションの決済、
          {
            int dir = checkMarket(price) || checkTime(name);
            if(dir != 0)
            {
              double lot = StringToDouble(ObjectGetString(0, name, OBJPROP_TEXT)); //ロット
              if(lot > 0)
              {
                if(e.placeMarketOrder(OP_BUY, lot) != -1) //OrderCloseBy () がトリガされます。
                {
                  disableLine(i);
                }
                else
                {
                  showMessage("Partial sell close failed with '" + name + "'");
                }
              }
              else
              {
                if(e.closeMarketOrders(e.mask(OP_SELL)) > 0)
                {
                  disableLine(i);
                }
                else
                {
                  showMessage("Complete sell close failed with '" + name + "'");
                }
              }
            }
          }

checkMarket 関数 (より複雑なバージョンの checkActivation) は、イベント発生をチェックします (両方とも後述します)。 イベントがトリガされると、ストップロスを取るか、オブジェクトのプロパティから利益とロットレベルを取得し、その後オーダーを開きます。

このロットサイズは、フリーマージンのパーセンテージで指定されます。 このような表記の意味は、実際には、資金のどの部分を新しいオーダーのセキュリティとして使用されることを示していることを想像する場合は覚えやすいです。

checkActivation と checkMarket 関数は似ています。 どちらも、イベントアクティベーション領域のサイズを定義する EA エントリーを使用します。

    bool checkActivation(const double price)
    {
      if(Bid >= price - EventHotSpot * _Point && Bid <= price + EventHotSpot * _Point)
      {
        return true;
      }
      
      if((PreviousBid < price && Bid >= price)
      || (PreviousBid > price && Bid <= price))
      {
        return true;
      }
      return false;
    }
    
    int checkMarket(const double price, const datetime last = 0) //価格移動の方向を返します
    {
      if(last != 0 && (TimeCurrent() - last) / PeriodSeconds() < EventInterval)
      {
        return 0;
      }
    
      if(PreviousBid >= price - EventHotSpot * _Point && PreviousBid <= price + EventHotSpot * _Point)
      {
        if(Bid > price + EventHotSpot * _Point)
        {
          return +1; // up
        }
        else if(Bid < price - EventHotSpot * _Point)
        {
          return -1; // down
        }
      }
    
      if(PreviousBid < price && Bid >= price && MathAbs(Bid - PreviousBid) >= EventHotSpot * _Point)
      {
        return +1;
      }
      else if(PreviousBid > price && Bid <= price && MathAbs(Bid - PreviousBid) >= EventHotSpot * _Point)
      {
        return -1;
      }
      
      return 0;
    }

覚えているかもしれませんが、PreviousBid の価格は EventTimeSpan 秒の周期性を持つ handleTimer のハンドラで EA によって保存されます。 この関数の演算の結果は、checkActivation が論理フラグを返す一方で、checkMarket が価格移動方向である場合は、0番目の足のオブジェクトのBid価格を越える価格のフラグです。

クオートによるオブジェクトのクロスは、適用された価格を含むBid価格でチャート全体が構築されるため、Bid価格によって検出されます。 トレーダーは、買いオーダーのマークアップを形成している場合でも、正しいシグナルによってトリガされます。ask のチャートに基づいている可能性がある潜在的なラインは、スプレッドの値によって現在のマークアップを使用します。

PendingType およびActivationColor のラインは特別です。 価格によるクロスの時に、未決のオーダーが置かれます。 オーダーの配置は、他のラインを使用して設定されます。 名前は、アクティベーションラインの説明でスラッシュ ('/') で区切られます。 説明が空の場合、システムはすべての保留中のオーダー明細ラインをスタイル別に検索し、配置します。 相場のオーダーと同様に、保留中のオーダーの方向は、その色に対応しています-BuyColor または SellColor。ロットと期限 (足で) を指定することができます。

スタイルとオブジェクトの色と対応する値を組み合わせるためのメソッドは、テーブルに与えられます。

色とスタイル BuyColor SellColor ActivationColor
InstantType buying by market selling by market alert
PendingType potential pending buy order potential pending sell order initiate placing pending orders
CloseStopLossTakeProfitType closing, stop loss, take profit
for a short position
closing, stop loss, take profit
for a long position
close all

最も重要なものである getCurrentPrice メソッドに戻りましょう。

    double getCurrentPrice(const string name, double &auxiliary, double &auxiliaryFibo)
    {
      int type = (int)ObjectGetInteger(0, name, OBJPROP_TYPE);
      if(type == OBJ_TREND)
      {
        datetime dt1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
        datetime dt2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1);
        int i1 = iBarShift(NULL, 0, dt1, true);
        int i2 = iBarShift(NULL, 0, dt2, true);
        if(i1 <= i2 || i1 == -1 || i2 == -1)
        {
          Print("Incorrect line: ", name);
          return 0;
        }
        double p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
        double p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);
        
        double k = -(p1 - p2)/(i2 - i1);
        double b = -(i1 * p2 - i2 * p1)/(i2 - i1);
        
        return b;
      }
      else if(type == OBJ_HLINE)
      {
        return ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
      }
      else if(type == OBJ_VLINE)
      {
        return EMPTY_VALUE; //null ではなく、以外の場合は使用しない
      }
      else if(type == OBJ_CHANNEL)
      {
        datetime dt1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
        datetime dt2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1);
        datetime dt3 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 2);
        int i1 = iBarShift(NULL, 0, dt1, true);
        int i2 = iBarShift(NULL, 0, dt2, true);
        int i3 = iBarShift(NULL, 0, dt3, true);
        if(i1 <= i2 || i1 == -1 || i2 == -1 || i3 == -1)
        {
          Print("Incorrect channel: ", name);
          return 0;
        }
        double p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
        double p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);
        double p3 = ObjectGetDouble(0, name, OBJPROP_PRICE, 2);
        
        double k = -(p1 - p2)/(i2 - i1);
        double b = -(i1 * p2 - i2 * p1)/(i2 - i1);
        
        double dy = i3 * k + b - p3;
        
        auxiliary = p3 - i3 * k;
        
        return b;
      }
      else if(type == OBJ_FIBO)
      {
        //レベル61.8 は、パーセントフィボナッチリトレースメント (買い/売り制限) でエントリー
        // 38.2 and 161.8 as stoploss/takeprofit
        
        double p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
        double p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);
        datetime dt1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
        datetime dt2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1);
        
        if(dt2 < dt1)
        {
          swap(p1, p2);
        }
        
        double price = (p2 - p1) * ObjectGetDouble(0, name, OBJPROP_LEVELVALUE, 4) + p1;
        auxiliary = (p2 - p1) * ObjectGetDouble(0, name, OBJPROP_LEVELVALUE, 2) + p1;
        auxiliaryFibo = (p2 - p1) * ObjectGetDouble(0, name, OBJPROP_LEVELVALUE, 6) + p1;
        return price;
      }
      return 0;
    }

このアイデアはシンプルです。オブジェクトの種類に応じて、 (メインと追加のラインの) 0 番目の足でその価格を計算する必要があります。 オブジェクトをチャートに配置する場合、すべてのオブジェクトポイントが過去に存在することが重要です。 それ以外の場合は、その価格を明確に計算することは不可能であるため、オブジェクトは無効と見なされます。

垂直線の場合、 EMPTY_VALUE を返します。-ゼロでも、特定の価格 でもありません。(そのようなラインは、任意の価格を満たしているため) したがって、垂直線の場合は、現在の時刻に一致するための追加のチェックを使用する必要があります。 checkTime 関数によって実行されます。 processLines フラグメントで既に呼び出されています。

    bool checkTime(const string name)
    {
      return (ObjectGetInteger(0, name, OBJPROP_TYPE) == OBJ_VLINE
        && (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0) == Time[0]);
    }

最後に、コードで既に何度も遭遇した disableLine 関数の実装について説明しましょう。

    void disableLine(const string name)
    {
      int width = (int)ObjectGetInteger(0, name, OBJPROP_WIDTH);
      if(width > 1)
      {
        ObjectSetInteger(0, name, OBJPROP_WIDTH, width - 1);
        ObjectSetInteger(0, name, OBJPROP_ZORDER, TimeCurrent());
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_BACK, true);
        ObjectSetInteger(0, name, OBJPROP_COLOR, darken((color)ObjectGetInteger(0, name, OBJPROP_COLOR)));
      }
      display();
    }

ラインの幅が1を超える場合は、1ずつ増やし、現在のイベント時刻を OBJPROP_ZORDER プロパティに保存します。 普通の線の場合は、背景にずらして色の明るさを下げます。 バックグラウンドのオブジェクトは無効と見なされます。

OBJPROP_ZORDER プロパティについては、processLines メソッドで ' datetime last ' 変数 (上に示すように) に読み込まれ、 checkMarket (price、last) メソッドに引数として渡されます。 内部で、前のアクティベーション以降の時間が、エントリー変数 (bar) で設定されている間隔を超えていることを確認します。

      if(last != 0 && (TimeCurrent() - last) / PeriodSeconds() < EventInterval)
      {
        return 0;
      }

TradeObjects を使用すると、CloseStopLossTakeProfitType タイプのオブジェクトの説明でロットが指定されている場合に、部分的なクローズを実行できます。 指定されたボリュームのカウンタオーダーが開き、OrderCloseBy が呼び出されます。 このモードを有効にするには、エントリー変数に特別な AllowOrderCloseBy フラグがあります。 ONの場合は、カウンターのポジションは常に1つになります。 ご存じのように、この関数はすべてのアカウントでは許可されていません (EAの設定をチェックし、オプションがブロックされている場合は適切なメッセージを送信します)。 MetaTrader5の場合、アカウントはヘッジをサポートする必要があります。 OrderCloseBy を使用せずに、ポジションのリストを表示し、さまざまな属性によって減らすことができる特定のものを選択する能力を持つ代替部分的な決済を実装することによって、システムを改善することができます。

TradeObjects のすべてのトレードオペレーションを実行するエキスパートクラスに戻りましょう。 指定されたリスクに基づいてストップロスをサポートし、ロットを計算するためのシンプルなメソッドセットです。 メタトレーダーは、 MetaTrader4Ordersライブラリを使用してMetaTrader5に適合した4つのオーダーを適用します。

このクラスには、配置された保留中のオーダーを変更する機能はありません。 その価格を移動するだけでなく、ストップロスやテイクプロフィットレベルがターミナルによって管理されます。 "トレードレベルを表示" オプションが有効になっている場合、ドロップを使用してを行うことができます。

エキスパートクラスは、慣れている他のと交換することができます。

添付されたソースコードは、MetaTrader4とメタトレーダー 5 (追加のヘッダーファイル) の両方でコンパイルされています。

現在の TradeObjects では、簡潔にするために、厳密な OOP からの逸脱があります。 抽象的なトレーディングインターフェイスを適用し、インターフェイスとしてエキスパートクラスを実装し、TradeObjects クラス (たとえば、コンストラクタのパラメータを介して) に渡す必要があります。 よく知られている依存関係の挿入OOP テンプレートです。 このプロジェクトでは、トレーディングエンジンはコードに配置されています。TradeObjects オブジェクト内で作成および削除されます。

さらに、OOP の原則に反して、TradeObjects クラスのコードでグローバルエントリー変数を直接使用します。 最適なプログラミングスタイルは、コンストラクターまたは特殊メソッドのパラメータとしてクラスに渡す必要があります。 たとえば、TradeObjects を別の EA 内のライブラリとして使用して、マークアップによる裁量トレードの関数を補完することができます。

プロジェクトが大きければ大きいほど、基本的な OOP 原則が重要になります。 オブジェクトによる自動トレードのむしろシンプルで孤立したエンジンを検討するので、(制限を知っている) その改善は、オプションの研究に残されています。


アクションプログラム

以下に、システムがデフォルトのスタイルと色でアクションでどのように見えるかを示します。

ヘッドアンドショルダーのパターンを検出したと仮定し、売りオーダーを配置する予定です。 制御されたオブジェクトのリストがコメントで表示されます。


ストップロスは、DefaultStopLoss EA パラメータに従って設定されます。 相場のライフタイムは、テイクプロフィットではなく、垂直な青色の点線で制限されています。


このラインに到達すると、ポジションは (収益性に関係なく) 閉じられます。 アクティブなラインは非アクティブとしてマークされます (色の明るさは減少し、背景に移動されます)。


しばらくすると、クオートが再び下に移動するように見え、61.8 のレベルからのブレイクアウトで Fibo レベルを設定します。 (すべてのフィボナッチは、デフォルトでは、このプロジェクトで行うことができますが、他のタイプを実装することができます)。 Fibo オブジェクトの色は、レベルではなく対角線の色であることに注意してください。レベルの色は別の設定によって設定されます。


価格がこのレベルに達すると、指定されたストップロス (38.2) と利確ライン (161.8、スクリーンショットには表示されません) が開きます。


一定時間後に、上からのレジスタンスの形成があり、価格がまだ上がると仮定して青のチャネルを配置します。


これまでのすべてのラインには説明が含まれていなかったため、ロットパラメータ (デフォルトでは 0.01) からロットを使用してオーダーが開かれたことに注意してください。 この場合、'-1 ' の記述があり、すなわちロットサイズはフリーマージンの 1% が計算されます。 補助線はメインの1つ下にあるため、チャネルはストップロス (デフォルト値とは異なる) までの距離を指定します。


チャネルが中断され、新しいロングポジションが開かれます。 ご覧の通り、ボリュームは0.04 として計算されました。 ($1000 の証拠金)。 スクリーンショットの青色のセグメントは、アクティブなチャネルがバックグラウンドに移動されます (MetaTrader4は、バックグラウンドでチャネルを示しています)。

両方のオープンな買いのポジションを閉めるためには、赤い点線に利確ラインを置きます。


価格はこのレベルに達し、両方のオーダーがクローズされます。


そのような動きの後で、価格は「通路」の内で動くことを仮定します。 このボラティリティをキャッチするために、灰色の破線と同様に、価格の上下の指値オーダーを2つの水平破線に立てます。 実際には、水平または垂直である必要はありません。


たとえば、0.02 のカスタムロットと 24 bar (時間) のテイクプロフィットが、下位の保留中のオーダーに対して指定されていることに注意してください。 アクティベーションラインに到達すると、保留中のオーダーがセットされます。


しばらくすると、売り制限がトリガされます。


買いリミットは月曜日にテイクプロフィットが切れます。


すべてのポジションの決済として、垂直の灰色の点線を置きます。


到達した後、ショートポジションが閉じていますが、これはロングでも関係ありません。


EA はそのタスク中に、ログ内のメインイベントを表示します。

2017.07.06 02:00:00  TradeObjects EURUSD,H1: New line added: 'exp Channel 42597 break up' OBJ_CHANNEL buy -1
2017.07.06 02:00:00  TradeObjects EURUSD,H1: New lines: 1
2017.07.06 10:05:27  TradeObjects EURUSD,H1: Activated: exp Channel 42597 break up
2017.07.06 10:05:27  TradeObjects EURUSD,H1: open #3 buy 0.04 EURUSD at 1.13478 sl: 1.12908 ok
...
2017.07.06 19:02:18  TradeObjects EURUSD,H1: Activated: exp Horizontal Line 43116 takeprofit
2017.07.06 19:02:18  TradeObjects EURUSD,H1: close #3 buy 0.04 EURUSD at 1.13478 sl: 1.13514 at price 1.14093
2017.07.06 19:02:18  TradeObjects EURUSD,H1: close #2 buy 0.01 EURUSD at 1.13414 sl: 1.13514 tp: 1.16143 at price 1.14093
...
2017.07.07 05:00:09  TradeObjects EURUSD,H1: Activated: exp Vertical Line 42648
2017.07.07 05:00:09  TradeObjects EURUSD,H1: open #4 sell limit 0.01 EURUSD at 1.14361 sl: 1.15395 ok
2017.07.07 05:00:09  TradeObjects EURUSD,H1: #4 2017.07.07 05:00:09 sell limit 0.01 EURUSD 1.14361 1.15395 0.00000 0.00000 0.00 0.00 0.00  0 expiration 2017.07.08 05:00
2017.07.07 05:00:09  TradeObjects EURUSD,H1: open #5 buy limit 0.02 EURUSD at 1.13731 sl: 1.13214 ok
2017.07.07 05:00:09  TradeObjects EURUSD,H1: #5 2017.07.07 05:00:09 buy limit 0.02 EURUSD 1.13731 1.13214 0.00000 0.00000 0.00 0.00 0.00  0 expiration 2017.07.08 05:00

もちろんオブジェクトを削除することも可能です。 この例では、実行されたアクションのプロトコルとして残されています。

MetaTrader4とMetaTrader5のテンプレートは、 2017年7月から始まる EURUSD H1 のデモトレードは、 この記事に添付されています. DefaultStopLoss パラメータが-1 (フリーマージンの 1% を失うことに相当) に設定されている場合を除き、標準の EA 設定を適用します。 $1000 のデポジットと1:500 のレバレッジは、ストップロスの計算とトラッキングをするためにあります。 MetaTrader5の場合、最初にテンプレートをtester.tplに変更する必要があります。 (テスターで直接テンプレートを読み込んで編集することは、プラットフォームではまだサポートされていません)。


結論

この記事は、トレーダーによってチャート上に配置された標準的なオブジェクトを使用して、半自動トレードをするための効率的な方法を提供しています。 トレンド、水平線および垂直線, Fibo チャンネルとグリッドにより、このシステムは相場にオーダー、予約注文を出すことができ、トレーダーに特定のパターンの通知を行うことができます。 オープンソースコードを使用すると、ユーザーはサポートされているオブジェクトタイプのセットを拡張し、トレーディング関数を向上させることができます。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/3442

添付されたファイル |
MT4.zip (11.97 KB)
MT5.zip (29.13 KB)
一連の取引に対するリスク評価 一連の取引に対するリスク評価
この記事では、トレードシステムの分析における確率と数学的統計理論のメソッドについて説明します。
クロスプラットフォームEA: ストップ クロスプラットフォームEA: ストップ
この記事では、2つのプラットフォームMetaTrader4とMetaTrader5との互換性を確保するために、EAのストップの実装について説明します。
ディープニューラルネットワーク(その4)ニューラルネットワークモデルの作成、訓練、テスト ディープニューラルネットワーク(その4)ニューラルネットワークモデルの作成、訓練、テスト
本稿では、darchパッケージ(v.0.12.0)の新しい機能について考察し、異なるデータタイプ、構造及び訓練シーケンスを有するディープニューラルネットワーク訓練を説明します。訓練結果も含まれています。
ディープニューラルネットワーク(その3)サンプル選択と次元削減 ディープニューラルネットワーク(その3)サンプル選択と次元削減
本稿は、ディープニューラルネットワークに関する一連の記事の続きです。ここでは、ニューラルネットワークの訓練データの準備に当たってのサンプルの選択(ノイズ除去)、入力データの次元数の削減、及びデータセットの訓練/検証/テストセットへの分割を検討します。