400 lines
16 KiB
MQL5
400 lines
16 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| ScanAllCalendarEvents.mq5 |
|
||
|
|
//| 用于获取MT5所有财经日历事件ID|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "www.eatrader.cn"
|
||
|
|
#property link "https://www.eatrader.cn"
|
||
|
|
#property version "1.00"
|
||
|
|
#property description "使用方法:需要把本脚本放到Scripts文件夹下,放开加载,每次加载仅仅执行1次"
|
||
|
|
#property description "存放路径 C:\Users\Administrator\AppData\Roaming\MetaQuotes\Terminal\Common\Files\Common\Files"
|
||
|
|
#property description "主要显示:最重要的数据"
|
||
|
|
#property description "作者:648555129"
|
||
|
|
#property script_show_inputs
|
||
|
|
|
||
|
|
//--- 输入参数:你可以根据需要修改起始时间
|
||
|
|
input int 过去n天 = 7;
|
||
|
|
input int 未来n天 = 30;
|
||
|
|
|
||
|
|
enum countname
|
||
|
|
{
|
||
|
|
us = 0, // 美国
|
||
|
|
cn = 1, // 中国
|
||
|
|
eu = 2, // 欧元区
|
||
|
|
jp = 3, // 日本
|
||
|
|
gb = 4, // 英国
|
||
|
|
ca = 5, // 加拿大
|
||
|
|
au = 6, // 澳大利亚
|
||
|
|
ch = 7, // 瑞士
|
||
|
|
nz = 8, // 新西兰
|
||
|
|
};
|
||
|
|
|
||
|
|
input countname 选择国家_单独显示 = us;
|
||
|
|
input bool 显示所有9大主要经济体的最重要的数据 = true;
|
||
|
|
input ENUM_CALENDAR_EVENT_IMPORTANCE 重要性级别_选择=CALENDAR_IMPORTANCE_HIGH;
|
||
|
|
// 日志文件全局句柄
|
||
|
|
int g_logFileHandle = -1;
|
||
|
|
string g_logFilePath = "";
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 输出到日志文件和控制台 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Log(string msg)
|
||
|
|
{
|
||
|
|
// 输出到控制台
|
||
|
|
Print(msg);
|
||
|
|
// 输出到文件(如果文件已打开)
|
||
|
|
if(g_logFileHandle != INVALID_HANDLE)
|
||
|
|
FileWrite(g_logFileHandle, msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 生成日志文件名(按日期时间) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
string GetLogFileName()
|
||
|
|
{
|
||
|
|
datetime now = TimeLocal();
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(now, dt);
|
||
|
|
// 格式:CalendarEvents_2026-06-12_14-30-22.txt
|
||
|
|
return StringFormat("CalendarEvents_%04d-%02d-%02d_%02d-%02d-%02d.txt",
|
||
|
|
dt.year, dt.mon, dt.day, dt.hour, dt.min, dt.sec);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 打开日志文件 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool OpenLogFile()
|
||
|
|
{
|
||
|
|
string fileName = GetLogFileName();
|
||
|
|
// 使用 Common 文件夹(所有终端共享,路径在:C:\Users\用户名\AppData\Roaming\MetaQuotes\Terminal\Common\Files)
|
||
|
|
g_logFilePath = "Common\\Files\\" + fileName;
|
||
|
|
g_logFileHandle = FileOpen(g_logFilePath, FILE_WRITE | FILE_TXT | FILE_COMMON | FILE_ANSI);
|
||
|
|
if(g_logFileHandle == INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
Print("错误:无法创建日志文件 ", g_logFilePath, " 错误码=", GetLastError());
|
||
|
|
Print("将只输出到控制台,不保存文件。");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
// 写入文件头
|
||
|
|
Log("======================================================");
|
||
|
|
Log("MT5 财经日历事件记录 - 生成时间: " + TimeToString(TimeLocal(), TIME_DATE|TIME_MINUTES|TIME_SECONDS));
|
||
|
|
Log("======================================================");
|
||
|
|
Log("");
|
||
|
|
// 打印存放路径方便用户查找
|
||
|
|
Print("日志文件已保存到 Common 文件夹:", g_logFilePath);
|
||
|
|
Print("你可以通过MT5菜单:文件 -> 打开数据文件夹 -> MQL5 -> Files 找到该文件。");
|
||
|
|
Print("存放路径:C:\Users\Administrator\AppData\Roaming\MetaQuotes\Terminal\Common\Files\Common\Files");
|
||
|
|
Print("如需桌面保存,请手动复制该文件到桌面。或创建快捷方式到桌面");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 关闭日志文件 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CloseLogFile()
|
||
|
|
{
|
||
|
|
if(g_logFileHandle != INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
FileClose(g_logFileHandle);
|
||
|
|
g_logFileHandle = INVALID_HANDLE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 脚本主函数 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnStart()
|
||
|
|
{
|
||
|
|
// 打开日志文件
|
||
|
|
OpenLogFile();
|
||
|
|
|
||
|
|
switch(选择国家_单独显示)
|
||
|
|
{
|
||
|
|
case 0: PrintHighImportanceEvents("US"); break;
|
||
|
|
case 1: PrintHighImportanceEvents("CN"); break;
|
||
|
|
case 2: PrintHighImportanceEvents("EU"); break;
|
||
|
|
case 3: PrintHighImportanceEvents("JP"); break;
|
||
|
|
case 4: PrintHighImportanceEvents("GB"); break;
|
||
|
|
case 5: PrintHighImportanceEvents("CA"); break;
|
||
|
|
case 6: PrintHighImportanceEvents("AU"); break;
|
||
|
|
case 7: PrintHighImportanceEvents("CH"); break;
|
||
|
|
case 8: PrintHighImportanceEvents("NZ"); break;
|
||
|
|
}
|
||
|
|
|
||
|
|
string countries[] = {"EU", "JP", "CA", "AU", "CN", "US", "GB", "CH", "NZ"};
|
||
|
|
if(显示所有9大主要经济体的最重要的数据 == true)
|
||
|
|
PrintSortedHighImportanceEvents(countries, true);
|
||
|
|
|
||
|
|
// 关闭日志文件
|
||
|
|
CloseLogFile();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 打印指定国家的高重要性经济事件 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void PrintHighImportanceEvents(string countryCode)
|
||
|
|
{
|
||
|
|
if(MQLInfoInteger(MQL_TESTER))
|
||
|
|
{
|
||
|
|
Log("【错误】财经日历API不能在策略测试器中使用!请将此函数挂载到在线图表上运行。");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
datetime now = TimeTradeServer();
|
||
|
|
datetime from = now - 过去n天 * 86400;
|
||
|
|
datetime to = now + 未来n天 * 86400;
|
||
|
|
|
||
|
|
string country_chinese_name = "";
|
||
|
|
if(countryCode == "US") country_chinese_name = "美国";
|
||
|
|
if(countryCode == "EU") country_chinese_name = "欧盟";
|
||
|
|
if(countryCode == "JP") country_chinese_name = "日本";
|
||
|
|
if(countryCode == "GB") country_chinese_name = "英国";
|
||
|
|
if(countryCode == "AU") country_chinese_name = "澳洲";
|
||
|
|
if(countryCode == "NZ") country_chinese_name = "新西兰";
|
||
|
|
if(countryCode == "CN") country_chinese_name = "中国";
|
||
|
|
if(countryCode == "CA") country_chinese_name = "加拿大";
|
||
|
|
if(countryCode == "CH") country_chinese_name = "瑞士";
|
||
|
|
|
||
|
|
MqlCalendarValue values[];
|
||
|
|
int total = CalendarValueHistory(values, from, to, countryCode);
|
||
|
|
|
||
|
|
if(total <= 0)
|
||
|
|
{
|
||
|
|
Log(StringFormat("未获取到 %s 的事件数据。错误码: %d", country_chinese_name, GetLastError()));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
Log(StringFormat("===== %s 未来一个月(外加过去%d天)高重要性事件 =====", country_chinese_name, 过去n天));
|
||
|
|
Log("发布时间(服务器)\t\t事件名称\t\t公布值\t\t预期值\t\t上次值");
|
||
|
|
|
||
|
|
ulong printedEvents[];
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
MqlCalendarValue val = values[i];
|
||
|
|
MqlCalendarEvent ev;
|
||
|
|
if(!CalendarEventById(val.event_id, ev))
|
||
|
|
continue;
|
||
|
|
if(ev.importance < 重要性级别_选择)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
string ev_chinese = (ev.importance == 2) ? "中MID" : ((ev.importance == 3) ? "高HIGH" : "");
|
||
|
|
|
||
|
|
bool alreadyPrinted = false;
|
||
|
|
for(int j = 0; j < ArraySize(printedEvents); j++)
|
||
|
|
{
|
||
|
|
if(printedEvents[j] == val.event_id)
|
||
|
|
{
|
||
|
|
alreadyPrinted = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(alreadyPrinted)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
int newSize = ArraySize(printedEvents);
|
||
|
|
ArrayResize(printedEvents, newSize + 1);
|
||
|
|
printedEvents[newSize] = val.event_id;
|
||
|
|
|
||
|
|
if(val.time > TimeCurrent())
|
||
|
|
Log("");
|
||
|
|
|
||
|
|
string actual = (val.actual_value != LONG_MIN) ? DoubleToString((double)val.actual_value / 1000000.0, 2) : "未公布/未更新";
|
||
|
|
string forecast = (val.forecast_value != LONG_MIN) ? DoubleToString((double)val.forecast_value / 1000000.0, 2) : "—";
|
||
|
|
string previous = (val.prev_value != LONG_MIN) ? DoubleToString((double)val.prev_value / 1000000.0, 2) : "—";
|
||
|
|
|
||
|
|
string cmt = "";
|
||
|
|
if(val.actual_value != LONG_MIN && (double)val.actual_value > (double)val.forecast_value && (double)val.actual_value > (double)val.prev_value)
|
||
|
|
cmt = " >>>>> 实际值大于>预期值,也大于前值,利好 ?利空 ?";
|
||
|
|
if(val.actual_value != LONG_MIN && (double)val.actual_value < (double)val.forecast_value && (double)val.actual_value < (double)val.prev_value)
|
||
|
|
cmt = " <<<<< 实际值小于<预期值,也小于前值,利好 ?利空 ?";
|
||
|
|
|
||
|
|
Log(StringFormat("%s\t\t<%d_%s>\t\t%s :\t\t%s\t\t%s\t\t%s \t\t%s",
|
||
|
|
TimeToString(val.time, TIME_DATE|TIME_MINUTES),
|
||
|
|
ev.importance, ev_chinese, ev.name,
|
||
|
|
actual, forecast, previous, cmt));
|
||
|
|
}
|
||
|
|
|
||
|
|
if(ArraySize(printedEvents) == 0)
|
||
|
|
Log(StringFormat("未找到 %s 未来一个月的高重要性事件", country_chinese_name));
|
||
|
|
else
|
||
|
|
Log(StringFormat("===== 共找到 %d 条高重要性事件 =====", ArraySize(printedEvents)));
|
||
|
|
Log("");
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 事件结构体 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
struct SEventItem
|
||
|
|
{
|
||
|
|
datetime time;
|
||
|
|
string country;
|
||
|
|
string event_name;
|
||
|
|
double forecast;
|
||
|
|
double actual;
|
||
|
|
double prev;
|
||
|
|
ulong event_id;
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 收集指定国家列表的高重要性事件 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int CollectHighImportanceEvents(string &countries[], SEventItem &events[])
|
||
|
|
{
|
||
|
|
ArrayResize(events, 0);
|
||
|
|
if(MQLInfoInteger(MQL_TESTER))
|
||
|
|
{
|
||
|
|
Log("【错误】财经日历API不能在策略测试器中使用!");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
datetime now = TimeTradeServer();
|
||
|
|
datetime from = now - 过去n天 * 86400;
|
||
|
|
datetime to = now + 未来n天 * 86400;
|
||
|
|
|
||
|
|
for(int c = 0; c < ArraySize(countries); c++)
|
||
|
|
{
|
||
|
|
string country = countries[c];
|
||
|
|
MqlCalendarValue values[];
|
||
|
|
int total = CalendarValueHistory(values, from, to, country);
|
||
|
|
if(total <= 0)
|
||
|
|
{
|
||
|
|
if(GetLastError() != 0)
|
||
|
|
Log(StringFormat("国家 %s 无事件或错误码: %d", country, GetLastError()));
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
ulong processed_ids[];
|
||
|
|
for(int i = 0; i < total; i++)
|
||
|
|
{
|
||
|
|
MqlCalendarValue val = values[i];
|
||
|
|
MqlCalendarEvent ev;
|
||
|
|
if(!CalendarEventById(val.event_id, ev))
|
||
|
|
continue;
|
||
|
|
if(ev.importance < 重要性级别_选择)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
bool already = false;
|
||
|
|
for(int j = 0; j < ArraySize(processed_ids); j++)
|
||
|
|
{
|
||
|
|
if(processed_ids[j] == val.event_id)
|
||
|
|
{
|
||
|
|
already = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(already)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
int sz = ArraySize(processed_ids);
|
||
|
|
ArrayResize(processed_ids, sz + 1);
|
||
|
|
processed_ids[sz] = val.event_id;
|
||
|
|
|
||
|
|
SEventItem item;
|
||
|
|
item.time = val.time;
|
||
|
|
item.country = country;
|
||
|
|
item.event_name = ev.name;
|
||
|
|
item.event_id = val.event_id;
|
||
|
|
item.forecast = (val.forecast_value != LONG_MIN) ? (double)val.forecast_value / 1000000.0 : -1;
|
||
|
|
item.actual = (val.actual_value != LONG_MIN) ? (double)val.actual_value / 1000000.0 : -1;
|
||
|
|
item.prev = (val.prev_value != LONG_MIN) ? (double)val.prev_value / 1000000.0 : -1;
|
||
|
|
|
||
|
|
int idx = ArraySize(events);
|
||
|
|
ArrayResize(events, idx + 1);
|
||
|
|
events[idx] = item;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ArraySize(events);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 排序事件数组 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void SortEventsByTime(SEventItem &events[], bool ascending = true)
|
||
|
|
{
|
||
|
|
int n = ArraySize(events);
|
||
|
|
if(n <= 1) return;
|
||
|
|
for(int i = 0; i < n - 1; i++)
|
||
|
|
{
|
||
|
|
for(int j = 0; j < n - i - 1; j++)
|
||
|
|
{
|
||
|
|
bool needSwap = ascending ? (events[j].time > events[j+1].time) : (events[j].time < events[j+1].time);
|
||
|
|
if(needSwap)
|
||
|
|
{
|
||
|
|
SEventItem temp = events[j];
|
||
|
|
events[j] = events[j+1];
|
||
|
|
events[j+1] = temp;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 打印并排序高重要性事件 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void PrintSortedHighImportanceEvents(string &countries[], bool ascending = true)
|
||
|
|
{
|
||
|
|
SEventItem events[];
|
||
|
|
int count = CollectHighImportanceEvents(countries, events);
|
||
|
|
if(count == 0)
|
||
|
|
{
|
||
|
|
Log("未获取到任何高重要性事件。");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
SortEventsByTime(events, ascending);
|
||
|
|
Log(StringFormat("===== 主要国家的最高重要性事件列表 (%s) =====", ascending ? "时间升序" : "时间降序"));
|
||
|
|
Log("发布时间(服务器)\t\t\t\t国家\t\t事件名称\t\t\t\t公布值\t\t预期值\t\t上次值");
|
||
|
|
|
||
|
|
for(int i = 0; i < count; i++)
|
||
|
|
{
|
||
|
|
string timeStr = TimeToString(events[i].time, TIME_DATE|TIME_MINUTES);
|
||
|
|
string forecastStr = (events[i].forecast != -1) ? DoubleToString(events[i].forecast, 2) : "—";
|
||
|
|
string actualStr = (events[i].actual != -1) ? DoubleToString(events[i].actual, 2) : "未公布/更新";
|
||
|
|
string prevStr = (events[i].prev != -1) ? DoubleToString(events[i].prev, 2) : "—";
|
||
|
|
|
||
|
|
string cmt = "";
|
||
|
|
if(events[i].actual != -1 && events[i].actual > events[i].forecast && events[i].actual > events[i].prev)
|
||
|
|
cmt = " >>>>> 实际值大于>预期值,也大于前值,利好 ?利空 ?";
|
||
|
|
if(events[i].actual != -1 && events[i].actual < events[i].forecast && events[i].actual < events[i].prev)
|
||
|
|
cmt = " <<<<< 实际值小于<预期值,也小于前值,利好 ?利空 ?";
|
||
|
|
|
||
|
|
string country_chinese_name = "";
|
||
|
|
if(events[i].country == "US") country_chinese_name = "美国";
|
||
|
|
if(events[i].country == "EU") country_chinese_name = "欧盟";
|
||
|
|
if(events[i].country == "JP") country_chinese_name = "日本";
|
||
|
|
if(events[i].country == "GB") country_chinese_name = "英国";
|
||
|
|
if(events[i].country == "AU") country_chinese_name = "澳洲";
|
||
|
|
if(events[i].country == "NZ") country_chinese_name = "新西兰";
|
||
|
|
if(events[i].country == "CN") country_chinese_name = "中国";
|
||
|
|
if(events[i].country == "CA") country_chinese_name = "加拿大";
|
||
|
|
if(events[i].country == "CH") country_chinese_name = "瑞士";
|
||
|
|
|
||
|
|
if(events[i].time > GetTodayDate())
|
||
|
|
Log("");
|
||
|
|
|
||
|
|
Log(StringFormat("%s\t\t%s\t\t%s\t\t : %s\t\t%s\t\t%s \t\t%s",
|
||
|
|
timeStr,
|
||
|
|
events[i].country + country_chinese_name,
|
||
|
|
events[i].event_name,
|
||
|
|
actualStr,
|
||
|
|
forecastStr,
|
||
|
|
prevStr,
|
||
|
|
cmt));
|
||
|
|
}
|
||
|
|
Log(StringFormat("===== 共 %d 条事件 =====", count));
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 获取今日起始时间 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
datetime GetTodayDate()
|
||
|
|
{
|
||
|
|
datetime now = TimeCurrent();
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(now, dt);
|
||
|
|
dt.hour = 0;
|
||
|
|
dt.min = 0;
|
||
|
|
dt.sec = 0;
|
||
|
|
return StructToTime(dt);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|