English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
MetaTrader 4へのシグナル提供者としてのMetaTrader 5利用

MetaTrader 4へのシグナル提供者としてのMetaTrader 5利用

MetaTrader 5 | 28 10月 2015, 08:40
570 0
Karlis Balcers
Karlis Balcers

はじめに

本稿を執筆し、それを行うのが可能であるか調べようと思った理由は複数あります。

まず MetaTrader 5 が発表され利用可能となって長い時間が経過していますが皆お気に入りのブローカーが実際にトレードさせてくれるのをいまだに待ち続けているのです。MQL5を使用して戦略を作成して、よい成績を出し、今実アカウントでそれを実行したいと思っている人がいます。またトレーディングが行われている方法を好み、MetaTrader 4の代わりにMetaTrader 5を使ってマニュアルでトレードしたいと思う人もいるかもしれません。

第二に、自動売買チャンピオンシップ の間、皆自身の実アカウントでリーダーをフォローしたいと考えています。トレードをフォローする独自の方法を作り上げた人もいれば、まだそれをどうやって実現するか、チャンピオンシップに出場しているトレーダーにできるだけ近い結果を出すにはどんな方法があるか、同じ資金管理を適用するにはどうするのか探している人もいます。

第三に、よい戦略を持ち、自分だけでなく友人その他にトレーディングシグナルを提供したいと思う人もいます。そういう人達はパフォーマンスをキープしたまま複数の連携を受け入れリアルタイムでシグナルを配布する機能を必要としています。

これらは私の中にずっとあった疑問であり、私はこういった要件を満たす解決法を見つけたいと思います。


1. MQL5 チャンピオンシップのアクティビティをフォローするには?

最近、MQL5.communityで私の知識レベルにあり、自分にも構築できると思える記事を複数みつけました。またチャンピオンシップのホームページで行われているアクティビティをフォローし、実アカウント(運のよいことに収益を上げている)でトレーディングをするアプリケーションを使っているということもお伝えします。問題はデータが5分ごとにアップデートされ、オープンとクローズの正しいタイミングを逃す可能性があるということです。

チャンピオンシップフォーラムから同じことをしている人が他にもいることを知りました。そしてそれは効率的でなく、またチャンピオンシップのホームページに大きなトラフィックを起こし、主催者はそれを好まないだろうと 思われるのです。そこで解決法はあるのでしょうか?すべての解決法を見て、MetaTrader 5を使って「読み取り専用」(トレーディング無効)モードで出場者の各アカウントにアクセスする機能がよいのではないかと思いました。

リアルタイムですべてのトレード情報を入手し、それをリアルタイムで送信するのにそれを利用することは可能でしょうか?それをつきとめるため、私はExpert Advisor を作成し、「読み取り専用」モードアクセスのみのアカウントでを実行してみました。驚いたことにそれをアタッチし、また「ポジション」、「オーダー」、「ディール」に関する情報を取得できたのです。これらは可能性ある解決法へ導く扉なのです!


2. ポジション、オーダー、ディール-何をフォローすべきか?

MetaTrader 5 から MetaTrader 4に情報を転送しようとしたら、, MetaTrader 4で可能なすべての注文タイプを考慮する必要があります。また、フォローする場合、アカウントで行われるトレーディングに関するすべての処理について知りたいと思います。そうすると「ポジション」は毎チックまたは毎秒の「ポジション」状態を比較しない限り情報をすべて伝えてはくれません。

ということは「オーダー」または「ディール」を追うのがよいと思われます。

そこで「オーダー」から見ていきます。

オーダー

オーダーは「ディール」の前に実行されるのが好ましい点で、また指値(リミット)注文に関する情報も持ちますが、「ディール」と比べると、一つ重要なじことが抜けています。エントリータイプです(ENUM_DEAL_ENTRY)。

ディール

DEAL_ENTRY_TYPE は「オーダー」が並列計算を要求する間、トレーダーのアカウントで起こっていることを理解するのに役立ちます。一番良いのは「ディール」と「オーダー」の融合で、そうすると指値注文をし、かつトレードアカウントで起こっていることをすべて追いかけることができるでしょう。値動きは仲介会社間で異なるため、指値注文は実際誤りや不正な結果につながるかもしれません。

「ディール」だけを追った場合、指値注文はまだ実行しますが、小さな遅延(ネットワーク接続によります)を伴います。スピード(指値注文)とパフォーマンス(ディール)の間でパフォーマンス(「ディール」)を行うことを選択するのです。


3. 「シグナル」を提供する方法は?

これまで異なる記事が何件かあり、そこで MetaTrader 5 から他のアプリケーションやコンピュータへの通信とデータ転送をする方法について離されています。他のクライアントにわれわれと連結できるようになってもらいたいと思い、そういう人達はたいがい別のコンピュータにいるため、私は TCP ソケットを選択します。

MQL5 では API 関数によってそれを行うことができないため外部ライブラリを使う必要があります。"WinInet.dll" ライブラリに関する記事は複数あります(たとえば"Using WinInet.dll for Data Exchange between Terminals via the Internet" などです。)が、われわれのニーズを満たすものはひとつもありません。

私は少しばかり C#に詳しいので、自分自身のライブラリを作成することにしました。このために、記事 "Exposing C# code to MQL5 using unmanaged exports" を利用し互換性の問題を助けてもらいました。ひじょうにシンプルなインターフェースを持つサーバーと同時に500のクライアントまで受け入れる(お手持ちのパソコンに.NETフレームワーク3.5以降。すでにほとんどのコンピュータにインストールされている"Microsoft .NET Framework 3.5"が必要です)機能を作成しました。

#import "SocketServer.dll"    // Library created on C# (created by using information available on https://www.mql5.com/en/articles/249)
string About();            // Information about library.
int SendToAll(string msg);  // Sends one text message to all clients.
bool Stop();               // Stops the server.
bool StartListen(int port); // Starts the server. Server will listen from incomming connections (max 500 clients). 
                               // All clients are built on Assync threads.
string ReadLogLine();       // Retrieve one log line from server (can contain erros and other information).
                               // Reading is optional. Server stores only last 100 lines.
#import

サーバー自体は個別スレッド上の背景で走っています。そしていくつクライアントが連携していようと MetaTrader 5 またはご自身の戦略を遮断したり動作を遅くしたりすることはありません。

C# 言語ソースコード 

         internal static void WaitForClients()
        {
            if (server != null)
            {
                Debug("Cant start lisening! Server not disposed.");
                return;
            }
            try
            {

                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, iPort);
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                server.Bind(localEndPoint);
                server.Listen(500);
                isServerClosed = false;
                isServerClosedOrClosing = false;

                while (!isServerClosedOrClosing)
                {
                    allDone.Reset();
                    server.BeginAccept(new AsyncCallback(AcceptCallback), server);
                    allDone.WaitOne();
                }
                
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception e)
            {
                Debug("WaitForClients() Error: " + e.Message);
            }
            finally
            {
                if (server != null)
                {
                    server.Close();
                    server = null;
                }
                isServerClosed = true;
                isServerClosedOrClosing = true;
            }
        }

        internal static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                allDone.Set();
                if (isServerClosedOrClosing)
                    return;
                Socket listener = (Socket)ar.AsyncState;
                Socket client = listener.EndAccept(ar);

                if (clients != null)
                {
                    lock (clients)
                    {
                        Array.Resize(ref clients, clients.Length + 1);
                        clients[clients.Length - 1].socket = client;
                        clients[clients.Length - 1].ip = client.RemoteEndPoint.ToString();
                        clients[clients.Length - 1].alive = true;
                    }
                    Debug("Client connected: " + clients[clients.Length - 1].ip);
                }
            }
            catch (Exception ex)
            {
                Debug("AcceptCallback() Error: " + ex.Message);
            }
        }

C#言語での非同期サーバーソケットに関してもっと知りたい場合は、 Microsoft MSDN またはGoogleで見つけられる記事をいくつか読むことをお薦めします。


4. 「シグナル」を収集するには?

MetaTrader 4 では新規ティック生成時のみならず常に情報を受け取りたいと思うので、そのためにExpert Advisorではなく「スクリプト」を作成します。また、シグナル提供者であるMetaTrader 5とのソケット接続を開通できるようにする必要があります。

このために MQL4 のコードベースcodebase: "https://www.mql5.com/en/code/9296"の助けを借りることにします。そこに、ひじょうにシンプルな方法でソケットと連携できるようにするかなり優れたインクルードファイル(WinSock.mqh)を見つけました。その安定性について不満を述べる人がいたとしても、私の目的には十分良いと思い、検証中何も問題はありませんでした。

#include <winsock.mqh>  // Downloaded from MQ4 homepage
                     // DOWNLOAD:   http://codebase.mql4.com/download/18644
                     // ARTICLE:    http://codebase.mql4.com/6122


5. データ処理

われわれはコンセプトを持った今、必要なのはディールが確実に処理され、すべてのクライアントに対して理解でき、実行可能はフォーマットで1件ずつ転送することだけです。

5.1. サーバー側

明確にしたように、それは Expert Advisor ですが、それが追加される対象の通貨に関しては注意を払いません。

起動中にはまた入ってくる接続を待っているスレッドに耳を傾け始めます。

int OnInit()
  {
   string str="";
   Print(UTF8_to_ASCII(About()));
//--- start the server
   Print("Starting server on port ",InpPort,"...");
   if(!StartListen(InpPort))
     {
      PrintLogs();
      Print("OnInit() - FAILED");
      return -1;
     }

このバージョンでは Expert Advisor は接続しているクライアントを気に掛けることはありません。毎回トレードがあり、それは全クライアントに対して通知を送ります。たとえトレードがまったくなかったとしても同様です。われわれが知りたいのはトレードのことだけなので、関数 OnTrade() を使用してOnTick()を消去します。この関数では最新の履歴を見、それが情報を必要とするディールなのかどうか判断します。

このことをよりよく理解するためにコード内のコメントを見てください。

//+------------------------------------------------------------------+
//| OnTrade() - every time when there is an activity related to      |
//|             traiding.                                            |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- find all new deals and report them to all connected clients
//--- 24 hours back.
   datetime dtStart=TimeCurrent()-60*60*24;
//--- 24 hours front (in case if you live in GMT-<hours>)
   datetime dtEnd=TimeCurrent()+60*60*24;
//--- select history from last 24 hours.
   if(HistorySelect(dtStart,dtEnd))
     {
      //--- go through all deals (from oldest to newest).
      for(int i=0;i<HistoryDealsTotal();i++)
        {
         //--- get deal ticket.
         ulong ticket=HistoryDealGetTicket(i);
         //--- if this deal is interesting for us.
         if(HistoryDealGetInteger(ticket,DEAL_ENTRY)!=DEAL_ENTRY_STATE)
           {
            //Print("Entry type ok.");
            //--- check if this deal is newer than previously reported one.
            if(HistoryDealGetInteger(ticket,DEAL_TIME)>g_dtLastDealTime)
              {
               //--- if some part of position has been closed then check if we need to enable it
               if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT)
                 {
                  vUpdateEnabledSymbols();
                 }
               //--- if opposite position is opened, then we need to enable disabled symbol.
               else if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_INOUT)
                 {
                  //--- enable this specific symbol.
                  vEnableSymbol(HistoryDealGetString(ticket,DEAL_SYMBOL));
                 }
               //--- check if symbol is enabled.
               if(bIsThisSymbolEnabled(HistoryDealGetString(ticket,DEAL_SYMBOL)))
                 {
                  //--- build deal-string and send to all connected clients
                  int cnt=SendToAll(sBuildDealString(ticket));
                  //--- technical error with server.
                  if(cnt<0)
                    {
                     Print("Failed to send new deals!");
                    }
                  //--- if sent to no one (cnt==0) or if sent to someone (cnt>0)                  
                  else
                    {
                     //--- update datetime for last sucessfully transfered deal
                     g_dtLastDealTime=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
                    }
                 }
               //--- do not notify becayse symbol is disabled.
               else
                 {
                  //--- update datetime for last deal, we will not notify about.
                  g_dtLastDealTime=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
                 }
              }
           }
        }
     }
  }

おわかりのように、新規ディールをみつけるとデータ転送準備のために関数 BuildDealString() を呼びます。すべてのデータはテキスト形式で転送され、各ディールは '<' で始まり '>'で終わっています。

これはTCP/IPプロトコルのためある時点で2件以上のディールを受け取る可能性があるため、複数ディールを区別するのに役立ちます。

//+------------------------------------------------------------------+
//| This function builds deal string                                 |
//| Examples:                                                        |
//| EURUSD;BUY;IN;0.01;1.37294                                       |
//| EURUSD;SELL;OUT;0.01;1.37310                                     |
//| EURUSD;SELL;IN;0.01;1.37320                                      |
//| EURUSD;BUY;INOUT;0.02;1.37294                                    |
//+------------------------------------------------------------------+
string sBuildDealString(ulong ticket)
  {
   string deal="";
   double volume=0;
   bool bFirstInOut=true;
//--- find deal volume.
//--- if this is INOUT then volume must contain ONLY volume of 'IN'.
   if(HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_INOUT)
     {
      if(PositionSelect(HistoryDealGetString(ticket,DEAL_SYMBOL)))
        {
         volume=PositionGetDouble(POSITION_VOLUME);
        }
      else
        {
         Print("Failed to get volume!");
        }
     }
//--- if it's 'IN' or 'OUT' deal then use it's volume as is.
   else
     {
      volume=HistoryDealGetDouble(ticket,DEAL_VOLUME);
     }
//--- build deal string(format sample: "<EURUSD;BUY;IN;0.01;1.37294>").
   int iDealEntry=(int)HistoryDealGetInteger(ticket,DEAL_ENTRY);
//--- if this is OUT deal, and there are no open positions left.
   if(iDealEntry==DEAL_ENTRY_OUT && !PositionSelect(HistoryDealGetString(ticket,DEAL_SYMBOL)))
     {
      //--- For safety reasons, we check if there is any position left with current symbol. If NO, then let's use 
      //--- new deal type - OUTALL. This will guarante that there are no open orders left on or account when all
      //--- position has been closed on 'remote' MetaTrader 5 side. This can happen due to fact, that volume is 
      //--- is mapped to new values on client side, therefor there can be some very small difference which leaves
      //--- order open with very small lot size. 
      iDealEntry=DEAL_ENTRY_OUTALL;  // My own predefined value (this value should not colide with EMUN_DEAL_ENTRY values).
     }
   StringConcatenate(deal,"<",AccountInfoInteger(ACCOUNT_LOGIN),";",
                   HistoryDealGetString(ticket,DEAL_SYMBOL),";",
                   Type2String((ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE)),";",
                   Entry2String(iDealEntry),";",DoubleToString(volume,2),";",
                      DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
                   (int)SymbolInfoInteger(HistoryDealGetString(ticket,DEAL_SYMBOL),SYMBOL_DIGITS)),">");
   Print("DEAL:",deal);
   return deal;
  }

コードを見ていると、新しい DEAL_ENTRY type - DEAL_ENTRY_OUTALLに驚かれるかもしれません。これは私が作成したもので、MetaTrader 4 側でのボリューム処理について説明するともっとよく理解できることと思います。

もうひとつ面白いかもしれないことは OnTimer() 関数です。初期化中、毎秒OnTimer() 呼び出しを得るため EventSetTimer(1) を呼びます。その関数がサーバーライブラリから情報(ログ)をプリントアウトする1行であれば、内部は以下です。

//+------------------------------------------------------------------+
//| Print logs from Server every second (if there are any)           |
//+------------------------------------------------------------------+
void OnTimer()
  {
   PrintLogs();
  }

サーバーライブラリから実行する関数すべての後にこの関数(PrintLogs)を呼び、状態とエラー情報をプリントアウトします。

サーバー側では入力パラメータ StartupTypeも見られます。

enum ENUM_STARTUP_TYPE
  {
   STARTUP_TYPE_CLEAR,    // CLEAR - Send every new DEAL wich appears on account.
   STARTUP_TYPE_CONTINUE  // CONTINUE - Do not send DEAL before existing POSITION has been closed.
  };
//--- input parameters
input ENUM_STARTUP_TYPE InpStartupType=STARTUP_TYPE_CONTINUE; // Startup type

これは、シグナル提供者がすでにポジションをオープンしているアカウントに追加される可能性があり、そのためそれらに関する情報がクライアント側で誤解を招くおそれがあることを踏まえて追加されます。このパラメータにより既存のトレードからの情報を受け取りたいか、または新規オープンのポジションからのみ情報を受け取りたいか選択することができます。

それはまた、初めてアカウントに問い合わせるのか、以前に実行したことのあるアカウントに問い合わせるのかということ、そしてPC、プログラムを再起動したばかりかまたはコードに変更を加えたかということも重要です。


5.2. クライアント側

クライアント側には無限に(recv)ソケット受け取り関数内で循環するスクリプトがあります。この関数は「ブロック」状態にあるため、スクリプトはサーバーから何かが受け取られるまでの間ロックされています。よってプロセッサの時間を心配することはありません。

//--- server up and running. Start data collection and processing
   while(!IsStopped())
     {
      Print("Client: Waiting for DEAL...");
      ArrayInitialize(iBuffer,0);
      iRetVal=recv(iSocketHandle,iBuffer,ArraySize(iBuffer)<<2,0);
      if(iRetVal>0)
        {
         string sRawData=struct2str(iBuffer,iRetVal<<18);
         Print("Received("+iRetVal+"): "+sRawData);

これはクライアントを停止するのに問題を起こします。『スクリプト消去』をクリックしても、スクリプトが消去されないのです。二度クリックするとスクリプトはタイムアウトにより消去されます。これは受け取り関数に対してタイムアウトが適用されるなら修正可能でしょうが、すでに「コードベース」で利用可能なサンプルを使っているため、これは原形のままにすることとします。 

ひとたびデータが受け取られると、実アカウントでディールが処理される前にそれを分割し検証します。

         //--- split records
         string arrDeals[];
         //--- split raw data in multiple deals (in case if more than one is received).
         int iDealsReceived=Split(sRawData,"<",10,arrDeals);
         Print("Found ",iDealsReceived," deal orders.");
         //--- process each record
         //--- go through all DEALs received
         for(int j=0;j<iDealsReceived;j++) 
           {
            //--- split each record to values
            string arrValues[];
            //--- split each DEAL in to values
            int iValuesInDeal=Split(arrDeals[j],";",10,arrValues);
            //--- verify if DEAL request received in correct format (with correct count of values)
            if(iValuesInDeal==6)
              {
                 if(ProcessOrderRaw(arrValues[0],arrValues[1],arrValues[2],
                                    arrValues[3],arrValues[4],
                                         StringSubstr(arrValues[5],0,StringLen(arrValues[5])-1)))
                 {
                  Print("Processing of order done sucessfully.");
                 }
               else
                 {
                  Print("Processing of order failed:\"",arrDeals[j],"\"");
                 }
              }
            else
              {
               Print("Invalid order received:\"",arrDeals[j],"\"");
               //--- this was last one in array
               if(j==iDealsReceived-1)
                 {
                  //--- it might be incompleate beginning of next deal.
                  sLeftOver=arrDeals[j];
                 }
              }
           }
//+------------------------------------------------------------------+
//| Processing received raw data (text format)                       |
//+------------------------------------------------------------------+
bool ProcessOrderRaw(string saccount,string ssymbol,string stype,string sentry,string svolume,string sprice)
  {
//--- clearing
   saccount= Trim(saccount);
   ssymbol = Trim(ssymbol);
   stype=Trim(stype);
   sentry=Trim(sentry);
   svolume= Trim(svolume);
   sprice = Trim(sprice);
//--- validations
   if(!ValidateAccountNumber(saccount)){Print("Invalid account:",saccount);return(false);}
   if(!ValidateSymbol(ssymbol)){Print("Invalid symbol:",ssymbol);return(false);}
   if(!ValidateType(stype)){Print("Invalid type:",stype);return(false);}
   if(!ValidateEntry(sentry)){Print("Invalid entry:",sentry);return(false);}
   if(!ValidateVolume(svolume)){Print("Invalid volume:",svolume);return(false);}
   if(!ValidatePrice(sprice)){Print("Invalid price:",sprice);return(false);}
//--- convertations
   int account=StrToInteger(saccount);
   string symbol=ssymbol;
   int type=String2Type(stype);
   int entry=String2Entry(sentry);
   double volume= GetLotSize(StrToDouble(svolume),symbol);
   double price = NormalizeDouble(StrToDouble(sprice),(int)MarketInfo(ssymbol,MODE_DIGITS));
   Print("DEAL[",account,"|",symbol,"|",Type2String(type),"|",
        Entry2String(entry),"|",volume,"|",price,"]");
//--- execution
   ProcessOrder(account,symbol,type,entry,volume,price);
   return(true);
  }

皆がアカウントに$10,000を持っているとは限らないので、クライアント側では関数 GetLotSize() によって「ロット」サイズが再計算されます。サーバー側で実行されている戦略もまた資金管理の可能性があり、そのためクライアント側でも同じことをする必要があります。

私からみなさんに『ロットマッピング』を提供します。クライアントのユーザーが自身の選ぶロットサイズ(最小および最大)を指定することができ、そうすると「クライアントスクリプト」がみなさんに代わってマッピングをしてくれるのです。

extern string _1 = "--- LOT MAPPING ---";
extern double  InpMinLocalLotSize  =  0.01;
extern double  InpMaxLocalLotSize  =  1.00; // Recomended bigger than
extern double  InpMinRemoteLotSize =  0.01;
extern double  InpMaxRemoteLotSize =  15.00;
//+------------------------------------------------------------------+
//| Calculate lot size                                               |
//+------------------------------------------------------------------+
double GetLotSize(string remote_lots, string symbol)
{
   double dRemoteLots = StrToDouble(remote_lots);
   double dLocalLotDifference = InpMaxLocalLotSize - InpMinLocalLotSize;
   double dRemoteLotDifference = InpMaxRemoteLotSize - InpMinRemoteLotSize;
   double dLots = dLocalLotDifference * (dRemoteLots / dRemoteLotDifference);
   double dMinLotSize = MarketInfo(symbol, MODE_MINLOT); 
   if(dLots<dMinLotSize)
      dLots=dMinLotSize;
   return (NormalizeDouble(dLots,InpVolumePrecision));
}

クライアント側では 4 および 5 のディジットブローカー をサポートし、そこにはまた「標準ロット」 (0.1)サポート と「ミニロット」 (0.01) サポートもあります。この理由で私は新しい DEAL_ENTRY type - DEAL_OUTALLを作成する必要があったのです。

クライアント側はマッピングを行っているので、小さなロットサイズはクローズしないままになっているといくらか問題となりえます。

void ProcessOrder(int account, string symbol, int type, int entry, double volume, double price)
{
   if(entry==OP_IN)
   {
      DealIN(symbol,type,volume,price,0,0,account);
   }
   else if(entry==OP_OUT)
   {
      DealOUT(symbol, type, volume, price, 0, 0,account);
   }
   else if(entry==OP_INOUT)
   {
      DealOUT_ALL(symbol, type, account);
      DealIN(symbol,type,volume,price,0,0,account);
   }
   else if(entry==OP_OUTALL)
   {
      DealOUT_ALL(symbol, type, account);
   }
}

5.3. MetaTrader 5 ポジション vs MetaTrader 4 オーダー

実装中に別の問題を発見しました。MetaTrader 5  にはつねにそれぞれのシンボルに対してポジションはただ一つですが、MetaTrader 4 では全くことなる方法で処理されるのです。できるだけ近く取得するため、MetaTrader 4 側で複数オーダーをオープンすることで同一エントリーとシンボルを持つそれぞれの新規ディールをカバーします。

各新規「イン」ディールは新規オーダーで、「アウト」ディールがある場合、クローズにあたり3ステップで処理を行う機能性を実装しました。

  1. オープンしているオーダーをすべて確認し、依頼のサイズに一致するものがあればそれをクローズします。もし一致するオーダーがなければ、
  2. オープンしているオーダーをすべて確認し、依頼されている「アウト」のボリュームサイズより小さいサイズのオーダーをクローズします。もしもまだ何か残っているものがある場合は、
  3. サイズが依頼サイズより大きいオーダーをクローズし、クローズされず残るはずのサイズの新規オーダーをオープンします。通常は3番目のステップはまったく行われることはありません。保護目的で作成されるものです。
//+------------------------------------------------------------------+
//| Process DEAL ENTRY OUT                                           |
//+------------------------------------------------------------------+
void DealOUT(string symbol, int cmd, double volume, double price, double stoploss, double takeprofit, int account)
{
   int type = -1;
   int i=0;
   
   if(cmd==OP_SELL)
      type = OP_BUY;
   else if(cmd==OP_BUY)
      type = OP_SELL;  
   
   string comment = "OUT."+Type2String(cmd);
   //--- Search for orders with equal VOLUME size and with PROFIT > 0
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()==volume)
                  {
                     if(OrderProfit()>0)
                     {
                        if(CloseOneOrder(OrderTicket(), symbol, type, volume))
                        {
                           Print("Order with exact volume and profit>0 found and executed.");
                           return;
                        }
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with equal VOLUME size and with ANY profit size
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()==volume)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, volume))
                     {
                        Print("Order with exact volume found and executed.");
                        return;
                     }
                  }
               }
            }
         }
      }
   }
   double volume_to_clear = volume;
   //--- Search for orders with smaller volume AND with PROFIT > 0
   int limit = OrdersTotal();
   for(i=0;i<limit;i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()<=volume_to_clear)
                  {
                     if(OrderProfit()>0)
                     {
                        if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                        {
                           Print("Order with smaller volume and profit>0 found and executed.");
                           volume_to_clear-=OrderLots();
                           if(volume_to_clear==0)
                           {
                              Print("All necessary volume is closed.");
                              return;
                           }
                           limit = OrdersTotal();
                           i = -1; // Because it will be increased at end of cycle and will have value 0.
                        }
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with smaller volume
   limit = OrdersTotal();
   for(i=0;i<limit;i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()<=volume_to_clear)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                     {
                        Print("Order with smaller volume found and executed.");
                        volume_to_clear-=OrderLots();
                        if(volume_to_clear==0)
                        {
                           Print("All necessary volume is closed.");
                           return;
                        }
                        limit = OrdersTotal();
                        i = -1; // Because it will be increased at end of cycle and will have value 0.
                     }
                  }
               }
            }
         }
      }
   }
   //--- Search for orders with higher volume
   for(i=0;i<OrdersTotal();i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderMagicNumber()==account)
         {
            if(OrderSymbol()==symbol)
            {
               if(OrderType()==type)
               {
                  if(OrderLots()>=volume_to_clear)
                  {
                     if(CloseOneOrder(OrderTicket(), symbol, type, OrderLots()))
                     {
                        Print("Order with smaller volume found and executed.");
                        volume_to_clear-=OrderLots();
                        if(volume_to_clear<0)//Closed too much
                        {
                           //Open new to compensate lose
                           DealIN(symbol,type,volume_to_clear,price,OrderStopLoss(),OrderTakeProfit(),account);
                        }
                        else if(volume_to_clear==0)
                        {
                           Print("All necessary volume is closed.");
                           return;
                        }
                     }
                  }
               }
            }
         }
      }
   }
   if(volume_to_clear!=0)
   {
      Print("Some volume left unclosed: ",volume_to_clear);
   }
}

おわりに

ここで作成し添付されているファイルは必ずより良いクライアントサーバープロトコル、より高性能な通信、より良い実行方法によって改善されうるものです。私のタスクはそれが可能なのか検証するため、許容できるクオリティーで構築することでした。よって各人個人のニーズに応じて利用されることと思います。

それはご自身の戦略や MQL5 チャンピオンシップ出場者すべての戦略を追いかけるには十分よく動作します。MQL4 および MQL5 によって提供されているパフォーマンスや機能性はプロとしてまた商業的に取り入れるにも十分です。個人のコンピュータとご自身の戦略だけですべての MetaTrader 4 および MetaTrader 5 クライアントに向けたひじょうにすぐれたシグナルプロバイダーを作成することができると信じています。

ここで提供させていただいたコードを改良され、ご意見やご提案を持ってきていただけるのをお待ちしています。またご質問がおありの場合は答えるようにいたします。同時に私はチャンピオンシップのお気に入りの出場者をフォローするテストも実行しています。これまで1週間調子よく走っています。何か問題を見つけたら、アップデートをお届けするつもりです。

Tsaktuo

ご自身のアカウントで本稿記載の機能性や実行ファイルご使用にあたっては、アカウントに生じうる損失や損害について一切の責任はご自身で追うこととなりますのでご注意ください。実アカウントではよく検証をした後に「限り」、また本稿で提供されている機能性に関してよく理解した場合「のみ」トレードを行ってください。


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/344

添付されたファイル |
MQL5-RPC. MQL5からのリモートプロシージャコール:ウェブサービスアクセスと、利益のためのXML-RPC ATC アナライザー MQL5-RPC. MQL5からのリモートプロシージャコール:ウェブサービスアクセスと、利益のためのXML-RPC ATC アナライザー
この記事は、リモートプロシージャコールを可能にするMQL5-RPCフレームワークを紹介します。XML-RPCの基礎から始め、MQL5の実装、そして、二つの実例を紹介します。最初の例は、外部のウェブサービスを使用するというもので、二つ目は、XML-RPC ATC 2011 Analyzerサービスのクライアントの例です。もし、ATC 2011からの異なる統計の実装や分析方法に興味のある場合、この記事はうってつけだと思います。
重回帰分析ストラテジージェネレータ兼ストラテジーテスタ 重回帰分析ストラテジージェネレータ兼ストラテジーテスタ
本稿ではトレーディングシステム開発のために重回帰分析を利用する方法を述べます。戦略検索自動化のための回帰分析の利用法を示します。例としてプログラミングに高い技能を要求せず作成され統合される回帰式を提供します。
「EA 階層」を用いたMQL5 Expert Advisors の簡単作成 「EA 階層」を用いたMQL5 Expert Advisors の簡単作成
「EA 階層」は最初のドラッグアンドドロップ MetaTrader MQL5 Expert Advisor ビルダーです。使用法がひじょうに簡単なグラフィックユーザーインターフェースを用いて複雑な MQL5 を作成することが可能です。「EA 階層」ではボックスをつなぐことによってExpert Advisors を作成します。ボックスには MQL5 関数、テクニカルインディケータ、カスタムインディケータ、値などが入っています。『ボックス階層』を利用して「EA 階層」は Expert Advisor の MQL5 コードを作成します。
初めてのお客様へのアドバイス 初めてのお客様へのアドバイス
有名人の格言ではよくこう言われます。「失敗を恐れる者はなにもなしえない。」怠慢自体が誤りであることを認めなければ、この言葉を語るのは難しいでしょう。しかし、将来の過ちを最小にするために過去の過ち(自分自身または他者の)を分析することは常に可能です。これから、同じ名前のサービスにおけるジョブ実行中に再発生可能性な状況を検証していこうと思います。