[{"data":1,"prerenderedAt":1329},["ShallowReactive",2],{"\u002Fzh\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor":3,"\u002Fzh\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor-surround":1314,"\u002Fzh\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor-langs":1325},{"id":4,"title":5,"authors":6,"badge":12,"body":14,"date":1302,"description":1303,"draft":1304,"extension":1305,"image":1306,"meta":1308,"navigation":256,"path":1309,"seo":1310,"stem":1311,"updated":1312,"__hash__":1313},"posts\u002Fzh\u002F3.guides\u002F41.add-a-news-filter-to-your-mt4-expert-advisor.md","如何为您的 MT4 EA 添加新闻过滤器",[7],{"name":8,"to":9,"avatar":10},"ShowMyTrades Team","https:\u002F\u002Fshowmytrades.com",{"src":11},"\u002Ficon-512.png",{"label":13},"自动化",{"type":15,"value":16,"toc":1291},"minimark",[17,26,39,44,55,139,143,159,165,176,180,195,952,958,962,965,1031,1042,1053,1057,1060,1123,1127,1175,1179,1187,1191,1202,1208,1218,1224,1234,1245,1249,1276,1279,1287],[18,19,20,25],"p",{},[21,22,24],"a",{"href":23},"\u002Fglossary\u002Fwhat-is-an-expert-advisor","EA（智能交易系统）","如果在非农数据或利率决议公布时照常交易，跑的就不再是它的策略，而是在宽点差下掷硬币。新闻过滤器正是为此而设：在影响行情的数据公布前后几分钟暂停新开仓，其余时间策略照常运行。",[18,27,28,29,33,34,38],{},"MetaTrader 5 自带财经日历，",[30,31,32],"strong",{},"MetaTrader 4 没有","，EA 只能从外部获取新闻。本指南用的是免费的 ",[21,35,37],{"href":36},"\u002Fdocs\u002Fapi\u002Feconomic-calendar","ShowMyTrades 财经日历 API","：无需账户，无需 API 密钥，在终端里放行一个网址，再把一个包含文件粘贴进任意 EA 即可。",[40,41,43],"h2",{"id":42},"整体思路","🧭 整体思路",[45,46,51],"pre",{"className":47,"code":49,"language":50},[48],"language-text","每 15 分钟\n┌───────────────────\n│ 下载未来 2 天新闻\n│ （CSV，一次请求）\n└───────────────────\n          │\n          ▼\n每个 tick\n┌────────────────────────────────\n│ 是否有数据公布落在当前时间的\n│ [-30 分钟, +30 分钟] 范围内？\n│   是 → 不开新仓\n│   否 → 运行策略\n└────────────────────────────────\n","text",[52,53,49],"code",{"__ignoreMap":54},"",[56,57,58,74],"table",{},[59,60,61],"thead",{},[62,63,64,68,71],"tr",{},[65,66,67],"th",{},"设置项",[65,69,70],{},"代码中的默认值",[65,72,73],{},"原因",[75,76,77,91,107,118,128],"tbody",{},[62,78,79,83,88],{},[80,81,82],"td",{},"哪些货币",[80,84,85],{},[52,86,87],{},"EUR,USD",[80,89,90],{},"即所交易货币对的两种货币。留空则包含全部九种主要货币",[62,92,93,96,101],{},[80,94,95],{},"哪种影响级别",[80,97,98],{},[52,99,100],{},"high",[80,102,103,104],{},"想过滤得更保守，就用 ",[52,105,106],{},"high,medium",[62,108,109,112,115],{},[80,110,111],{},"公布前暂停",[80,113,114],{},"30 分钟",[80,116,117],{},"数据公布前点差扩大、流动性变薄",[62,119,120,123,125],{},[80,121,122],{},"公布后暂停",[80,124,114],{},[80,126,127],{},"第一波行情和随后的反转都出现在这段时间",[62,129,130,133,136],{},[80,131,132],{},"下载间隔",[80,134,135],{},"15 分钟",[80,137,138],{},"日历一天只变动几次；而且 API 本身就缓存 15 分钟",[40,140,142],{"id":141},"第-1-步在-metatrader-中允许该网址","🔑 第 1 步：在 MetaTrader 中允许该网址",[18,144,145,146,149,150,153,154,158],{},"MQL4 用 ",[52,147,148],{},"WebRequest()"," 从网上下载数据，但它只访问您事先批准过的网址。在终端中打开",[30,151,152],{},"工具 → 选项 → 智能交易系统（Tools → Options → Expert Advisors）","，勾选",[155,156,157],"em",{},"允许 WebRequest 访问下列 URL（Allow WebRequest for listed URL）","，然后添加：",[45,160,163],{"className":161,"code":162,"language":50,"meta":54},[48],"https:\u002F\u002Fshowmytrades.com\n",[52,164,162],{"__ignoreMap":54},[18,166,167,168,171,172,175],{},"漏了这一步，下载会返回 ",[52,169,170],{},"-1","，错误代码 ",[52,173,174],{},"4060","。这时包含文件会在「智能交易（Experts）」选项卡里打印一条提醒，免得您对着毫无动静的过滤器摸不着头脑。",[40,177,179],{"id":178},"第-2-步保存包含文件","📄 第 2 步：保存包含文件",[18,181,182,183,186,187,190,191,194],{},"把下面的代码保存为 ",[52,184,185],{},"SMT_NewsFilter.mqh","，放进终端的 ",[52,188,189],{},"MQL4\\Include"," 文件夹（文件 → 打开数据文件夹，File → Open Data Folder）。这就是 ",[21,192,193],{"href":36},"API 参考","里给出的那个文件（注释和日志文字已译成中文），在 MQL5 中也能原样编译。",[45,196,200],{"className":197,"code":198,"filename":185,"language":199,"meta":54,"style":54},"language-cpp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F+------------------------------------------------------------------+\n\u002F\u002F| SMT_NewsFilter.mqh - MT4 和 MT5 新闻过滤器\n\u002F\u002F| 数据：https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1（免费）\n\u002F\u002F| 文档：https:\u002F\u002Fshowmytrades.com\u002Fdocs\u002Fapi\u002Feconomic-calendar\n\u002F\u002F| 设置：工具 > 选项 > 智能交易系统 > 允许 WebRequest 访问\n\u002F\u002F|       https:\u002F\u002Fshowmytrades.com\n\u002F\u002F+------------------------------------------------------------------+\n#property strict\n\ninput string SMT_Currencies     = \"EUR,USD\"; \u002F\u002F 新闻：货币（逗号分隔，留空 = 全部）\ninput string SMT_Impact         = \"high\";    \u002F\u002F 新闻：影响级别（high | high,medium | all）\ninput int    SMT_MinutesBefore  = 30;        \u002F\u002F 新闻：公布前暂停（分钟）\ninput int    SMT_MinutesAfter   = 30;        \u002F\u002F 新闻：公布后暂停（分钟）\ninput int    SMT_RefreshMinutes = 15;        \u002F\u002F 新闻：下载间隔（分钟，最少 15）\n\ndatetime smt_eventTime[];\nstring   smt_eventTitle[];\ndatetime smt_nextDownload = 0;\nlong     smt_serverTime   = 0;     \u002F\u002F 上次下载时的服务器 UTC 时间（Unix 秒）\nuint     smt_syncTick     = 0;     \u002F\u002F 当时的 GetTickCount()\nbool     smt_clockSynced  = false;\n\n\u002F\u002F--- 真实的 UTC 时间：服务器时钟加上自下载以来经过的毫秒数，\n\u002F\u002F--- 用 GetTickCount() 计数，它不受时区、夏令时和电脑时钟校正的影响\n\u002F\u002F--- （uint 减法在计数器回绕后依然正确）。\n\u002F\u002F--- 首次下载之前退回使用 TimeGMT()。绝不用 TimeCurrent()：\n\u002F\u002F--- 经纪商的 GMT 偏移未知，而且会随夏令时跳变。\ndatetime SMT_Now()\n{\n   if(!smt_clockSynced) return TimeGMT();\n   uint elapsedMs = GetTickCount() - smt_syncTick;\n   return (datetime)(smt_serverTime + elapsedMs \u002F 1000);\n}\n\n\u002F\u002F--- 经纪商时钟与 UTC 的差值，单位为秒（7200 = GMT+2）。它本身就很有用：\n\u002F\u002F--- 大多数 EA 从来不知道这个值。TimeCurrent() 是最后一个 tick 的时间，\n\u002F\u002F--- 所以请在开市时读取，不要在周末读取。\nint SMT_BrokerGmtOffsetSeconds()\n{\n   return (int)(TimeCurrent() - SMT_Now());\n}\n\n\u002F\u002F--- 第 n 个逗号之后的全部内容（CSV 的标题，标题本身可能含有逗号）\nstring SMT_AfterComma(const string line, const int n)\n{\n   int pos = -1;\n   for(int i = 0; i \u003C n; i++)\n   {\n      pos = StringFind(line, \",\", pos + 1);\n      if(pos \u003C 0) return \"\";\n   }\n   string rest = StringSubstr(line, pos + 1);\n   StringReplace(rest, \"\\\"\\\"\", \"\\\"\");\n   if(StringLen(rest) >= 2 && StringGetCharacter(rest, 0) == '\"')\n      rest = StringSubstr(rest, 1, StringLen(rest) - 2);\n   return rest;\n}\n\n\u002F\u002F--- 下载未来 2 天的数据（时间窗口也包含过去 6 小时）\nbool SMT_Download()\n{\n   string url = \"https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1?format=csv&days=2&impact=\" + SMT_Impact;\n   if(StringLen(SMT_Currencies) > 0) url += \"&currency=\" + SMT_Currencies;\n\n   char   post[], result[];\n   string headers;\n   ResetLastError();\n   int status = WebRequest(\"GET\", url, \"\", NULL, 10000, post, 0, result, headers);\n   if(status != 200)\n   {\n      Print(\"[SMT] 日历下载失败：HTTP \", status, \"，错误 \", GetLastError(),\n            \"。https:\u002F\u002Fshowmytrades.com 是否已添加到 工具 > 选项 > 智能交易系统 > 允许 WebRequest？\");\n      smt_nextDownload = TimeCurrent() + 60; \u002F\u002F 一分钟后重试\n      return false;\n   }\n\n   string body = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);\n   string lines[];\n   int n = StringSplit(body, '\\n', lines);\n\n   ArrayResize(smt_eventTime, 0);\n   ArrayResize(smt_eventTitle, 0);\n   for(int i = 1; i \u003C n; i++) \u002F\u002F 第 0 行是表头\n   {\n      \u002F\u002F 最后一行 \"# server_time=1760702400 server_time_iso=...\"：服务器的 UTC 时钟\n      if(StringFind(lines[i], \"# server_time=\") == 0)\n      {\n         long serverTime = StringToInteger(StringSubstr(lines[i], 14, 10));\n         if(serverTime > 0)\n         {\n            smt_serverTime  = serverTime;\n            smt_syncTick    = GetTickCount();\n            smt_clockSynced = true;\n         }\n         continue;\n      }\n      string f[];\n      if(StringSplit(lines[i], ',', f) \u003C 8) continue;\n      int k = ArraySize(smt_eventTime);\n      ArrayResize(smt_eventTime, k + 1);\n      ArrayResize(smt_eventTitle, k + 1);\n      smt_eventTime[k]  = (datetime)StringToInteger(f[0]);      \u002F\u002F Unix 秒，UTC\n      smt_eventTitle[k] = f[2] + \" \" + f[3] + \": \" + SMT_AfterComma(lines[i], 7);\n   }\n   smt_nextDownload = TimeCurrent() + MathMax(SMT_RefreshMinutes, 15) * 60;\n   Print(\"[SMT] 日历：已加载 \", ArraySize(smt_eventTime), \" 个事件。当前 UTC \",\n         TimeToString(SMT_Now(), TIME_DATE|TIME_MINUTES), \"，经纪商时钟为 GMT\",\n         (SMT_BrokerGmtOffsetSeconds() >= 0 ? \"+\" : \"\"), DoubleToString(SMT_BrokerGmtOffsetSeconds() \u002F 3600.0, 1));\n   return true;\n}\n\n\u002F\u002F--- 有数据公布处于暂停窗口内时返回 true；reason 说明是哪一条\nbool SMT_IsNewsTime(string &reason)\n{\n   reason = \"\";\n   if(MQLInfoInteger(MQL_TESTER)) return false;   \u002F\u002F 测试器中没有网络\n   if(TimeCurrent() >= smt_nextDownload) SMT_Download();\n\n   datetime now = SMT_Now();\n   for(int i = 0; i \u003C ArraySize(smt_eventTime); i++)\n   {\n      if(now >= smt_eventTime[i] - SMT_MinutesBefore * 60 && now \u003C= smt_eventTime[i] + SMT_MinutesAfter * 60)\n      {\n         reason = smt_eventTitle[i];\n         return true;\n      }\n   }\n   return false;\n}\n","cpp",[52,201,202,210,216,222,228,234,240,245,251,258,264,270,276,282,288,293,299,305,311,317,323,329,334,340,346,352,358,364,370,376,382,388,394,400,405,411,417,423,429,434,440,445,450,456,462,467,473,479,485,491,497,503,509,515,521,527,533,538,543,549,555,560,566,572,577,583,589,595,601,607,612,618,624,630,636,641,646,652,658,664,669,675,681,687,692,698,704,710,716,722,728,734,740,746,752,758,764,770,776,782,788,794,800,806,811,817,823,829,835,841,846,851,857,863,868,874,880,886,891,897,903,908,914,919,925,931,936,941,947],{"__ignoreMap":54},[203,204,207],"span",{"class":205,"line":206},"line",1,[203,208,209],{},"\u002F\u002F+------------------------------------------------------------------+\n",[203,211,213],{"class":205,"line":212},2,[203,214,215],{},"\u002F\u002F| SMT_NewsFilter.mqh - MT4 和 MT5 新闻过滤器\n",[203,217,219],{"class":205,"line":218},3,[203,220,221],{},"\u002F\u002F| 数据：https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1（免费）\n",[203,223,225],{"class":205,"line":224},4,[203,226,227],{},"\u002F\u002F| 文档：https:\u002F\u002Fshowmytrades.com\u002Fdocs\u002Fapi\u002Feconomic-calendar\n",[203,229,231],{"class":205,"line":230},5,[203,232,233],{},"\u002F\u002F| 设置：工具 > 选项 > 智能交易系统 > 允许 WebRequest 访问\n",[203,235,237],{"class":205,"line":236},6,[203,238,239],{},"\u002F\u002F|       https:\u002F\u002Fshowmytrades.com\n",[203,241,243],{"class":205,"line":242},7,[203,244,209],{},[203,246,248],{"class":205,"line":247},8,[203,249,250],{},"#property strict\n",[203,252,254],{"class":205,"line":253},9,[203,255,257],{"emptyLinePlaceholder":256},true,"\n",[203,259,261],{"class":205,"line":260},10,[203,262,263],{},"input string SMT_Currencies     = \"EUR,USD\"; \u002F\u002F 新闻：货币（逗号分隔，留空 = 全部）\n",[203,265,267],{"class":205,"line":266},11,[203,268,269],{},"input string SMT_Impact         = \"high\";    \u002F\u002F 新闻：影响级别（high | high,medium | all）\n",[203,271,273],{"class":205,"line":272},12,[203,274,275],{},"input int    SMT_MinutesBefore  = 30;        \u002F\u002F 新闻：公布前暂停（分钟）\n",[203,277,279],{"class":205,"line":278},13,[203,280,281],{},"input int    SMT_MinutesAfter   = 30;        \u002F\u002F 新闻：公布后暂停（分钟）\n",[203,283,285],{"class":205,"line":284},14,[203,286,287],{},"input int    SMT_RefreshMinutes = 15;        \u002F\u002F 新闻：下载间隔（分钟，最少 15）\n",[203,289,291],{"class":205,"line":290},15,[203,292,257],{"emptyLinePlaceholder":256},[203,294,296],{"class":205,"line":295},16,[203,297,298],{},"datetime smt_eventTime[];\n",[203,300,302],{"class":205,"line":301},17,[203,303,304],{},"string   smt_eventTitle[];\n",[203,306,308],{"class":205,"line":307},18,[203,309,310],{},"datetime smt_nextDownload = 0;\n",[203,312,314],{"class":205,"line":313},19,[203,315,316],{},"long     smt_serverTime   = 0;     \u002F\u002F 上次下载时的服务器 UTC 时间（Unix 秒）\n",[203,318,320],{"class":205,"line":319},20,[203,321,322],{},"uint     smt_syncTick     = 0;     \u002F\u002F 当时的 GetTickCount()\n",[203,324,326],{"class":205,"line":325},21,[203,327,328],{},"bool     smt_clockSynced  = false;\n",[203,330,332],{"class":205,"line":331},22,[203,333,257],{"emptyLinePlaceholder":256},[203,335,337],{"class":205,"line":336},23,[203,338,339],{},"\u002F\u002F--- 真实的 UTC 时间：服务器时钟加上自下载以来经过的毫秒数，\n",[203,341,343],{"class":205,"line":342},24,[203,344,345],{},"\u002F\u002F--- 用 GetTickCount() 计数，它不受时区、夏令时和电脑时钟校正的影响\n",[203,347,349],{"class":205,"line":348},25,[203,350,351],{},"\u002F\u002F--- （uint 减法在计数器回绕后依然正确）。\n",[203,353,355],{"class":205,"line":354},26,[203,356,357],{},"\u002F\u002F--- 首次下载之前退回使用 TimeGMT()。绝不用 TimeCurrent()：\n",[203,359,361],{"class":205,"line":360},27,[203,362,363],{},"\u002F\u002F--- 经纪商的 GMT 偏移未知，而且会随夏令时跳变。\n",[203,365,367],{"class":205,"line":366},28,[203,368,369],{},"datetime SMT_Now()\n",[203,371,373],{"class":205,"line":372},29,[203,374,375],{},"{\n",[203,377,379],{"class":205,"line":378},30,[203,380,381],{},"   if(!smt_clockSynced) return TimeGMT();\n",[203,383,385],{"class":205,"line":384},31,[203,386,387],{},"   uint elapsedMs = GetTickCount() - smt_syncTick;\n",[203,389,391],{"class":205,"line":390},32,[203,392,393],{},"   return (datetime)(smt_serverTime + elapsedMs \u002F 1000);\n",[203,395,397],{"class":205,"line":396},33,[203,398,399],{},"}\n",[203,401,403],{"class":205,"line":402},34,[203,404,257],{"emptyLinePlaceholder":256},[203,406,408],{"class":205,"line":407},35,[203,409,410],{},"\u002F\u002F--- 经纪商时钟与 UTC 的差值，单位为秒（7200 = GMT+2）。它本身就很有用：\n",[203,412,414],{"class":205,"line":413},36,[203,415,416],{},"\u002F\u002F--- 大多数 EA 从来不知道这个值。TimeCurrent() 是最后一个 tick 的时间，\n",[203,418,420],{"class":205,"line":419},37,[203,421,422],{},"\u002F\u002F--- 所以请在开市时读取，不要在周末读取。\n",[203,424,426],{"class":205,"line":425},38,[203,427,428],{},"int SMT_BrokerGmtOffsetSeconds()\n",[203,430,432],{"class":205,"line":431},39,[203,433,375],{},[203,435,437],{"class":205,"line":436},40,[203,438,439],{},"   return (int)(TimeCurrent() - SMT_Now());\n",[203,441,443],{"class":205,"line":442},41,[203,444,399],{},[203,446,448],{"class":205,"line":447},42,[203,449,257],{"emptyLinePlaceholder":256},[203,451,453],{"class":205,"line":452},43,[203,454,455],{},"\u002F\u002F--- 第 n 个逗号之后的全部内容（CSV 的标题，标题本身可能含有逗号）\n",[203,457,459],{"class":205,"line":458},44,[203,460,461],{},"string SMT_AfterComma(const string line, const int n)\n",[203,463,465],{"class":205,"line":464},45,[203,466,375],{},[203,468,470],{"class":205,"line":469},46,[203,471,472],{},"   int pos = -1;\n",[203,474,476],{"class":205,"line":475},47,[203,477,478],{},"   for(int i = 0; i \u003C n; i++)\n",[203,480,482],{"class":205,"line":481},48,[203,483,484],{},"   {\n",[203,486,488],{"class":205,"line":487},49,[203,489,490],{},"      pos = StringFind(line, \",\", pos + 1);\n",[203,492,494],{"class":205,"line":493},50,[203,495,496],{},"      if(pos \u003C 0) return \"\";\n",[203,498,500],{"class":205,"line":499},51,[203,501,502],{},"   }\n",[203,504,506],{"class":205,"line":505},52,[203,507,508],{},"   string rest = StringSubstr(line, pos + 1);\n",[203,510,512],{"class":205,"line":511},53,[203,513,514],{},"   StringReplace(rest, \"\\\"\\\"\", \"\\\"\");\n",[203,516,518],{"class":205,"line":517},54,[203,519,520],{},"   if(StringLen(rest) >= 2 && StringGetCharacter(rest, 0) == '\"')\n",[203,522,524],{"class":205,"line":523},55,[203,525,526],{},"      rest = StringSubstr(rest, 1, StringLen(rest) - 2);\n",[203,528,530],{"class":205,"line":529},56,[203,531,532],{},"   return rest;\n",[203,534,536],{"class":205,"line":535},57,[203,537,399],{},[203,539,541],{"class":205,"line":540},58,[203,542,257],{"emptyLinePlaceholder":256},[203,544,546],{"class":205,"line":545},59,[203,547,548],{},"\u002F\u002F--- 下载未来 2 天的数据（时间窗口也包含过去 6 小时）\n",[203,550,552],{"class":205,"line":551},60,[203,553,554],{},"bool SMT_Download()\n",[203,556,558],{"class":205,"line":557},61,[203,559,375],{},[203,561,563],{"class":205,"line":562},62,[203,564,565],{},"   string url = \"https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1?format=csv&days=2&impact=\" + SMT_Impact;\n",[203,567,569],{"class":205,"line":568},63,[203,570,571],{},"   if(StringLen(SMT_Currencies) > 0) url += \"&currency=\" + SMT_Currencies;\n",[203,573,575],{"class":205,"line":574},64,[203,576,257],{"emptyLinePlaceholder":256},[203,578,580],{"class":205,"line":579},65,[203,581,582],{},"   char   post[], result[];\n",[203,584,586],{"class":205,"line":585},66,[203,587,588],{},"   string headers;\n",[203,590,592],{"class":205,"line":591},67,[203,593,594],{},"   ResetLastError();\n",[203,596,598],{"class":205,"line":597},68,[203,599,600],{},"   int status = WebRequest(\"GET\", url, \"\", NULL, 10000, post, 0, result, headers);\n",[203,602,604],{"class":205,"line":603},69,[203,605,606],{},"   if(status != 200)\n",[203,608,610],{"class":205,"line":609},70,[203,611,484],{},[203,613,615],{"class":205,"line":614},71,[203,616,617],{},"      Print(\"[SMT] 日历下载失败：HTTP \", status, \"，错误 \", GetLastError(),\n",[203,619,621],{"class":205,"line":620},72,[203,622,623],{},"            \"。https:\u002F\u002Fshowmytrades.com 是否已添加到 工具 > 选项 > 智能交易系统 > 允许 WebRequest？\");\n",[203,625,627],{"class":205,"line":626},73,[203,628,629],{},"      smt_nextDownload = TimeCurrent() + 60; \u002F\u002F 一分钟后重试\n",[203,631,633],{"class":205,"line":632},74,[203,634,635],{},"      return false;\n",[203,637,639],{"class":205,"line":638},75,[203,640,502],{},[203,642,644],{"class":205,"line":643},76,[203,645,257],{"emptyLinePlaceholder":256},[203,647,649],{"class":205,"line":648},77,[203,650,651],{},"   string body = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);\n",[203,653,655],{"class":205,"line":654},78,[203,656,657],{},"   string lines[];\n",[203,659,661],{"class":205,"line":660},79,[203,662,663],{},"   int n = StringSplit(body, '\\n', lines);\n",[203,665,667],{"class":205,"line":666},80,[203,668,257],{"emptyLinePlaceholder":256},[203,670,672],{"class":205,"line":671},81,[203,673,674],{},"   ArrayResize(smt_eventTime, 0);\n",[203,676,678],{"class":205,"line":677},82,[203,679,680],{},"   ArrayResize(smt_eventTitle, 0);\n",[203,682,684],{"class":205,"line":683},83,[203,685,686],{},"   for(int i = 1; i \u003C n; i++) \u002F\u002F 第 0 行是表头\n",[203,688,690],{"class":205,"line":689},84,[203,691,484],{},[203,693,695],{"class":205,"line":694},85,[203,696,697],{},"      \u002F\u002F 最后一行 \"# server_time=1760702400 server_time_iso=...\"：服务器的 UTC 时钟\n",[203,699,701],{"class":205,"line":700},86,[203,702,703],{},"      if(StringFind(lines[i], \"# server_time=\") == 0)\n",[203,705,707],{"class":205,"line":706},87,[203,708,709],{},"      {\n",[203,711,713],{"class":205,"line":712},88,[203,714,715],{},"         long serverTime = StringToInteger(StringSubstr(lines[i], 14, 10));\n",[203,717,719],{"class":205,"line":718},89,[203,720,721],{},"         if(serverTime > 0)\n",[203,723,725],{"class":205,"line":724},90,[203,726,727],{},"         {\n",[203,729,731],{"class":205,"line":730},91,[203,732,733],{},"            smt_serverTime  = serverTime;\n",[203,735,737],{"class":205,"line":736},92,[203,738,739],{},"            smt_syncTick    = GetTickCount();\n",[203,741,743],{"class":205,"line":742},93,[203,744,745],{},"            smt_clockSynced = true;\n",[203,747,749],{"class":205,"line":748},94,[203,750,751],{},"         }\n",[203,753,755],{"class":205,"line":754},95,[203,756,757],{},"         continue;\n",[203,759,761],{"class":205,"line":760},96,[203,762,763],{},"      }\n",[203,765,767],{"class":205,"line":766},97,[203,768,769],{},"      string f[];\n",[203,771,773],{"class":205,"line":772},98,[203,774,775],{},"      if(StringSplit(lines[i], ',', f) \u003C 8) continue;\n",[203,777,779],{"class":205,"line":778},99,[203,780,781],{},"      int k = ArraySize(smt_eventTime);\n",[203,783,785],{"class":205,"line":784},100,[203,786,787],{},"      ArrayResize(smt_eventTime, k + 1);\n",[203,789,791],{"class":205,"line":790},101,[203,792,793],{},"      ArrayResize(smt_eventTitle, k + 1);\n",[203,795,797],{"class":205,"line":796},102,[203,798,799],{},"      smt_eventTime[k]  = (datetime)StringToInteger(f[0]);      \u002F\u002F Unix 秒，UTC\n",[203,801,803],{"class":205,"line":802},103,[203,804,805],{},"      smt_eventTitle[k] = f[2] + \" \" + f[3] + \": \" + SMT_AfterComma(lines[i], 7);\n",[203,807,809],{"class":205,"line":808},104,[203,810,502],{},[203,812,814],{"class":205,"line":813},105,[203,815,816],{},"   smt_nextDownload = TimeCurrent() + MathMax(SMT_RefreshMinutes, 15) * 60;\n",[203,818,820],{"class":205,"line":819},106,[203,821,822],{},"   Print(\"[SMT] 日历：已加载 \", ArraySize(smt_eventTime), \" 个事件。当前 UTC \",\n",[203,824,826],{"class":205,"line":825},107,[203,827,828],{},"         TimeToString(SMT_Now(), TIME_DATE|TIME_MINUTES), \"，经纪商时钟为 GMT\",\n",[203,830,832],{"class":205,"line":831},108,[203,833,834],{},"         (SMT_BrokerGmtOffsetSeconds() >= 0 ? \"+\" : \"\"), DoubleToString(SMT_BrokerGmtOffsetSeconds() \u002F 3600.0, 1));\n",[203,836,838],{"class":205,"line":837},109,[203,839,840],{},"   return true;\n",[203,842,844],{"class":205,"line":843},110,[203,845,399],{},[203,847,849],{"class":205,"line":848},111,[203,850,257],{"emptyLinePlaceholder":256},[203,852,854],{"class":205,"line":853},112,[203,855,856],{},"\u002F\u002F--- 有数据公布处于暂停窗口内时返回 true；reason 说明是哪一条\n",[203,858,860],{"class":205,"line":859},113,[203,861,862],{},"bool SMT_IsNewsTime(string &reason)\n",[203,864,866],{"class":205,"line":865},114,[203,867,375],{},[203,869,871],{"class":205,"line":870},115,[203,872,873],{},"   reason = \"\";\n",[203,875,877],{"class":205,"line":876},116,[203,878,879],{},"   if(MQLInfoInteger(MQL_TESTER)) return false;   \u002F\u002F 测试器中没有网络\n",[203,881,883],{"class":205,"line":882},117,[203,884,885],{},"   if(TimeCurrent() >= smt_nextDownload) SMT_Download();\n",[203,887,889],{"class":205,"line":888},118,[203,890,257],{"emptyLinePlaceholder":256},[203,892,894],{"class":205,"line":893},119,[203,895,896],{},"   datetime now = SMT_Now();\n",[203,898,900],{"class":205,"line":899},120,[203,901,902],{},"   for(int i = 0; i \u003C ArraySize(smt_eventTime); i++)\n",[203,904,906],{"class":205,"line":905},121,[203,907,484],{},[203,909,911],{"class":205,"line":910},122,[203,912,913],{},"      if(now >= smt_eventTime[i] - SMT_MinutesBefore * 60 && now \u003C= smt_eventTime[i] + SMT_MinutesAfter * 60)\n",[203,915,917],{"class":205,"line":916},123,[203,918,709],{},[203,920,922],{"class":205,"line":921},124,[203,923,924],{},"         reason = smt_eventTitle[i];\n",[203,926,928],{"class":205,"line":927},125,[203,929,930],{},"         return true;\n",[203,932,934],{"class":205,"line":933},126,[203,935,763],{},[203,937,939],{"class":205,"line":938},127,[203,940,502],{},[203,942,944],{"class":205,"line":943},128,[203,945,946],{},"   return false;\n",[203,948,950],{"class":205,"line":949},129,[203,951,399],{},[18,953,954,955],{},"代码的流程是：按输入参数拼出请求，下载 CSV，每个事件只留一个时间戳和一个标签，最后回答 EA 唯一关心的问题：",[155,956,957],{},"此刻是否处在某条数据公布的窗口内？",[40,959,961],{"id":960},"第-3-步每次入场前先调用","🔌 第 3 步：每次入场前先调用",[18,963,964],{},"在 EA 里包含这个文件，开仓前先检查一下：",[45,966,969],{"className":197,"code":967,"filename":968,"language":199,"meta":54,"style":54},"#include \u003CSMT_NewsFilter.mqh>\n\nvoid OnTick()\n{\n   string news;\n   if(SMT_IsNewsTime(news))\n   {\n      Comment(\"新闻过滤生效中：\", news);\n      return;                       \u002F\u002F 新闻窗口内不开新仓\n   }\n   Comment(\"\");\n   \u002F\u002F ... 您的入场逻辑 ...\n}\n","YourEA.mq4",[52,970,971,976,980,985,989,994,999,1003,1008,1013,1017,1022,1027],{"__ignoreMap":54},[203,972,973],{"class":205,"line":206},[203,974,975],{},"#include \u003CSMT_NewsFilter.mqh>\n",[203,977,978],{"class":205,"line":212},[203,979,257],{"emptyLinePlaceholder":256},[203,981,982],{"class":205,"line":218},[203,983,984],{},"void OnTick()\n",[203,986,987],{"class":205,"line":224},[203,988,375],{},[203,990,991],{"class":205,"line":230},[203,992,993],{},"   string news;\n",[203,995,996],{"class":205,"line":236},[203,997,998],{},"   if(SMT_IsNewsTime(news))\n",[203,1000,1001],{"class":205,"line":242},[203,1002,484],{},[203,1004,1005],{"class":205,"line":247},[203,1006,1007],{},"      Comment(\"新闻过滤生效中：\", news);\n",[203,1009,1010],{"class":205,"line":253},[203,1011,1012],{},"      return;                       \u002F\u002F 新闻窗口内不开新仓\n",[203,1014,1015],{"class":205,"line":260},[203,1016,502],{},[203,1018,1019],{"class":205,"line":266},[203,1020,1021],{},"   Comment(\"\");\n",[203,1023,1024],{"class":205,"line":272},[203,1025,1026],{},"   \u002F\u002F ... 您的入场逻辑 ...\n",[203,1028,1029],{"class":205,"line":278},[203,1030,399],{},[18,1032,1033,1034,1037,1038,1041],{},"这五个输入参数（",[52,1035,1036],{},"SMT_Currencies","、",[52,1039,1040],{},"SMT_Impact"," 和三个分钟数）会和其他设置一起出现在 EA 的输入参数窗口里，每张图表都可以单独设置。",[18,1043,1044,1045,1048,1049,1052],{},"重新编译，把 EA 加载到图表上，再看「智能交易」选项卡：第一行应是 ",[52,1046,1047],{},"[SMT] 日历：已加载 N 个事件","。没什么数据的日子里 ",[52,1050,1051],{},"N"," 可能是零，这属于正常，不是出错。",[40,1054,1056],{"id":1055},"️-时间时钟与测试器","⏱️ 时间、时钟与测试器",[18,1058,1059],{},"过滤器能不能卡准时间触发，取决于三个细节。",[56,1061,1062,1070],{},[59,1063,1064],{},[62,1065,1066,1068],{},[65,1067],{},[65,1069],{},[75,1071,1072,1093,1110],{},[62,1073,1074,1079],{},[80,1075,1076],{},[30,1077,1078],{},"API 用 UTC，还会告诉您现在几点",[80,1080,1081,1084,1085,1088,1089,1092],{},[52,1082,1083],{},"ts"," 是 UTC 的 Unix 时间（秒）。每次下载的末尾都带有服务器自己的时钟（",[52,1086,1087],{},"# server_time=…","），",[52,1090,1091],{},"SMT_Now()"," 再用电脑的 tick 计数器接着往下走，这个计数器不受时区和夏令时影响。所以经纪商在哪个时区、哪里切换夏令时、VPS 的时区设得对不对，都无关紧要",[62,1094,1095,1103],{},[80,1096,1097],{},[30,1098,1099,1100],{},"绝不用 ",[52,1101,1102],{},"TimeCurrent()",[80,1104,1105,1106,1109],{},"经纪商时间和 UTC 之间有个偏移，EA 无从知道，而且一年要变两次。",[52,1107,1108],{},"SMT_BrokerGmtOffsetSeconds()"," 可以替您测出来，单独用也很方便；但过滤器从不依赖它",[62,1111,1112,1117],{},[80,1113,1114],{},[30,1115,1116],{},"策略测试器不能联网",[80,1118,1119,1120,1122],{},"测试器里 ",[52,1121,148],{}," 是禁用的，所以回测时这个函数总是返回「没有新闻」。想在历史数据上测试新闻过滤器，得准备一份本地的历史事件文件，那是另一回事了",[40,1124,1126],{"id":1125},"️-调整设置","🎛️ 调整设置",[1128,1129,1130,1145,1157,1166],"ul",{},[1131,1132,1133,1136,1137,1140,1141,1144],"li",{},[30,1134,1135],{},"持仓","。代码只拦",[155,1138,1139],{},"新的","入场。数据公布前要不要平仓、收紧或放宽止损，是策略层面的决定；什么时候动手，同样可以问 ",[52,1142,1143],{},"SMT_IsNewsTime()","。",[1131,1146,1147,1150,1151,1153,1154,1144],{},[30,1148,1149],{},"只看货币对的一边","。如果您只在意美元新闻，把 ",[52,1152,1036],{}," 设为 ",[52,1155,1156],{},"USD",[1131,1158,1159,1144,1162,1165],{},[30,1160,1161],{},"加上中影响",[52,1163,1164],{},"SMT_Impact = \"high,medium\""," 会让事件数量增加到大约三倍。适合剥头皮，对波段系统来说往往过多。",[1131,1167,1168,1144,1171,1174],{},[30,1169,1170],{},"节假日",[52,1172,1173],{},"impact=all"," 会把市场节假日当作全天事件一并返回。多数过滤器用不上，默认设置不包含。",[40,1176,1178],{"id":1177},"核对数据","🔍 核对数据",[18,1180,1181,1182,1186],{},"这份日历也有网页版：",[21,1183,1185],{"href":1184},"\u002Ftools\u002Feconomic-calendar","财经日历","。EA 提示因“USD high: Nonfarm Payrolls”暂停时，可以去那里找到这条事件，看到预期值，公布后还能看到公布值。",[40,1188,1190],{"id":1189},"常见问题","❓ 常见问题",[18,1192,1193,1196,1197,1201],{},[30,1194,1195],{},"需要 ShowMyTrades 账户吗？","\n不需要。财经日历 API 免费开放，不用密钥。想要一个",[21,1198,1200],{"href":1199},"\u002Fguides\u002Fhow-to-verify-your-account","经验证的公开页面","的话，可以在 ShowMyTrades 上追踪您的账户，但新闻过滤器与此无关。",[18,1203,1204,1207],{},[30,1205,1206],{},"它会产生多少流量？","\n每张图表每 15 分钟请求一次，每次几 KB。一个终端跑十个 EA，每小时也就 40 个请求。上限是每个 IP 地址每分钟 600 个。",[18,1209,1210,1213,1214,1217],{},[30,1211,1212],{},"为什么不直接读取 Forex Factory？","\n可以，很多 EA 就是这么做的。但它的周数据文件每个 IP 地址每五分钟只能下载两次，一台 VPS 上开很多终端就会撞上限制，而且没有 ",[52,1215,1216],{},"actual","（公布值）。ShowMyTrades 的端点在服务器端就按货币和影响级别筛好，只返回几 KB，本来就是为轮询设计的。",[18,1219,1220,1223],{},[30,1221,1222],{},"我的 VPS 时钟不准或时区设错了，过滤器还能准时触发吗？","\n能。每次下载时服务器都会告诉 EA 当前的 UTC 时间，之后 EA 用自己的计时接着走。VPS 时区设错，或者快慢几分钟，都没有影响。VPS 切换夏令时也一样：EA 计算经过的时间靠的是 tick 计数器，不是时钟。",[18,1225,1226,1229,1230,1233],{},[30,1227,1228],{},"如果下载失败怎么办？","\n过滤器会沿用上一次加载的日历，一分钟后重试。如果一次都没加载成功（刚启动、没联网），它会返回「没有新闻」，EA 就照常交易。不想这样的话，检查 ",[52,1231,1232],{},"ArraySize(smt_eventTime) == 0","，自己决定怎么处理。",[18,1235,1236,1239,1240,1244],{},[30,1237,1238],{},"同一个文件能在 MT5 中使用吗？","\n能，原样可用。MT5 还有",[21,1241,1243],{"href":1242},"\u002Fguides\u002Fnews-filter-mt5-expert-advisor-built-in-calendar-or-api","自带的日历","，按需选用即可。",[40,1246,1248],{"id":1247},"相关内容","🔗 相关内容",[1128,1250,1251,1257,1262,1269],{},[1131,1252,1253,1256],{},[21,1254,1255],{"href":36},"财经日历 API 参考","：所有参数、JSON 格式、用于 cTrader 的 WebSocket",[1131,1258,1259],{},[21,1260,1261],{"href":1242},"MT5 中的新闻过滤器：内置日历还是 API",[1131,1263,1264,1268],{},[21,1265,1267],{"href":1266},"\u002Fguides\u002Fadd-a-news-filter-to-a-ctrader-cbot","cTrader cBot 的新闻过滤器","：同样的思路，用在无法使用 HTTP 的环境里",[1131,1270,1271,1275],{},[21,1272,1274],{"href":1273},"\u002Fguides\u002Flive-ea-results-on-your-sales-page","在销售页面上展示 EA 实时交易结果","：EA 跑出成绩后，就把它展示出来",[1277,1278],"hr",{},[18,1280,1281,1282,1286],{},"整个过滤器不过是一页代码加一个网址。如果您的 EA 已经",[21,1283,1285],{"href":1284},"\u002Fsignup","在 ShowMyTrades 上追踪","，它在新闻前后避开的那些交易，就会体现在少吃的那段回撤上。",[1288,1289,1290],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":54,"searchDepth":212,"depth":212,"links":1292},[1293,1294,1295,1296,1297,1298,1299,1300,1301],{"id":42,"depth":212,"text":43},{"id":141,"depth":212,"text":142},{"id":178,"depth":212,"text":179},{"id":960,"depth":212,"text":961},{"id":1055,"depth":212,"text":1056},{"id":1125,"depth":212,"text":1126},{"id":1177,"depth":212,"text":1178},{"id":1189,"depth":212,"text":1190},{"id":1247,"depth":212,"text":1248},"2026-10-17T00:00:00.000Z","MetaTrader 4 的 EA 没有财经日历可用。本指南教您十分钟补上：一个免费的财经日历 API、一个包含文件，每次入场前调用一个函数即可。附完整 MQL4 代码，无需 API 密钥。",false,"md",{"src":1307},"\u002Fimages\u002Fblog\u002Fadd-a-news-filter-to-your-mt4-expert-advisor-hero.svg",{},"\u002Fzh\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor",{"title":5,"description":1303},"zh\u002F3.guides\u002F41.add-a-news-filter-to-your-mt4-expert-advisor",null,"N6rRnmILHcPXewazfWfAH5rZbYYFXH8JPfnWvqvj_o4",[1315,1320],{"title":1316,"path":1317,"stem":1318,"description":1319,"children":-1},"如何在一个页面上展示多个交易账户","\u002Fzh\u002Fguides\u002Fshow-multiple-trading-accounts-on-one-page","zh\u002F3.guides\u002F40.show-multiple-trading-accounts-on-one-page","在同一个网页上展示多个账户、多个 EA 或多家经纪商：每个账户一行的实时对比表、并排的图表，或者一条合并曲线。附代码。",{"title":1321,"path":1322,"stem":1323,"description":1324,"children":-1},"MT5 新闻过滤器：用内置日历还是 API？","\u002Fzh\u002Fguides\u002Fnews-filter-mt5-expert-advisor-built-in-calendar-or-api","zh\u002F3.guides\u002F42.news-filter-mt5-expert-advisor-built-in-calendar-or-api","MetaTrader 5 自带财经日历，EA 无需下载就能读取。什么时候够用、什么时候不够，以及怎样改用免费的日历 API，两种方式都附 MQL5 代码。",[1326,1328],{"path":1327},"\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor",{"path":1309},1793898039324]