[英]How can I convert Metatrader 4 alert or email indicator signal to Expert Advisor to open trades?
這是本機 MQL 解決方案的工作示例script
,它使用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);
}
}
}
}
無需從您的電子郵件中提取數據,因為指標也通過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()
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()
函數和示例從 MT4 應用程序讀取文件。 讀取文件后,您需要使用字符串函數對其進行解析,並創建一個 OrderSend() 請求(可能檢查輸入以及您的邏輯是否允許您的機器人發送交易,例如您沒有達到允許的最大未平倉交易) ,交易時間,其他邏輯)。
您是否找到了可行的解決方案,我也需要嗎,請告訴我,我願意支付$ 30
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.