【发布时间】:2020-04-02 01:54:09
【问题描述】:
【问题讨论】:
标签: mql4 algorithmic-trading metatrader4
【问题讨论】:
标签: mql4 algorithmic-trading metatrader4
这是一个本地 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);
}
}
}
}
【讨论】:
0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286 说明这如何适用于开仓交易,我将不胜感激
由于指标也通过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()
【讨论】:
0 02:20:00.874 SuperIndicator USDCAD,M5: Alert: USDCAD, M5: Super Indicator SELL @ 1.29136, TP 1.28836, SL 1.29286 中的显示方式。非常感谢您的帮助。
*.py 扩展名保存脚本,然后您将从命令行运行它。 C:\Path\To\Script>python log_parser.py。我不确定它是否适用于 mac,因为您可能使用的是虚拟机...
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() 请求(可能检查输入以及您的逻辑是否允许您的机器人发送交易,例如您没有达到允许的最大值未平仓交易、交易时间、其他逻辑)。
【讨论】:
ChartEventCustom() 来轻松解决问题,并且您会在该事件中提及符号、tf 和警报消息(或至少是方向)。但是你不能在没有源的情况下编辑指标。