[{"data":1,"prerenderedAt":1516},["ShallowReactive",2],{"\u002Fguides\u002Fadd-a-news-filter-to-a-ctrader-cbot":3,"\u002Fguides\u002Fadd-a-news-filter-to-a-ctrader-cbot-surround":1505},{"id":4,"title":5,"authors":6,"badge":12,"body":14,"date":1493,"description":1494,"draft":1495,"extension":1496,"image":1497,"meta":1499,"navigation":218,"path":1500,"seo":1501,"stem":1502,"updated":1503,"__hash__":1504},"posts\u002F3.guides\u002F43.add-a-news-filter-to-a-ctrader-cbot.md","How to Add a News Filter to a cTrader cBot (Cloud Included)",[7],{"name":8,"to":9,"avatar":10},"ShowMyTrades Team","https:\u002F\u002Fshowmytrades.com",{"src":11},"\u002Ficon-512.png",{"label":13},"Automation",{"type":15,"value":16,"toc":1484},"minimark",[17,39,52,57,67,151,162,166,173,1272,1295,1299,1333,1337,1354,1358,1386,1390,1396,1408,1428,1439,1443,1469,1472,1480],[18,19,20,21,25,26,29,30,34,35,38],"p",{},"A news filter in a cBot runs into two walls that MetaTrader developers never see. First, the cTrader Automate API has ",[22,23,24],"strong",{},"no economic calendar",": the platform shows one, a cBot cannot read it. Second, most people run their cBots in ",[22,27,28],{},"cTrader Cloud",", where a cBot cannot make HTTP requests at all: ",[31,32,33],"code",{},"Http.Get()"," comes back with status ",[31,36,37],{},"503",".",[18,40,41,42,45,46,51],{},"The cloud does allow one thing: a ",[22,43,44],{},"WebSocket on port 25345",". So that is how the free ",[47,48,50],"a",{"href":49},"\u002Fdocs\u002Fapi\u002Feconomic-calendar","ShowMyTrades economic calendar"," reaches a cBot. This guide gives you the whole thing: the idea, the protocol, and a cBot you can compile as it is.",[53,54,56],"h2",{"id":55},"how-it-works","🧭 How It Works",[58,59,64],"pre",{"className":60,"code":62,"language":63},[61],"language-text","cBot (desktop or cloud)                      ShowMyTrades\n┌──────────────────────────┐                 ┌─────────────────────────┐\n│ every 15 min:            │  ws:\u002F\u002F…:25345   │ same calendar as        │\n│  open socket, send       │ ──────────────► │ \u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1 │\n│  \"currency=EUR,USD&      │                 │ cached, filtered,       │\n│   impact=high&days=2&    │ ◄────────────── │ a few kilobytes         │\n│   format=csv\"            │   CSV text      └─────────────────────────┘\n│  parse, close            │\n│ every tick: IsNewsTime() │\n└──────────────────────────┘\n","text",[31,65,62],{"__ignoreMap":66},"",[68,69,70,81],"table",{},[71,72,73],"thead",{},[74,75,76,79],"tr",{},[77,78],"th",{},[77,80],{},[82,83,84,95,116,124,135,143],"tbody",{},[74,85,86,90],{},[87,88,89],"td",{},"Address",[87,91,92],{},[31,93,94],{},"ws:\u002F\u002Fshowmytrades.com:25345\u002F",[74,96,97,100],{},[87,98,99],{},"You send",[87,101,102,103,106,107,106,110,106,113],{},"One line of text with the same parameters as the HTTP API: ",[31,104,105],{},"currency",", ",[31,108,109],{},"impact",[31,111,112],{},"days",[31,114,115],{},"format",[74,117,118,121],{},[87,119,120],{},"You get",[87,122,123],{},"One text frame with the CSV (or JSON) body",[74,125,126,129],{},[87,127,128],{},"Access rights",[87,130,131,134],{},[31,132,133],{},"AccessRights.None"," is enough, so the cBot can be published",[74,136,137,140],{},[87,138,139],{},"Key or account",[87,141,142],{},"None",[74,144,145,148],{},[87,146,147],{},"Desktop fallback",[87,149,150],{},"Plain HTTP works there; the code below falls back to it if the socket fails",[18,152,153,154,157,158,161],{},"The socket uses ",[31,155,156],{},"ws:\u002F\u002F",", not ",[31,159,160],{},"wss:\u002F\u002F",", because that is what cTrader Cloud connects with. Nothing about you or your account travels on it: only the four parameters.",[53,163,165],{"id":164},"the-cbot","📄 The cBot",[18,167,168,169,172],{},"Create a new cBot in cTrader Automate, replace its code with this, and build. On the desktop it compiles against ",[31,170,171],{},"cTrader.Automate"," from NuGet; in the cloud it runs as it is.",[58,174,179],{"className":175,"code":176,"filename":177,"language":178,"meta":66,"style":66},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F News filter for cTrader (desktop and cloud), data by ShowMyTrades, free, no key.\n\u002F\u002F Docs: https:\u002F\u002Fshowmytrades.com\u002Fdocs\u002Fapi\u002Feconomic-calendar\nusing System;\nusing System.Collections.Generic;\nusing cAlgo.API;\n\nnamespace cAlgo.Robots\n{\n    [Robot(AccessRights = AccessRights.None, TimeZone = TimeZones.UTC)]\n    public class SmtNewsFilterBot : Robot\n    {\n        [Parameter(\"News: currencies (comma-separated, empty = all)\", DefaultValue = \"EUR,USD\")]\n        public string NewsCurrencies { get; set; }\n\n        [Parameter(\"News: impact (high | high,medium | all)\", DefaultValue = \"high\")]\n        public string NewsImpact { get; set; }\n\n        [Parameter(\"News: minutes before\", DefaultValue = 30, MinValue = 0)]\n        public int MinutesBefore { get; set; }\n\n        [Parameter(\"News: minutes after\", DefaultValue = 30, MinValue = 0)]\n        public int MinutesAfter { get; set; }\n\n        private const string CalendarWs = \"ws:\u002F\u002Fshowmytrades.com:25345\u002F\";\n        private const string CalendarHttp = \"https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1?\";\n        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n\n        private readonly List\u003CKeyValuePair\u003CDateTime, string>> _events = new List\u003CKeyValuePair\u003CDateTime, string>>();\n        private WebSocketClient _ws;\n        private bool _received;\n\n        protected override void OnStart()\n        {\n            DownloadCalendar();\n            Timer.Start(TimeSpan.FromMinutes(15));\n        }\n\n        protected override void OnTimer()\n        {\n            DownloadCalendar();\n        }\n\n        protected override void OnTick()\n        {\n            string reason;\n            if (IsNewsTime(out reason))\n            {\n                \u002F\u002F No new entries during the news window. Manage open positions as you prefer.\n                return;\n            }\n\n            \u002F\u002F ... your entry logic ...\n        }\n\n        protected override void OnStop()\n        {\n            CloseSocket();\n        }\n\n        \u002F\u002F True while a release is inside the pause window; reason says which one.\n        \u002F\u002F Server.TimeInUtc is cTrader's own UTC clock: the platform knows the broker's offset,\n        \u002F\u002F so it is safe to compare with the calendar (ParseCsv checks it against the server).\n        public bool IsNewsTime(out string reason)\n        {\n            DateTime now = Server.TimeInUtc;\n            foreach (KeyValuePair\u003CDateTime, string> e in _events)\n            {\n                if (now >= e.Key.AddMinutes(-MinutesBefore) && now \u003C= e.Key.AddMinutes(MinutesAfter))\n                {\n                    reason = e.Value;\n                    return true;\n                }\n            }\n            reason = null;\n            return false;\n        }\n\n        private string Query()\n        {\n            string q = \"format=csv&days=2&impact=\" + NewsImpact;\n            if (!string.IsNullOrWhiteSpace(NewsCurrencies)) q += \"&currency=\" + NewsCurrencies.Replace(\" \", \"\");\n            return q;\n        }\n\n        private void DownloadCalendar()\n        {\n            if (RunningMode != RunningMode.RealTime) return; \u002F\u002F no network in the backtester\n\n            \u002F\u002F cTrader Cloud blocks HTTP from cBots and allows only a WebSocket on port 25345,\n            \u002F\u002F so the socket is the primary path everywhere; HTTP is the fallback on the desktop.\n            try\n            {\n                CloseSocket();\n                _received = false;\n                _ws = new WebSocketClient(new WebSocketClientOptions { Timeout = TimeSpan.FromSeconds(15) });\n                _ws.Connected += OnSocketConnected;\n                _ws.TextReceived += OnSocketText;\n                _ws.Disconnected += OnSocketDisconnected;\n                _ws.Connect(new Uri(CalendarWs));\n            }\n            catch (Exception ex)\n            {\n                Print(\"[SMT] calendar socket failed: {0}\", ex.Message);\n                DownloadOverHttp();\n            }\n        }\n\n        private void OnSocketConnected(WebSocketClientConnectedEventArgs args)\n        {\n            try { _ws.Send(Query()); }\n            catch (Exception ex) { Print(\"[SMT] calendar send failed: {0}\", ex.Message); }\n        }\n\n        private void OnSocketText(WebSocketClientTextReceivedEventArgs args)\n        {\n            _received = true;\n            if (args.Text.StartsWith(\"Error:\")) Print(\"[SMT] calendar: {0}\", args.Text);\n            else ParseCsv(args.Text);\n            CloseSocket();\n        }\n\n        private void OnSocketDisconnected(WebSocketClientDisconnectEventArgs args)\n        {\n            if (!_received) DownloadOverHttp(); \u002F\u002F desktop only: the cloud answers 503\n        }\n\n        private void DownloadOverHttp()\n        {\n            try\n            {\n                HttpResponse r = Http.Get(CalendarHttp + Query());\n                if (r.IsSuccessful) ParseCsv(r.Body);\n                else Print(\"[SMT] calendar HTTP failed: {0}\", r.StatusCode);\n            }\n            catch (Exception ex) { Print(\"[SMT] calendar HTTP failed: {0}\", ex.Message); }\n        }\n\n        private void CloseSocket()\n        {\n            WebSocketClient ws = _ws;\n            _ws = null;\n            if (ws == null) return;\n            try { ws.Connected -= OnSocketConnected; ws.TextReceived -= OnSocketText; ws.Disconnected -= OnSocketDisconnected; } catch (Exception) { }\n            try { if (ws.State == WebSocketClientState.Open) ws.Close(WebSocketClientCloseStatus.NormalClosure, \"done\"); } catch (Exception) { }\n            try { ws.Dispose(); } catch (Exception) { }\n        }\n\n        \u002F\u002F CSV: ts,time,currency,impact,forecast,previous,actual,title (title last, may be quoted),\n        \u002F\u002F then one last line \"# server_time=\u003Cunix> server_time_iso=\u003Ciso>\" with the server's UTC clock.\n        private void ParseCsv(string csv)\n        {\n            List\u003CKeyValuePair\u003CDateTime, string>> list = new List\u003CKeyValuePair\u003CDateTime, string>>();\n            foreach (string raw in csv.Split('\\n'))\n            {\n                string line = raw.TrimEnd('\\r');\n                if (line.Length == 0 || line.StartsWith(\"ts,\")) continue;\n                if (line.StartsWith(\"# server_time=\"))\n                {\n                    long serverTime;\n                    if (long.TryParse(line.Substring(14, 10), out serverTime))\n                    {\n                        double skew = (Server.TimeInUtc - Epoch.AddSeconds(serverTime)).TotalSeconds;\n                        if (Math.Abs(skew) > 60) Print(\"[SMT] warning: cTrader's UTC clock is {0:F0} s away from the calendar server\", skew);\n                    }\n                    continue;\n                }\n                string[] f = line.Split(',');\n                if (f.Length \u003C 8) continue;\n                long ts;\n                if (!long.TryParse(f[0], out ts)) continue;\n                DateTime at = Epoch.AddSeconds(ts);\n                string title = AfterComma(line, 7).Replace(\"\\\"\\\"\", \"\\\"\");\n                if (title.Length >= 2 && title[0] == '\"') title = title.Substring(1, title.Length - 2);\n                list.Add(new KeyValuePair\u003CDateTime, string>(at, f[2] + \" \" + f[3] + \": \" + title));\n            }\n            _events.Clear();\n            _events.AddRange(list);\n            Print(\"[SMT] calendar: {0} events loaded\", _events.Count);\n        }\n\n        private static string AfterComma(string line, int n)\n        {\n            int pos = -1;\n            for (int i = 0; i \u003C n; i++)\n            {\n                pos = line.IndexOf(',', pos + 1);\n                if (pos \u003C 0) return \"\";\n            }\n            return line.Substring(pos + 1);\n        }\n    }\n}\n","SmtNewsFilterBot.cs","csharp",[31,180,181,189,195,201,207,213,220,226,232,238,244,250,256,262,267,273,279,284,290,296,301,307,313,318,324,330,336,341,347,353,359,364,370,376,382,388,394,399,405,410,415,420,425,431,436,442,448,454,460,466,472,477,483,488,493,499,504,510,515,520,526,532,538,544,549,555,561,566,572,578,584,590,596,601,607,613,618,623,629,634,640,646,652,657,662,668,673,679,684,690,696,702,707,713,719,725,731,737,743,749,754,760,765,771,777,782,787,792,798,803,809,815,820,825,831,836,842,848,854,859,864,869,875,880,886,891,896,902,907,912,917,923,929,935,940,946,951,956,962,967,973,979,985,991,997,1003,1008,1013,1019,1025,1031,1036,1042,1048,1053,1059,1065,1071,1076,1082,1088,1094,1100,1106,1112,1118,1123,1129,1135,1141,1147,1153,1159,1165,1171,1176,1182,1188,1194,1199,1204,1210,1215,1221,1227,1232,1238,1244,1249,1255,1260,1266],{"__ignoreMap":66},[182,183,186],"span",{"class":184,"line":185},"line",1,[182,187,188],{},"\u002F\u002F News filter for cTrader (desktop and cloud), data by ShowMyTrades, free, no key.\n",[182,190,192],{"class":184,"line":191},2,[182,193,194],{},"\u002F\u002F Docs: https:\u002F\u002Fshowmytrades.com\u002Fdocs\u002Fapi\u002Feconomic-calendar\n",[182,196,198],{"class":184,"line":197},3,[182,199,200],{},"using System;\n",[182,202,204],{"class":184,"line":203},4,[182,205,206],{},"using System.Collections.Generic;\n",[182,208,210],{"class":184,"line":209},5,[182,211,212],{},"using cAlgo.API;\n",[182,214,216],{"class":184,"line":215},6,[182,217,219],{"emptyLinePlaceholder":218},true,"\n",[182,221,223],{"class":184,"line":222},7,[182,224,225],{},"namespace cAlgo.Robots\n",[182,227,229],{"class":184,"line":228},8,[182,230,231],{},"{\n",[182,233,235],{"class":184,"line":234},9,[182,236,237],{},"    [Robot(AccessRights = AccessRights.None, TimeZone = TimeZones.UTC)]\n",[182,239,241],{"class":184,"line":240},10,[182,242,243],{},"    public class SmtNewsFilterBot : Robot\n",[182,245,247],{"class":184,"line":246},11,[182,248,249],{},"    {\n",[182,251,253],{"class":184,"line":252},12,[182,254,255],{},"        [Parameter(\"News: currencies (comma-separated, empty = all)\", DefaultValue = \"EUR,USD\")]\n",[182,257,259],{"class":184,"line":258},13,[182,260,261],{},"        public string NewsCurrencies { get; set; }\n",[182,263,265],{"class":184,"line":264},14,[182,266,219],{"emptyLinePlaceholder":218},[182,268,270],{"class":184,"line":269},15,[182,271,272],{},"        [Parameter(\"News: impact (high | high,medium | all)\", DefaultValue = \"high\")]\n",[182,274,276],{"class":184,"line":275},16,[182,277,278],{},"        public string NewsImpact { get; set; }\n",[182,280,282],{"class":184,"line":281},17,[182,283,219],{"emptyLinePlaceholder":218},[182,285,287],{"class":184,"line":286},18,[182,288,289],{},"        [Parameter(\"News: minutes before\", DefaultValue = 30, MinValue = 0)]\n",[182,291,293],{"class":184,"line":292},19,[182,294,295],{},"        public int MinutesBefore { get; set; }\n",[182,297,299],{"class":184,"line":298},20,[182,300,219],{"emptyLinePlaceholder":218},[182,302,304],{"class":184,"line":303},21,[182,305,306],{},"        [Parameter(\"News: minutes after\", DefaultValue = 30, MinValue = 0)]\n",[182,308,310],{"class":184,"line":309},22,[182,311,312],{},"        public int MinutesAfter { get; set; }\n",[182,314,316],{"class":184,"line":315},23,[182,317,219],{"emptyLinePlaceholder":218},[182,319,321],{"class":184,"line":320},24,[182,322,323],{},"        private const string CalendarWs = \"ws:\u002F\u002Fshowmytrades.com:25345\u002F\";\n",[182,325,327],{"class":184,"line":326},25,[182,328,329],{},"        private const string CalendarHttp = \"https:\u002F\u002Fshowmytrades.com\u002Fapi\u002Fpublic\u002Fcalendar\u002Fv1?\";\n",[182,331,333],{"class":184,"line":332},26,[182,334,335],{},"        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n",[182,337,339],{"class":184,"line":338},27,[182,340,219],{"emptyLinePlaceholder":218},[182,342,344],{"class":184,"line":343},28,[182,345,346],{},"        private readonly List\u003CKeyValuePair\u003CDateTime, string>> _events = new List\u003CKeyValuePair\u003CDateTime, string>>();\n",[182,348,350],{"class":184,"line":349},29,[182,351,352],{},"        private WebSocketClient _ws;\n",[182,354,356],{"class":184,"line":355},30,[182,357,358],{},"        private bool _received;\n",[182,360,362],{"class":184,"line":361},31,[182,363,219],{"emptyLinePlaceholder":218},[182,365,367],{"class":184,"line":366},32,[182,368,369],{},"        protected override void OnStart()\n",[182,371,373],{"class":184,"line":372},33,[182,374,375],{},"        {\n",[182,377,379],{"class":184,"line":378},34,[182,380,381],{},"            DownloadCalendar();\n",[182,383,385],{"class":184,"line":384},35,[182,386,387],{},"            Timer.Start(TimeSpan.FromMinutes(15));\n",[182,389,391],{"class":184,"line":390},36,[182,392,393],{},"        }\n",[182,395,397],{"class":184,"line":396},37,[182,398,219],{"emptyLinePlaceholder":218},[182,400,402],{"class":184,"line":401},38,[182,403,404],{},"        protected override void OnTimer()\n",[182,406,408],{"class":184,"line":407},39,[182,409,375],{},[182,411,413],{"class":184,"line":412},40,[182,414,381],{},[182,416,418],{"class":184,"line":417},41,[182,419,393],{},[182,421,423],{"class":184,"line":422},42,[182,424,219],{"emptyLinePlaceholder":218},[182,426,428],{"class":184,"line":427},43,[182,429,430],{},"        protected override void OnTick()\n",[182,432,434],{"class":184,"line":433},44,[182,435,375],{},[182,437,439],{"class":184,"line":438},45,[182,440,441],{},"            string reason;\n",[182,443,445],{"class":184,"line":444},46,[182,446,447],{},"            if (IsNewsTime(out reason))\n",[182,449,451],{"class":184,"line":450},47,[182,452,453],{},"            {\n",[182,455,457],{"class":184,"line":456},48,[182,458,459],{},"                \u002F\u002F No new entries during the news window. Manage open positions as you prefer.\n",[182,461,463],{"class":184,"line":462},49,[182,464,465],{},"                return;\n",[182,467,469],{"class":184,"line":468},50,[182,470,471],{},"            }\n",[182,473,475],{"class":184,"line":474},51,[182,476,219],{"emptyLinePlaceholder":218},[182,478,480],{"class":184,"line":479},52,[182,481,482],{},"            \u002F\u002F ... your entry logic ...\n",[182,484,486],{"class":184,"line":485},53,[182,487,393],{},[182,489,491],{"class":184,"line":490},54,[182,492,219],{"emptyLinePlaceholder":218},[182,494,496],{"class":184,"line":495},55,[182,497,498],{},"        protected override void OnStop()\n",[182,500,502],{"class":184,"line":501},56,[182,503,375],{},[182,505,507],{"class":184,"line":506},57,[182,508,509],{},"            CloseSocket();\n",[182,511,513],{"class":184,"line":512},58,[182,514,393],{},[182,516,518],{"class":184,"line":517},59,[182,519,219],{"emptyLinePlaceholder":218},[182,521,523],{"class":184,"line":522},60,[182,524,525],{},"        \u002F\u002F True while a release is inside the pause window; reason says which one.\n",[182,527,529],{"class":184,"line":528},61,[182,530,531],{},"        \u002F\u002F Server.TimeInUtc is cTrader's own UTC clock: the platform knows the broker's offset,\n",[182,533,535],{"class":184,"line":534},62,[182,536,537],{},"        \u002F\u002F so it is safe to compare with the calendar (ParseCsv checks it against the server).\n",[182,539,541],{"class":184,"line":540},63,[182,542,543],{},"        public bool IsNewsTime(out string reason)\n",[182,545,547],{"class":184,"line":546},64,[182,548,375],{},[182,550,552],{"class":184,"line":551},65,[182,553,554],{},"            DateTime now = Server.TimeInUtc;\n",[182,556,558],{"class":184,"line":557},66,[182,559,560],{},"            foreach (KeyValuePair\u003CDateTime, string> e in _events)\n",[182,562,564],{"class":184,"line":563},67,[182,565,453],{},[182,567,569],{"class":184,"line":568},68,[182,570,571],{},"                if (now >= e.Key.AddMinutes(-MinutesBefore) && now \u003C= e.Key.AddMinutes(MinutesAfter))\n",[182,573,575],{"class":184,"line":574},69,[182,576,577],{},"                {\n",[182,579,581],{"class":184,"line":580},70,[182,582,583],{},"                    reason = e.Value;\n",[182,585,587],{"class":184,"line":586},71,[182,588,589],{},"                    return true;\n",[182,591,593],{"class":184,"line":592},72,[182,594,595],{},"                }\n",[182,597,599],{"class":184,"line":598},73,[182,600,471],{},[182,602,604],{"class":184,"line":603},74,[182,605,606],{},"            reason = null;\n",[182,608,610],{"class":184,"line":609},75,[182,611,612],{},"            return false;\n",[182,614,616],{"class":184,"line":615},76,[182,617,393],{},[182,619,621],{"class":184,"line":620},77,[182,622,219],{"emptyLinePlaceholder":218},[182,624,626],{"class":184,"line":625},78,[182,627,628],{},"        private string Query()\n",[182,630,632],{"class":184,"line":631},79,[182,633,375],{},[182,635,637],{"class":184,"line":636},80,[182,638,639],{},"            string q = \"format=csv&days=2&impact=\" + NewsImpact;\n",[182,641,643],{"class":184,"line":642},81,[182,644,645],{},"            if (!string.IsNullOrWhiteSpace(NewsCurrencies)) q += \"&currency=\" + NewsCurrencies.Replace(\" \", \"\");\n",[182,647,649],{"class":184,"line":648},82,[182,650,651],{},"            return q;\n",[182,653,655],{"class":184,"line":654},83,[182,656,393],{},[182,658,660],{"class":184,"line":659},84,[182,661,219],{"emptyLinePlaceholder":218},[182,663,665],{"class":184,"line":664},85,[182,666,667],{},"        private void DownloadCalendar()\n",[182,669,671],{"class":184,"line":670},86,[182,672,375],{},[182,674,676],{"class":184,"line":675},87,[182,677,678],{},"            if (RunningMode != RunningMode.RealTime) return; \u002F\u002F no network in the backtester\n",[182,680,682],{"class":184,"line":681},88,[182,683,219],{"emptyLinePlaceholder":218},[182,685,687],{"class":184,"line":686},89,[182,688,689],{},"            \u002F\u002F cTrader Cloud blocks HTTP from cBots and allows only a WebSocket on port 25345,\n",[182,691,693],{"class":184,"line":692},90,[182,694,695],{},"            \u002F\u002F so the socket is the primary path everywhere; HTTP is the fallback on the desktop.\n",[182,697,699],{"class":184,"line":698},91,[182,700,701],{},"            try\n",[182,703,705],{"class":184,"line":704},92,[182,706,453],{},[182,708,710],{"class":184,"line":709},93,[182,711,712],{},"                CloseSocket();\n",[182,714,716],{"class":184,"line":715},94,[182,717,718],{},"                _received = false;\n",[182,720,722],{"class":184,"line":721},95,[182,723,724],{},"                _ws = new WebSocketClient(new WebSocketClientOptions { Timeout = TimeSpan.FromSeconds(15) });\n",[182,726,728],{"class":184,"line":727},96,[182,729,730],{},"                _ws.Connected += OnSocketConnected;\n",[182,732,734],{"class":184,"line":733},97,[182,735,736],{},"                _ws.TextReceived += OnSocketText;\n",[182,738,740],{"class":184,"line":739},98,[182,741,742],{},"                _ws.Disconnected += OnSocketDisconnected;\n",[182,744,746],{"class":184,"line":745},99,[182,747,748],{},"                _ws.Connect(new Uri(CalendarWs));\n",[182,750,752],{"class":184,"line":751},100,[182,753,471],{},[182,755,757],{"class":184,"line":756},101,[182,758,759],{},"            catch (Exception ex)\n",[182,761,763],{"class":184,"line":762},102,[182,764,453],{},[182,766,768],{"class":184,"line":767},103,[182,769,770],{},"                Print(\"[SMT] calendar socket failed: {0}\", ex.Message);\n",[182,772,774],{"class":184,"line":773},104,[182,775,776],{},"                DownloadOverHttp();\n",[182,778,780],{"class":184,"line":779},105,[182,781,471],{},[182,783,785],{"class":184,"line":784},106,[182,786,393],{},[182,788,790],{"class":184,"line":789},107,[182,791,219],{"emptyLinePlaceholder":218},[182,793,795],{"class":184,"line":794},108,[182,796,797],{},"        private void OnSocketConnected(WebSocketClientConnectedEventArgs args)\n",[182,799,801],{"class":184,"line":800},109,[182,802,375],{},[182,804,806],{"class":184,"line":805},110,[182,807,808],{},"            try { _ws.Send(Query()); }\n",[182,810,812],{"class":184,"line":811},111,[182,813,814],{},"            catch (Exception ex) { Print(\"[SMT] calendar send failed: {0}\", ex.Message); }\n",[182,816,818],{"class":184,"line":817},112,[182,819,393],{},[182,821,823],{"class":184,"line":822},113,[182,824,219],{"emptyLinePlaceholder":218},[182,826,828],{"class":184,"line":827},114,[182,829,830],{},"        private void OnSocketText(WebSocketClientTextReceivedEventArgs args)\n",[182,832,834],{"class":184,"line":833},115,[182,835,375],{},[182,837,839],{"class":184,"line":838},116,[182,840,841],{},"            _received = true;\n",[182,843,845],{"class":184,"line":844},117,[182,846,847],{},"            if (args.Text.StartsWith(\"Error:\")) Print(\"[SMT] calendar: {0}\", args.Text);\n",[182,849,851],{"class":184,"line":850},118,[182,852,853],{},"            else ParseCsv(args.Text);\n",[182,855,857],{"class":184,"line":856},119,[182,858,509],{},[182,860,862],{"class":184,"line":861},120,[182,863,393],{},[182,865,867],{"class":184,"line":866},121,[182,868,219],{"emptyLinePlaceholder":218},[182,870,872],{"class":184,"line":871},122,[182,873,874],{},"        private void OnSocketDisconnected(WebSocketClientDisconnectEventArgs args)\n",[182,876,878],{"class":184,"line":877},123,[182,879,375],{},[182,881,883],{"class":184,"line":882},124,[182,884,885],{},"            if (!_received) DownloadOverHttp(); \u002F\u002F desktop only: the cloud answers 503\n",[182,887,889],{"class":184,"line":888},125,[182,890,393],{},[182,892,894],{"class":184,"line":893},126,[182,895,219],{"emptyLinePlaceholder":218},[182,897,899],{"class":184,"line":898},127,[182,900,901],{},"        private void DownloadOverHttp()\n",[182,903,905],{"class":184,"line":904},128,[182,906,375],{},[182,908,910],{"class":184,"line":909},129,[182,911,701],{},[182,913,915],{"class":184,"line":914},130,[182,916,453],{},[182,918,920],{"class":184,"line":919},131,[182,921,922],{},"                HttpResponse r = Http.Get(CalendarHttp + Query());\n",[182,924,926],{"class":184,"line":925},132,[182,927,928],{},"                if (r.IsSuccessful) ParseCsv(r.Body);\n",[182,930,932],{"class":184,"line":931},133,[182,933,934],{},"                else Print(\"[SMT] calendar HTTP failed: {0}\", r.StatusCode);\n",[182,936,938],{"class":184,"line":937},134,[182,939,471],{},[182,941,943],{"class":184,"line":942},135,[182,944,945],{},"            catch (Exception ex) { Print(\"[SMT] calendar HTTP failed: {0}\", ex.Message); }\n",[182,947,949],{"class":184,"line":948},136,[182,950,393],{},[182,952,954],{"class":184,"line":953},137,[182,955,219],{"emptyLinePlaceholder":218},[182,957,959],{"class":184,"line":958},138,[182,960,961],{},"        private void CloseSocket()\n",[182,963,965],{"class":184,"line":964},139,[182,966,375],{},[182,968,970],{"class":184,"line":969},140,[182,971,972],{},"            WebSocketClient ws = _ws;\n",[182,974,976],{"class":184,"line":975},141,[182,977,978],{},"            _ws = null;\n",[182,980,982],{"class":184,"line":981},142,[182,983,984],{},"            if (ws == null) return;\n",[182,986,988],{"class":184,"line":987},143,[182,989,990],{},"            try { ws.Connected -= OnSocketConnected; ws.TextReceived -= OnSocketText; ws.Disconnected -= OnSocketDisconnected; } catch (Exception) { }\n",[182,992,994],{"class":184,"line":993},144,[182,995,996],{},"            try { if (ws.State == WebSocketClientState.Open) ws.Close(WebSocketClientCloseStatus.NormalClosure, \"done\"); } catch (Exception) { }\n",[182,998,1000],{"class":184,"line":999},145,[182,1001,1002],{},"            try { ws.Dispose(); } catch (Exception) { }\n",[182,1004,1006],{"class":184,"line":1005},146,[182,1007,393],{},[182,1009,1011],{"class":184,"line":1010},147,[182,1012,219],{"emptyLinePlaceholder":218},[182,1014,1016],{"class":184,"line":1015},148,[182,1017,1018],{},"        \u002F\u002F CSV: ts,time,currency,impact,forecast,previous,actual,title (title last, may be quoted),\n",[182,1020,1022],{"class":184,"line":1021},149,[182,1023,1024],{},"        \u002F\u002F then one last line \"# server_time=\u003Cunix> server_time_iso=\u003Ciso>\" with the server's UTC clock.\n",[182,1026,1028],{"class":184,"line":1027},150,[182,1029,1030],{},"        private void ParseCsv(string csv)\n",[182,1032,1034],{"class":184,"line":1033},151,[182,1035,375],{},[182,1037,1039],{"class":184,"line":1038},152,[182,1040,1041],{},"            List\u003CKeyValuePair\u003CDateTime, string>> list = new List\u003CKeyValuePair\u003CDateTime, string>>();\n",[182,1043,1045],{"class":184,"line":1044},153,[182,1046,1047],{},"            foreach (string raw in csv.Split('\\n'))\n",[182,1049,1051],{"class":184,"line":1050},154,[182,1052,453],{},[182,1054,1056],{"class":184,"line":1055},155,[182,1057,1058],{},"                string line = raw.TrimEnd('\\r');\n",[182,1060,1062],{"class":184,"line":1061},156,[182,1063,1064],{},"                if (line.Length == 0 || line.StartsWith(\"ts,\")) continue;\n",[182,1066,1068],{"class":184,"line":1067},157,[182,1069,1070],{},"                if (line.StartsWith(\"# server_time=\"))\n",[182,1072,1074],{"class":184,"line":1073},158,[182,1075,577],{},[182,1077,1079],{"class":184,"line":1078},159,[182,1080,1081],{},"                    long serverTime;\n",[182,1083,1085],{"class":184,"line":1084},160,[182,1086,1087],{},"                    if (long.TryParse(line.Substring(14, 10), out serverTime))\n",[182,1089,1091],{"class":184,"line":1090},161,[182,1092,1093],{},"                    {\n",[182,1095,1097],{"class":184,"line":1096},162,[182,1098,1099],{},"                        double skew = (Server.TimeInUtc - Epoch.AddSeconds(serverTime)).TotalSeconds;\n",[182,1101,1103],{"class":184,"line":1102},163,[182,1104,1105],{},"                        if (Math.Abs(skew) > 60) Print(\"[SMT] warning: cTrader's UTC clock is {0:F0} s away from the calendar server\", skew);\n",[182,1107,1109],{"class":184,"line":1108},164,[182,1110,1111],{},"                    }\n",[182,1113,1115],{"class":184,"line":1114},165,[182,1116,1117],{},"                    continue;\n",[182,1119,1121],{"class":184,"line":1120},166,[182,1122,595],{},[182,1124,1126],{"class":184,"line":1125},167,[182,1127,1128],{},"                string[] f = line.Split(',');\n",[182,1130,1132],{"class":184,"line":1131},168,[182,1133,1134],{},"                if (f.Length \u003C 8) continue;\n",[182,1136,1138],{"class":184,"line":1137},169,[182,1139,1140],{},"                long ts;\n",[182,1142,1144],{"class":184,"line":1143},170,[182,1145,1146],{},"                if (!long.TryParse(f[0], out ts)) continue;\n",[182,1148,1150],{"class":184,"line":1149},171,[182,1151,1152],{},"                DateTime at = Epoch.AddSeconds(ts);\n",[182,1154,1156],{"class":184,"line":1155},172,[182,1157,1158],{},"                string title = AfterComma(line, 7).Replace(\"\\\"\\\"\", \"\\\"\");\n",[182,1160,1162],{"class":184,"line":1161},173,[182,1163,1164],{},"                if (title.Length >= 2 && title[0] == '\"') title = title.Substring(1, title.Length - 2);\n",[182,1166,1168],{"class":184,"line":1167},174,[182,1169,1170],{},"                list.Add(new KeyValuePair\u003CDateTime, string>(at, f[2] + \" \" + f[3] + \": \" + title));\n",[182,1172,1174],{"class":184,"line":1173},175,[182,1175,471],{},[182,1177,1179],{"class":184,"line":1178},176,[182,1180,1181],{},"            _events.Clear();\n",[182,1183,1185],{"class":184,"line":1184},177,[182,1186,1187],{},"            _events.AddRange(list);\n",[182,1189,1191],{"class":184,"line":1190},178,[182,1192,1193],{},"            Print(\"[SMT] calendar: {0} events loaded\", _events.Count);\n",[182,1195,1197],{"class":184,"line":1196},179,[182,1198,393],{},[182,1200,1202],{"class":184,"line":1201},180,[182,1203,219],{"emptyLinePlaceholder":218},[182,1205,1207],{"class":184,"line":1206},181,[182,1208,1209],{},"        private static string AfterComma(string line, int n)\n",[182,1211,1213],{"class":184,"line":1212},182,[182,1214,375],{},[182,1216,1218],{"class":184,"line":1217},183,[182,1219,1220],{},"            int pos = -1;\n",[182,1222,1224],{"class":184,"line":1223},184,[182,1225,1226],{},"            for (int i = 0; i \u003C n; i++)\n",[182,1228,1230],{"class":184,"line":1229},185,[182,1231,453],{},[182,1233,1235],{"class":184,"line":1234},186,[182,1236,1237],{},"                pos = line.IndexOf(',', pos + 1);\n",[182,1239,1241],{"class":184,"line":1240},187,[182,1242,1243],{},"                if (pos \u003C 0) return \"\";\n",[182,1245,1247],{"class":184,"line":1246},188,[182,1248,471],{},[182,1250,1252],{"class":184,"line":1251},189,[182,1253,1254],{},"            return line.Substring(pos + 1);\n",[182,1256,1258],{"class":184,"line":1257},190,[182,1259,393],{},[182,1261,1263],{"class":184,"line":1262},191,[182,1264,1265],{},"    }\n",[182,1267,1269],{"class":184,"line":1268},192,[182,1270,1271],{},"}\n",[18,1273,1274,1275,1278,1279,1282,1283,1286,1287,1290,1291,1294],{},"To put the filter inside an existing cBot, copy everything from ",[31,1276,1277],{},"CalendarWs"," down to ",[31,1280,1281],{},"AfterComma()"," into it, call ",[31,1284,1285],{},"DownloadCalendar()"," from ",[31,1288,1289],{},"OnStart()"," and from a 15-minute timer, and check ",[31,1292,1293],{},"IsNewsTime()"," before each entry.",[53,1296,1298],{"id":1297},"trying-it","🧪 Trying It",[1300,1301,1302,1310,1326],"ol",{},[1303,1304,1305,1306,1309],"li",{},"Build the cBot and add an instance to a chart. The log should show ",[31,1307,1308],{},"[SMT] calendar: N events loaded"," within a few seconds. Zero on a quiet weekend is correct.",[1303,1311,1312,1313,1317,1318,1321,1322,1325],{},"Set ",[1314,1315,1316],"em",{},"News: impact"," to ",[31,1319,1320],{},"all"," and ",[1314,1323,1324],{},"minutes before"," to a large number to see the filter engage, then put the real values back.",[1303,1327,1328,1329,1332],{},"Run the same instance ",[22,1330,1331],{},"in the cloud",": the log line is the same. That is the whole point of the socket.",[53,1334,1336],{"id":1335},"️-times-and-the-backtester","⏱️ Times and the Backtester",[18,1338,1339,1340,1343,1344,1347,1348,1350,1351,1353],{},"The calendar's timestamps are UTC and the code compares them with ",[31,1341,1342],{},"Server.TimeInUtc",", so the broker's time zone and daylight saving never enter the picture. Every download also ends with the server's own UTC clock (",[31,1345,1346],{},"# server_time=…","): the cBot checks ",[31,1349,1342],{}," against it and prints a warning if the two disagree by more than a minute, so a wrong clock cannot go unnoticed. The backtester and the optimizer have no network: ",[31,1352,1285],{}," returns early there and the filter is silent. Backtesting a news filter needs a file of past events shipped with the cBot, a separate exercise.",[53,1355,1357],{"id":1356},"️-tuning","🎛️ Tuning",[1359,1360,1361,1370,1380],"ul",{},[1303,1362,1363,1366,1367,1369],{},[22,1364,1365],{},"Open positions"," are not touched. Use ",[31,1368,1293],{}," to decide what to do with them: close, trail, or leave alone.",[1303,1371,1372,1375,1376,1379],{},[22,1373,1374],{},"Medium impact"," (",[31,1377,1378],{},"high,medium",") triples the events. Scalpers often want it, swing systems rarely.",[1303,1381,1382,1385],{},[22,1383,1384],{},"Several cBots on one account"," each download on their own: ten of them are 40 small requests an hour, far below the limit of 60 socket messages per minute per IP address.",[53,1387,1389],{"id":1388},"faq","❓ FAQ",[18,1391,1392,1395],{},[22,1393,1394],{},"Why a WebSocket and not HTTPS?","\nBecause cTrader Cloud does not let a cBot make HTTP requests, and it allows WebSocket connections only on port 25345. On the desktop the same cBot would also work over HTTPS, and the code falls back to it if the socket cannot connect.",[18,1397,1398,1404,1405,38],{},[22,1399,1400,1401,1403],{},"Is it safe to send on ",[31,1402,156],{}," without encryption?","\nThe request contains four parameters (currencies, impact, days, format) and nothing about you or your account; the response is a public calendar. There is nothing to protect. The same data is available over HTTPS from the ",[47,1406,1407],{"href":49},"API endpoint",[18,1409,1410,1417,1418,1321,1421,1424,1425,1427],{},[22,1411,1412,1413,1416],{},"Does it need ",[31,1414,1415],{},"AccessRights.FullAccess","?","\nNo. ",[31,1419,1420],{},"WebSocketClient",[31,1422,1423],{},"Http"," are available with ",[31,1426,133],{},", which is also what the cTrader Store requires.",[18,1429,1430,1433,1434,1438],{},[22,1431,1432],{},"Does it need a ShowMyTrades account?","\nNo. If you also want to publish the results of the cBot, ",[47,1435,1437],{"href":1436},"\u002Fguides\u002Fshow-ctrader-results-on-your-website","connecting a cTrader account"," is a separate thing and takes one OAuth authorisation.",[53,1440,1442],{"id":1441},"related","🔗 Related",[1359,1444,1445,1451,1458,1463],{},[1303,1446,1447,1450],{},[47,1448,1449],{"href":49},"Economic calendar API reference",": the WebSocket protocol, the HTTP endpoint, JSON and CSV formats",[1303,1452,1453,1457],{},[47,1454,1456],{"href":1455},"\u002Fguides\u002Fadd-a-news-filter-to-your-mt4-expert-advisor","News filter for an MT4 Expert Advisor",": the same filter in MQL",[1303,1459,1460],{},[47,1461,1462],{"href":1436},"Show your cTrader results on your website",[1303,1464,1465],{},[47,1466,1468],{"href":1467},"\u002Fglossary\u002Fwhat-is-ctrader","What is cTrader",[1470,1471],"hr",{},[18,1473,1474,1475,1479],{},"The socket is the only unusual part, and it is already written. The rest is your strategy, with one fewer way to lose money around the news. ",[47,1476,1478],{"href":1477},"\u002Fsignup","Track the account free"," to see the difference on the drawdown chart.",[1481,1482,1483],"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":66,"searchDepth":191,"depth":191,"links":1485},[1486,1487,1488,1489,1490,1491,1492],{"id":55,"depth":191,"text":56},{"id":164,"depth":191,"text":165},{"id":1297,"depth":191,"text":1298},{"id":1335,"depth":191,"text":1336},{"id":1356,"depth":191,"text":1357},{"id":1388,"depth":191,"text":1389},{"id":1441,"depth":191,"text":1442},"2026-10-17T00:00:00.000Z","cTrader has no calendar API for cBots, and cTrader Cloud blocks HTTP requests. Here is a news filter that works on the desktop and in the cloud, with a free calendar over WebSocket and the full C# code.",false,"md",{"src":1498},"\u002Fimages\u002Fblog\u002Fadd-a-news-filter-to-a-ctrader-cbot-hero.svg",{},"\u002Fguides\u002Fadd-a-news-filter-to-a-ctrader-cbot",{"title":5,"description":1494},"3.guides\u002F43.add-a-news-filter-to-a-ctrader-cbot",null,"oLXah19QdWBEeujkiKgWrtTDuyaO0rpFKU22H3iY2us",[1506,1511],{"title":1507,"path":1508,"stem":1509,"description":1510,"children":-1},"News Filter in MT5: the Built-in Calendar or an API?","\u002Fguides\u002Fnews-filter-mt5-expert-advisor-built-in-calendar-or-api","3.guides\u002F42.news-filter-mt5-expert-advisor-built-in-calendar-or-api","MetaTrader 5 ships an economic calendar that Expert Advisors can read with no download. When it is enough, when it is not, and how to use a free calendar API instead, with MQL5 code for both.",{"title":1512,"path":1513,"stem":1514,"description":1515,"children":-1},"A Free Economic Calendar API for Trading Bots","\u002Fguides\u002Ffree-economic-calendar-api-for-trading-bots","3.guides\u002F44.free-economic-calendar-api-for-trading-bots","Where a trading robot can get the economic calendar: the sources people use, what breaks with each, and a free API with no key built for polling from Expert Advisors, cBots, Python and JavaScript.",1792249019844]