//+------------------------------------------------------------------+ // // Copyright (C) 2019 Nikolai Khramkov // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // //+------------------------------------------------------------------+ // TODO: Deviation #property copyright "Copyright 2019, Nikolai Khramkov." #property link "https://github.com/khramkov" #property version "2.00" #property description "MQL5 JSON API" #property description "See github link for documentation" #include #include #include #include #include #include //#include //#include //#include // Starts an Expert Advisor with specified parameters //#include // Set ports and host for ZeroMQ string HOST="*"; int SYS_PORT=15555; int DATA_PORT=15556; int LIVE_PORT=15557; int STR_PORT=15558; int IND_DATA_PORT=15559; int CHART_LIVE_PORT=15560; int PUB_CHART_LIVE_PORT=15562; // ZeroMQ Cnnections Context context("MQL5 JSON API"); Socket sysSocket(context,ZMQ_REP); Socket dataSocket(context,ZMQ_PUSH); Socket liveSocket(context,ZMQ_PUSH); Socket streamSocket(context,ZMQ_PUSH); Socket indicatorDataSocket(context,ZMQ_PUSH); Socket chartLiveSocket(context,ZMQ_PULL); Socket pubChartLiveSocket(context,ZMQ_PUB); // Global variables bool debug = false; bool liveStream = true; bool connectedFlag= true; int deInitReason = -1; double chartAttached = ChartID(); // Variables for handling price data stream struct SymbolSubscription { string symbol; string chartTf; datetime lastBar; }; SymbolSubscription symbolSubscriptions[]; //string chartSymbols[]; int symbolSubscriptionCount = 0; //string chartSymbolSettings[][3]; // Variables for controlling indicators struct Indicator { long id; string indicatorId; int indicatorHandle; int indicatorParamCount; int indicatorBufferCount; }; Indicator indicators[]; int indicatorCount = 0; /* double indicators[]; string indicatorIds[]; int indicatorParamCount[]; int indicatorBufferCount[]; */ // Variables for controlling chart struct ChartWindow { long id; string chartId; string indicatorId; int indicatorHandle; }; ChartWindow chartWindows[]; int chartWindowCount = 0; // Refresh chart windows interval. OnTimer function of indicatore JsonAPIIndicator gets triggered on each interval int chartWindowTimerInterval = 100; int chartWindowTimerCounter = 0; //string chartWindowObjects[]; //double chartCurvePositions[][3]; //+------------------------------------------------------------------+ //| Bind ZMQ sockets to ports | //+------------------------------------------------------------------+ bool BindSockets(){ bool result = false; result = sysSocket.bind(StringFormat("tcp://%s:%d", HOST,SYS_PORT)); if (result == false) return result; result = dataSocket.bind(StringFormat("tcp://%s:%d", HOST,DATA_PORT)); if (result == false) return result; result = liveSocket.bind(StringFormat("tcp://%s:%d", HOST,LIVE_PORT)); if (result == false) return result; result = streamSocket.bind(StringFormat("tcp://%s:%d", HOST,STR_PORT)); if (result == false) return result; result = indicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,IND_DATA_PORT)); if (result == false) return result; result = chartLiveSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_LIVE_PORT)); if (result == false) { return result; } else {Print("Bound 'Chart Live' socket on port ", CHART_LIVE_PORT);} result = pubChartLiveSocket.bind(StringFormat("tcp://%s:%d", HOST,PUB_CHART_LIVE_PORT)); if (result == false) { return result; } else {Print("Bound 'PUB Chart Live' socket on port ", PUB_CHART_LIVE_PORT);} Print("Bound 'System' socket on port ", SYS_PORT); Print("Bound 'Data' socket on port ", DATA_PORT); Print("Bound 'Live' socket on port ", LIVE_PORT); Print("Bound 'Streaming' socket on port ", STR_PORT); Print("Bound 'Indicator Data' socket on port ", IND_DATA_PORT); sysSocket.setLinger(1000); dataSocket.setLinger(1000); liveSocket.setLinger(1000); streamSocket.setLinger(1000); indicatorDataSocket.setLinger(1000); chartLiveSocket.setLinger(1000); //pubChartLiveSocket.setLinger(1000); // Number of messages to buffer in RAM. sysSocket.setSendHighWaterMark(1); dataSocket.setSendHighWaterMark(5); liveSocket.setSendHighWaterMark(1); streamSocket.setSendHighWaterMark(50); indicatorDataSocket.setSendHighWaterMark(5); chartLiveSocket.setReceiveHighWaterMark(1); // TODO confirm settings //pubChartLiveSocket.setReceiveHighWaterMark(1); return result; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ /* Bindinig ZMQ ports on init */ // Skip reloading of the EA script when the reason to reload is a chart timeframe change if (deInitReason != REASON_CHARTCHANGE){ EventSetMillisecondTimer(1); int bindSocketsDelay = 65; // Seconds to wait if binding of sockets fails. int bindAttemtps = 2; // Number of binding attemtps Print("Binding sockets..."); for(int i=0;i0){ // Pull request to RequestHandler(). RequestHandler(request); } // Publish indicator values for the JsonAPIIndicator indicator ZmqMsg chartMsg; chartLiveSocket.recv(chartMsg, true); if(chartMsg.size()>0){ Print(chartMsg.getData()); pubChartLiveSocket.send(chartMsg,true); ResetLastError(); } // Trigger the indicator JsonAPIIndicator to check for new Messages if(chartWindowTimerCounter >= chartWindowTimerInterval) { for(int i=0;i= 0) { if(CopyBuffer(indicators[idx].indicatorHandle, i, fromDate, 1, values) < 0) {/* Error handling */} results[i] = DoubleToString(values[0]); } else { // TODO error handling } } CJAVal message; message["id"] = (string) id; message["data"].Set(results); string t=message.Serialize(); if(debug) Print(t); InformClientSocket(indicatorDataSocket,t); // TODO more error handling //if(SymbolInfoInteger(symbol, SYMBOL_EXIST)){ // ActionDoneOrError(ERR_SUCCESS, __FUNCTION__); //} //else ActionDoneOrError(ERR_MARKET_UNKNOWN_SYMBOL, __FUNCTION__); } //+------------------------------------------------------------------+ //| Open new chart or add indicator to chart | //+------------------------------------------------------------------+ void ChartControl(CJAVal &dataObject){ string actionType=dataObject["actionType"].ToStr(); if(actionType=="ADDINDICATOR") { AddIndicatorChartToChart(dataObject); } else if(actionType=="OPEN") { OpenChart(dataObject); } } //+------------------------------------------------------------------+ //| Open new chart | //+------------------------------------------------------------------+ void OpenChart(CJAVal &dataObject){ string chartId=dataObject["chartId"].ToStr(); string symbol=dataObject["symbol"].ToStr(); string chartTF=dataObject["chartTF"].ToStr(); chartWindowCount++; ArrayResize(chartWindows,chartWindowCount); //ArrayResize(chartWindowIds,chartWindowCount); //ArrayResize(chartWindowPeriods,chartWindowCount); // ArrayResize(chartWindowObjects,chartWindowCount); int idx = chartWindowCount-1; chartWindows[idx].chartId = chartId; ENUM_TIMEFRAMES period = GetTimeframe(chartTF); chartWindows[idx].id = ChartOpen(symbol, period); //chartWindowPeriods[idx] = period; CJAVal message; message["chartId"] = (string) chartId; string t=message.Serialize(); if(debug) Print(t); InformClientSocket(dataSocket,t); } //+------------------------------------------------------------------+ //| Add JsonAPIIndicator indicator chart to chart | //+------------------------------------------------------------------+ void AddIndicatorChartToChart(CJAVal &dataObject){ string chartIdStr=dataObject["chartId"].ToStr(); string chartIndicatorId=dataObject["indicatorChartId"].ToStr(); int chartIndicatorSubWindow=dataObject["chartIndicatorSubWindow"].ToInt(); string shortname = dataObject["style"]["shortname"].ToStr(); string colorstyle = dataObject["style"]["color"].ToStr(); string linetype = dataObject["style"]["linetype"].ToStr(); string linestyle = dataObject["style"]["linestyle"].ToStr(); int linewidth = dataObject["style"]["linewidth"].ToInt(); int idx = GetChartWindowIdxByChartWindowId(chartIdStr); long ChartId = chartWindows[idx].id; double chartIndicatorHandle = iCustom(ChartSymbol(ChartId),ChartPeriod(ChartId),"JsonAPIIndicator",chartIndicatorId,shortname,colorstyle,linetype,linestyle,linewidth); ChartIndicatorAdd(ChartId, chartIndicatorSubWindow, chartIndicatorHandle); chartWindows[idx].indicatorId = chartIndicatorId; chartWindows[idx].indicatorHandle = chartIndicatorHandle; CJAVal message; message["chartId"] = (string) chartIdStr; string t=message.Serialize(); if(debug) Print(t); InformClientSocket(dataSocket,t); } //+------------------------------------------------------------------+ //| Draw on chart | //+------------------------------------------------------------------+ void ChartDraw(CJAVal &dataObject){ /* Print("drawchart"); string id=dataObject["id"].ToStr(); //int windowIdx = GetChartWindowIdxByChartWindowId(id); int datesSize = dataObject["data"][0].Size(); int x[]; int y[]; double x_dbl[]; double y_dbl[]; ArrayResize(chartCurvePositions, datesSize); ArrayResize(x,datesSize); ArrayResize(y,datesSize); ArrayResize(x_dbl,datesSize); ArrayResize(y_dbl,datesSize); for(int i=0;i0) { tradeInfo.Ticket(ticket); data["ticket"]=(long) tradeInfo.Ticket(); data["time"]=(long) tradeInfo.Time(); data["price"]=(double) tradeInfo.Price(); data["volume"]=(double) tradeInfo.Volume(); data["symbol"]=(string) tradeInfo.Symbol(); data["type"]=(string) tradeInfo.TypeDescription(); data["entry"]=(long) tradeInfo.Entry(); data["profit"]=(double) tradeInfo.Profit(); trades["trades"].Add(data); } } } else {trades["trades"].Add(data);} string t=trades.Serialize(); if(debug) Print(t); InformClientSocket(dataSocket,t); } // Error wrong action type else ActionDoneOrError(65538, __FUNCTION__); } //+------------------------------------------------------------------+ //| Fetch positions information | //+------------------------------------------------------------------+ void GetPositions(CJAVal &dataObject){ CPositionInfo myposition; CJAVal data, position; // Get positions int positionsTotal=PositionsTotal(); // Create empty array if no positions if(!positionsTotal) data["positions"].Add(position); // Go through positions in a loop for(int i=0;i