【问题标题】:How can I convert Metatrader 4 alert or email indicator signal to Expert Advisor to open trades?如何将 Metatrader 4 警报或电子邮件指标信号转换为 Expert Advisor 以进行交易?
【发布时间】:2020-04-02 01:54:09
【问题描述】:

我一直在使用指标进行交易。我没有开发指标,所以我只能访问.ex4 文件。如何在警报或电子邮件信号中提取止盈、未平仓交易和止损值以打开交易?请参阅下面的电子邮件和警报信号示例。

【问题讨论】:

    标签: mql4 algorithmic-trading metatrader4


    【解决方案1】:

    这是一个本地 MQL 解决方案的工作示例 script,它使用 kernel32.dll 将日志文件从 ./MQL4/Logs 复制到 ./MQL4/FilesLogSignalParser抽象基类需要子类化,需要实现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);
             }
          }
       }     
    }
    

    【讨论】:

    • 哇,这看起来不可思议。我很久没有编写 MQL 代码了,所以这里有很多东西我试图理解。虽然我没有问最初的问题,但如果您能用此日志 0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286 说明这如何适用于开仓交易,我将不胜感激
    • @TenOutOfTen 我更新了上面的帖子,提供了一个实际示例,说明如何使用与日志样本相关的类。
    • 再次感谢例如。如果您能帮我看看这个问题,我将不胜感激stackoverflow.com/questions/53421751/…
    • @nicholishen 我对编写 mql 代码还是很陌生。我问过stackoverflow.com/questions/55930471/…,但还没有找到解决方案。如果可能的话,如果你能帮助我,我将不胜感激。最近也有人悬赏我的问题。
    • 对于我提出的 MQL5 问题,我非常感谢您的帮助。我已经为这个问题创建了一个赏金,并提供了额外的信息。请看stackoverflow.com/questions/62528297/…
    【解决方案2】:

    由于指标也通过Alert 函数发送数据,因此无需从您的电子邮件中提取数据。警报会记录到 *.log 文本文件中的 .\MQL4\Logs 目录中。您可以编写一些使用win32 读取日志的MQL,然后在MQL 中制作您自己的解析器。

    另一种选择是编写一个看门狗脚本来扫描和解析日志文件,并将结果写入 EA 可以访问的 csv。与 MQL 解决方案相比,这种方法的好处是开发起来非常容易,并且由于它适用于所有符号,因此避免了多个 EA 尝试同时读取日志写入 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()
    

    【讨论】:

    • 我对此很感兴趣,虽然我不知道如何用 Python 编写代码。我刚刚为 Mac 安装了 Anaconda。我将如何使用它?这就是我的指标在 .log 0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286 中的显示方式。非常感谢您的帮助。
    • @TenOutOfTen 您需要使用*.py 扩展名保存脚本,然后您将从命令行运行它。 C:\Path\To\Script&gt;python log_parser.py。我不确定它是否适用于 mac,因为您可能使用的是虚拟机...
    • @TenOutOfTen 我刚刚发布了一个本地 MQL 解决方案,您也可以尝试一下。
    • 你能帮我看看这个问题吗stackoverflow.com/questions/62479581/…
    【解决方案3】:

    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)。然后使用 MQL4 文档中的 FileSearchNext() 函数和 example 从 MT4 应用程序读取文件。读取文件后,您需要使用字符串functions 对其进行解析,并创建一个 OrderSend() 请求(可能检查输入以及您的逻辑是否允许您的机器人发送交易,例如您没有达到允许的最大值未平仓交易、交易时间、其他逻辑)。

    【讨论】:

    • 感谢您的分析。我对两种方法感兴趣,从指标警报信号或电子邮件信号进行交易。从表面上看,我认为从指标信号警报中进行交易比从电子邮件中进行交易要简单得多。如果您可以确认这是可能的,我宁愿编辑我的问题以特别坚持指标信号警报。如果您也可以为警报弹出窗口提供解决方案,我将不胜感激,如果可能的话。
    • 我相信警报数据一经发布就无法通过简单的方式将其获取回MT4。正如我提到的,您可以从 Terminal\Common\Files 或 Terminal\TERMINAL_ID\MQL4\Files 读取文件,日志位于 Terminal\TERMINAL_ID\MQL4\Logs 中。因此,您无法阅读日志以查看警报(如果已发送,它也会打印在日志中)。如果您有代码,您可以通过在警报之后发送ChartEventCustom() 来轻松解决问题,并且您会在该事件中提及符号、tf 和警报消息(或至少是方向)。但是你不能在没有源的情况下编辑指标。
    • 我建议您检查指标是否有缓冲区。在这种情况下,可以从指标获取缓冲数据,并在缓冲区更改后发送交易。但是,即使您有资源,当然也不能保证缓冲区会对您有所帮助。
    • 指标没有缓冲区。我想更难的路线是最明显的:)。我不太了解 Java,但如果您能提供任何链接(更好的代码),我将不胜感激,因为您认为这些链接对于在电子邮件到达后立即阅读很有帮助。
    • 有趣的是,我什至无法为 Outlook 或 Gmail 找到自动保存来自一个收件人的电子邮件的插件。所有这些都需要手动完成。我想我不能逃避编码。 :)
    猜你喜欢
    • 2017-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多