How can I convert Metatrader 4 alert or email indicator signal to Expert Advisor to open trades?
Asked Answered
S

3

8

I have been using an indicator to take trades. I didn't develop the indicator, so I only have access to the .ex4 file. How can I extract the take profit, open trade and stop loss values in the alerts or email signals to open trades? Please see a sample of the email and alert signals below.

enter image description here

enter image description here

Slumberland answered 29/8, 2018 at 9:20 Comment(0)
R
3

Here is a working example script of a native MQL solution which uses kernel32.dll to copy the log file from ./MQL4/Logs to ./MQL4/Files. The LogSignalParser abstract base class needs to be subclassed and requires implementation of the virtual bool parse() method.

Edit: @TenOutOfTen would like a practical example of how to parse the following row format in a log file:

0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286

Step 1: Save the LogParser.mqh somewhere meaningful.

//LogParser.mqh

#property strict
#include <stdlib.mqh>
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayObj.mqh>
#import "kernel32.dll"
   bool CopyFileW(string lpExistingFileName,
                  string lpNewFileName,
                  bool   bFailIfExists);
#import
//+------------------------------------------------------------------+
//|                                                              
//+------------------------------------------------------------------+
class Signal : public CObject
{
public:
   string            symbol;
   datetime          signal_time;
   int               order_type;
   double            price_entry;
   double            price_sl;
   double            price_tp;
   virtual int Compare(const CObject *node,const int mode=0) const override
   {
      const Signal *other=node;
      if(this.signal_time>other.signal_time)
         return 1;
      if(this.signal_time<other.signal_time)
         return -1;
      return 0;
   }
   string to_string()
   {
      return StringFormat("%s - %s(%s) @ %.5f, SL=%.5f, TP=%.5f",
         signal_time,
         symbol,
         order_type==OP_BUYLIMIT ? "BUY" : "SELL",
         price_entry,
         price_sl,
         price_tp
      );
   }
};
//+------------------------------------------------------------------+
//|Vector-like collection                                                          
//+------------------------------------------------------------------+
class SignalList : public CArrayObj
{
   public: Signal *operator[](int i){return this.At(i);}
};
//+------------------------------------------------------------------+
//|Abstract abse class: the parse method must be implemented in subclass                                                             
//+------------------------------------------------------------------+
class LogSignalParser : public CObject
{
protected:
   CArrayString      m_rows;
   SignalList        m_signals;
   string            m_log_file_name;
   string            m_ind_name;
public:
                     LogSignalParser(string indicator_name);

                     // parse method must be overridden!
   virtual bool      parse() = 0;
   int               Total();
   Signal           *operator[](int i);
protected:
   bool              _copy_log();
   int               _open_log();
   bool              _parse_rows();
};
//+------------------------------------------------------------------+
LogSignalParser::LogSignalParser(string indicator_name)
{
   m_log_file_name="copy_log.log";
   m_ind_name=indicator_name;
}
//+------------------------------------------------------------------+
bool LogSignalParser::_copy_log(void)
{
   MqlDateTime t;
   TimeLocal(t);
   string data_path = TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL4";
   string logs_path = data_path + "\\Logs\\";
   string dest_file = data_path + "\\Files\\" + m_log_file_name;
   string log_file=logs_path+StringFormat("%d%02d%02d.log",
                                          t.year,t.mon,t.day);
   return CopyFileW(log_file, dest_file, false);
}
//+------------------------------------------------------------------+
bool LogSignalParser::_parse_rows()
{
   if(!this._copy_log())
      return false;
   int h= this._open_log();
   if(h == INVALID_HANDLE)
      return false;
   m_rows.Clear();
   while(!FileIsEnding(h)){
      string row=FileReadString(h);
      if(StringFind(row,"Alert:") >= 0 && StringFind(row,m_ind_name) >= 0)
         m_rows.Add(row);
   }
   m_rows.Sort();
   FileClose(h);
   return true;
}
//+------------------------------------------------------------------+
int LogSignalParser::_open_log(void)
{
   return FileOpen(m_log_file_name,
      FILE_TXT|FILE_READ|FILE_SHARE_READ|FILE_SHARE_WRITE);
}
//+------------------------------------------------------------------+
int LogSignalParser::Total(void)
{
   return m_signals.Total();
}
//+------------------------------------------------------------------+
Signal* LogSignalParser::operator[](int i)
{
   return m_signals.At(i);
}

Step 2: Subclass the LogSignalParser class and override parse

//SuperIndicatorParser.mqh
#property strict
#include "LogParser.mqh"
//+------------------------------------------------------------------+
class SuperIndicatorParser : public LogSignalParser
{ 
public:
   SuperIndicatorParser():LogSignalParser("SuperIndicator"){}
   virtual bool parse() override;
};
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
bool SuperIndicatorParser::parse() override
{
   if(!this._parse_rows())
      return false;
   m_signals.Clear();
   MqlDateTime local;
   TimeLocal(local);
   for(int i=m_rows.Total()-1; i>=0; i--)
   {
      string row=m_rows[i];
      MqlDateTime log_time;
      TimeToStruct(StringToTime(StringSubstr(row, 2, 12)), log_time);
      log_time.year = local.year;
      log_time.mon = local.mon;
      log_time.day = local.day;
      datetime time = StructToTime(log_time); 
      row = StringSubstr(row, StringFind(row, m_ind_name) + StringLen(m_ind_name) + 1);
      StringReplace(row, ",", " ");
      string parts[];
      StringSplit(row, ' ', parts);
      int len = ArraySize(parts);
      string debug = "";
      for(int k=0;k<len;k++)
         debug += "|" + parts[k];
      if(len != 17)
         continue;
      Signal *s      = new Signal();
      s.signal_time  = time;
      s.symbol       = parts[0];
      s.order_type   = parts[8] == "BUY" ? OP_BUYLIMIT : OP_SELLLIMIT;
      s.price_entry  = double(parts[10]);
      s.price_tp     = double(parts[13]);
      s.price_sl     = double(parts[16]);
      m_signals.Add(s);
   }
   m_signals.Sort();
   return true;
}

Step 3: Use in MQL program (example script)

#property strict
#include "SuperIndicatorParser.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   SuperIndicatorParser parser;
   if(parser.parse()){
      for(int i=parser.Total()-1; i>=0; i--){
         Signal *s = parser[i];
         int ticket = OrderSend(
            s.symbol, s.order_type, 0.1, 
            s.price_entry, 0, s.price_sl, s.price_tp 
         ); 
         if(ticket < 0){
            Print(_LastError);
         }
      }
   }     
}
Rato answered 30/8, 2018 at 19:21 Comment(5)
Wow, this looks incredible. I haven’t been coding MQL for long so there’s a good amount of stuff here that I’m trying to understand. Though I didn’t ask the original question, I would be grateful if you could show how this applies to opening a trade with this log 0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286Sixtasixteen
@Sixtasixteen I updated the post above to give a practical example of how to use the classes in regard to your log-sample.Rato
Thanks once again for example. I would be really grateful if you'd take a look at this question for me #53422251Sixtasixteen
@Rato I am quite new to writing mql code. I asked #55930971 but haven't been able to get very far with a solution. I would appreciate if you could help me out, if possible. Someone recently put a bounty on my question too.Kutaisi
I would really appreciate some help with a MQL5 question I asked. I've created a bounty for the question with additional info. Please take a look #62528797Tapeworm
R
2

There's no need to pull the data from your email since the indicator is also sending the data via the Alert function. Alerts are logged to the .\MQL4\Logs directory in a *.log text file. You could write some MQL which uses win32 to read the log, and then make your own parser in MQL.

Another option is to write a watchdog script to scan and parse the log file and write the results to a csv where the EA can access it. The benefit of this method is how easy it is to develop compared to an MQL solution and since it works for all symbols it avoids a potential race condition where multiple EAs are trying to read log write csv at the same time.

Here is an example written in Python.

import csv
import re
import time
from datetime import datetime
from pathlib import Path

MQL_DATA_PATH = Path(
    'C:/Users/user/Desktop/MT-TEST/Vanilla-MT4-v0_0_2/MT4/MQL4'
)
OUTPUT_FILENAME = 'signals.csv'

signal_pattern = re.compile(r'''# regex - verbose mode
    (?P<time>\d\d:\d\d:\d\d).*? # time stamp
    (?P<symbol>[A-Z]{6}\w*),.*? # symbol with ECN suffix
    (?P<type>BUY|SELL).*?       # BUY or SELL command
    (?P<price>\d+\.\d+).*?      # execution price
    (?P<tp>\d+\.\d+).*?         # takeprofit 
    (?P<sl>\d+\.\d+)            # stoploss
''', re.VERBOSE)


def log_to_csv():
    date = datetime.now()
    log_file = MQL_DATA_PATH / 'Logs' / f'{date.strftime("%Y%m%d")}.log'
    with open(log_file) as f:
        log_entries = f.read()
    signals = [s.groupdict() for s in signal_pattern.finditer(log_entries)]
    for signal in signals:
        # correct time to MQL datetime
        signal['time'] = f"{date.strftime('%Y.%m.%d')} {signal['time']}"
    csv_file = MQL_DATA_PATH / 'Files' / OUTPUT_FILENAME
    with open(csv_file, 'w') as f:
        writer = csv.DictWriter(f,
            fieldnames=('time', 'symbol', 'type', 'price', 'tp', 'sl',),
            lineterminator='\n',
        )
        writer.writerows(signals)


def main():
    print(f'Watching MQL log and saving signals to {OUTPUT_FILENAME}')
    print('Press Ctrl+C to exit')
    while True:
        try:
            log_to_csv()
            print(datetime.now().strftime('%Y.%m.%d %H:%M:%S'), end='\r')
            time.sleep(5)
        except KeyboardInterrupt:
            exit()


if __name__ == '__main__':
    main()
Rato answered 30/8, 2018 at 4:10 Comment(4)
I am really interested in this though I don't know any how to code in Python. I just installed Anaconda for mac. How will I use this? This is how my indicator appears in the .log 0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286. I would really appreciate your assistance.Sixtasixteen
@Sixtasixteen You will need to save the script with a *.py extension and then you will run it from the command line. C:\Path\To\Script>python log_parser.py. I'm not sure if it will work with mac since you're likely using a virtual machine...Rato
@Sixtasixteen I just posted a native MQL solution you might also try.Rato
Could you please take a look at this question for me #62480081Sixtasixteen
L
0

MT4 cannot read your emails. You need to employ some other tools or a more universal language to read your emails, Java.Mail.API or Pyhton, or something else. Read the email, make sure the the format is correct and it is from the sender you are expecting, then put down the message into a file that is available to MT4 - either own folder (C:\Users\UserName\AppData\Roaming\MetaQuotes\Terminal\12345678E7E35342DB4776F5AE09D64B\MQL4\Files) or Common folder (C:\Users\User1\AppData\Roaming\MetaQuotes\Terminal\Common\Files). Then read the file from the MT4 application using FileSearchNext() function and example in MQL4 docs. After reading the file, you need to parse it with the String functions, and create a OrderSend() request (probably check the input and that your logic allows your robot to send a trade, e.g. you do not have reached maximum of allowed open trades, trading time, other logic).

Liston answered 29/8, 2018 at 11:38 Comment(8)
Thanks for the breakdown. I am interested in two approaches, taking trades from Indicator Alert signal or Email signal. From the look of things, I think taking a trade from an indicator signal alert would be a whole lot simpler than from an email. If you can confirm this is possible, I would rather edit my question to specifically stick to indicator signal alert. Also I would be grateful if you could also provide a solution to the alert popup, that is if it is possible.Slumberland
I believe it is not possible to obtain the alert data back to MT4 in a simple way once it is published. As I mentioned, you can read files from Terminal\Common\Files or from Terminal\TERMINAL_ID\MQL4\Files, and the logs are in Terminal\TERMINAL_ID\MQL4\Logs. So you cannot read the logs to see the alert (if it is sent, it is printed in the logs too). If you had the code, you could solve the problem easily by sending ChartEventCustom() after the alert, and you would mention symbol, tf and alert message (or at least direction) in that Event. But you cannot edit the indicator without the source.Liston
I would suggest you to check whether the indicator has buffers. In such a case, it is possible to obtain buffered data from the indicator, and send trades once the buffers changed. But there is no guarantee of course that the buffers will help you even if you had the sources.Liston
the indicator doesn't have buffers. I guess the harder route is the most visible :). I don't know much Java, but I'll appreciate if you could provide any links (better still code) that you think would really help with regard to reading the email as soon as it arrives.Slumberland
Interestingly I haven't even been able to find an add in/on for Outlook or Gmail that automatically saves emails from one recipient. All of the them require it to be done manually. I guess I can't run away from coding this. :)Slumberland
#21459521 for javamail. I would suggest Python if there is no difference for you as py is much simpler and shorter I believe. For small scripts it seems perfectListon
@DanielKniaz I thought of something like this a while ago but didn't even try, because I thought it wasn't possible. That said, I am very interested in this question. I have an indicator and this how it looks in the MQL4\Logs .log file 0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286. Isn't there anyway this string can be accessed and formatted from the .log file in an EA using mql?Sixtasixteen
nicholishen showed a python example on how to get the data from the logs. You cannot access the Logs folder from an expert advisor directly, you need at least DLL or another engine that does not have such restrictionsListon

© 2022 - 2024 — McMap. All rights reserved.