Как я могу преобразовать сигнал оповещения Metatrader 4 или индикатор электронной почты в советник для открытия сделок? - PullRequest
0 голосов
/ 29 августа 2018

Я использую индикатор для совершения сделок. Я не разработал индикатор, поэтому у меня есть доступ только к файлу .ex4. Как я могу извлечь значения тейк-профита, открытой сделки и стоп-лосса в сигналах тревоги или по электронной почте для открытия сделок? Пожалуйста, посмотрите образец электронного письма и сигналов тревоги ниже.

enter image description here

enter image description here

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Нет необходимости извлекать данные из вашего электронного письма, так как индикатор также отправляет данные через функцию Alert. Оповещения записываются в каталог .\MQL4\Logs в текстовом файле *.log. Вы можете написать некоторый MQL, который использует win32 для чтения журнала, а затем создать свой собственный анализатор на MQL.

Другой вариант - написать сторожевой скрипт для сканирования и анализа файла журнала и записи результатов в CSV, где советник может получить к нему доступ. Преимущество этого метода заключается в том, насколько легко его разрабатывать по сравнению с решением MQL, и, поскольку он работает для всех символов, он избегает потенциального состояния гонки, когда несколько советников пытаются одновременно прочитать запись журнала csv.

Вот пример, написанный на 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()
0 голосов
/ 30 августа 2018

Вот рабочий пример script нативного решения MQL, которое использует kernel32.dll для копирования файла журнала из ./MQL4/Logs в ./MQL4/Files. Абстрактный базовый класс LogSignalParser должен быть разделен на подклассы и требует реализации метода virtual bool parse().

Редактировать: @TenOutOfTen хотел бы практический пример того, как проанализировать следующий формат строки в файле журнала:

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

Шаг 1: Сохраните LogParser.mqh где-нибудь значащим.

//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);
}

Шаг 2: Подкласс класса LogSignalParser и переопределение 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;
}

Шаг 3: Использование в MQL-программе (пример скрипта)

#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);
         }
      }
   }     
}
0 голосов
/ 29 августа 2018

MT4 не может читать ваши письма. Вам нужно использовать некоторые другие инструменты или более универсальный язык для чтения ваших электронных писем, Java.Mail.API или Pyhton, или что-то еще. Прочтите письмо, убедитесь, что формат указан правильно и получен от ожидаемого отправителя, затем запишите сообщение в файл, доступный для MT4 - либо в собственную папку (C: \ Users \ UserName \ AppData \ Roaming \ MetaQuotes \ Terminal \ 12345678E7E35342DB4776F5AE09D64B \ MQL4 \ Files) или общая папка (C: \ Users \ User1 \ AppData \ Roaming \ MetaQuotes \ Terminal \ Common \ Files). Затем прочитайте файл из приложения MT4, используя функцию FileSearchNext() и пример в документах MQL4. После прочтения файла вам нужно проанализировать его с помощью String functions и создать запрос OrderSend () (возможно, проверьте вход и ваша логика позволяет роботу отправлять сделку, например, у вас нет достигнут максимум разрешенных открытых сделок, время торгов, другая логика).

...