diff --git a/.gitignore b/.gitignore index 9bea433..40d9456 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,10 @@ .DS_Store + +*.mq5:CursorPos:$DATA + +*.mq5:LineFlags:$DATA + +*.mqh:CursorPos:$DATA + +*.mqh:LineFlags:$DATA diff --git a/Experts/JsonAPI.mq5 b/Experts/JsonAPI.mq5 index bc1576c..1d9ebf8 100644 --- a/Experts/JsonAPI.mq5 +++ b/Experts/JsonAPI.mq5 @@ -23,7 +23,7 @@ #property link "https://github.com/khramkov" #property version "2.00" #property description "MQL5 JSON API" -#property description "See github link for documentation" +#property description "See github link for documentation" #include #include @@ -39,19 +39,21 @@ int SYS_PORT=15555; int DATA_PORT=15556; int LIVE_PORT=15557; int STR_PORT=15558; -int INDICATOR_DATA_PORT=15559; -int CHART_DATA_PORT=15560; -int CHART_INDICATOR_DATA_PORT=15562; -// ZeroMQ Cnnections +// ZeroMQ Connections 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 chartDataSocket(context,ZMQ_PULL); -Socket chartIndicatorDataSocket(context,ZMQ_PUB); + +// Load MQL5-JSON-API includes +// Required: +#include +#include +// Optional: +#include +#include // Global variables \\ bool debug = false; @@ -61,142 +63,178 @@ int deInitReason = -1; double chartAttached = ChartID(); // Chart id where the expert is attached to // Variables for handling price data stream -struct SymbolSubscription { - string symbol; - string chartTf; - datetime lastBar; -}; +struct SymbolSubscription + { + string symbol; + string chartTf; + datetime lastBar; + }; SymbolSubscription symbolSubscriptions[]; int symbolSubscriptionCount = 0; -// Variables for controlling indicators -struct Indicator { - long id; // Internal id - string indicatorId; // UUID - int indicatorHandle; // Internal id/handle - int indicatorParamCount; // Number of parameters to be passed to the indicator - int indicatorBufferCount; // Numnber of buffers to be returned bythe indicator -}; -Indicator indicators[]; -int indicatorCount = 0; - -// Variables for controlling chart -struct ChartWindow { - long id; // Internal id - string chartId; // UUID -}; -ChartWindow chartWindows[]; -int chartWindowCount = 0; - -struct ChartWindowIndicator { - long id; // Internal id - string indicatorId; // UUID - int indicatorHandle; // Internal id/handle -}; - -ChartWindowIndicator chartWindowIndicators[]; -int chartWindowIndicatorCount = 0; - -// Refresh chart window interval for JsonAPIIndicator -int chartWindowTimerInterval = 100; // Cycles of the globally set EventSetMillisecondTimer interval -int chartWindowTimerCounter = 0; // Keeps track of the current cycle - // Error handling ControlErrors mControl; //+------------------------------------------------------------------+ //| 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; } else {Print("Bound 'System' socket on port ", SYS_PORT);} - result = dataSocket.bind(StringFormat("tcp://%s:%d", HOST,DATA_PORT)); - if (result == false) { return result; } else {Print("Bound 'Data' socket on port ", DATA_PORT);} - result = liveSocket.bind(StringFormat("tcp://%s:%d", HOST,LIVE_PORT)); - if (result == false) { return result; } else {Print("Bound 'Live' socket on port ", LIVE_PORT);} - result = streamSocket.bind(StringFormat("tcp://%s:%d", HOST,STR_PORT)); - if (result == false) { return result; } else {Print("Bound 'Streaming' socket on port ", STR_PORT);} - result = indicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,INDICATOR_DATA_PORT)); - if (result == false) { return result; } else {Print("Bound 'Indicator Data' socket on port ", INDICATOR_DATA_PORT);} - result = chartDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_DATA_PORT)); - if (result == false) { return result; } else {Print("Bound 'Chart Data' socket on port ", CHART_DATA_PORT);} - result = chartIndicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_INDICATOR_DATA_PORT)); - if (result == false) { return result; } else {Print("Bound 'JsonAPIIndicator Data' socket on port ", CHART_INDICATOR_DATA_PORT);} - - sysSocket.setLinger(1000); - dataSocket.setLinger(1000); - liveSocket.setLinger(1000); - streamSocket.setLinger(1000); - indicatorDataSocket.setLinger(1000); - chartDataSocket.setLinger(1000); - chartIndicatorDataSocket.setLinger(1000); - - // Number of messages to buffer in RAM. - sysSocket.setSendHighWaterMark(1); - dataSocket.setSendHighWaterMark(5); - liveSocket.setSendHighWaterMark(1); - streamSocket.setSendHighWaterMark(50); - indicatorDataSocket.setSendHighWaterMark(5); - chartDataSocket.setReceiveHighWaterMark(1); // TODO confirm settings - chartIndicatorDataSocket.setReceiveHighWaterMark(1); +bool BindSockets() + { + sysSocket.setLinger(1000); + dataSocket.setLinger(1000); + liveSocket.setLinger(1000); + streamSocket.setLinger(1000); +#ifdef START_INDICATOR + indicatorDataSocket.setLinger(1000); +#endif +#ifdef CHART_CONTROL + chartDataSocket.setLinger(1000); + chartIndicatorDataSocket.setLinger(1000); +#endif - return result; -} +// Number of messages to buffer in RAM. + sysSocket.setSendHighWaterMark(1000); + dataSocket.setSendHighWaterMark(1000); + liveSocket.setSendHighWaterMark(1000); + streamSocket.setSendHighWaterMark(1000); +#ifdef START_INDICATOR + indicatorDataSocket.setSendHighWaterMark(1000); +#endif +#ifdef CHART_CONTROL + chartDataSocket.setReceiveHighWaterMark(1000); // TODO confirm settings + chartIndicatorDataSocket.setReceiveHighWaterMark(1000); +#endif + + bool result = false; + result = sysSocket.bind(StringFormat("tcp://%s:%d", HOST,SYS_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'System' socket on port ", SYS_PORT); + } + result = dataSocket.bind(StringFormat("tcp://%s:%d", HOST,DATA_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'Data' socket on port ", DATA_PORT); + } + result = liveSocket.bind(StringFormat("tcp://%s:%d", HOST,LIVE_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'Live' socket on port ", LIVE_PORT); + } + result = streamSocket.bind(StringFormat("tcp://%s:%d", HOST,STR_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'Streaming' socket on port ", STR_PORT); + } +#ifdef START_INDICATOR + result = indicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,INDICATOR_DATA_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'Indicator Data' socket on port ", INDICATOR_DATA_PORT); + } +#endif +#ifdef CHART_CONTROL + result = chartDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_DATA_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'Chart Data' socket on port ", CHART_DATA_PORT); + } + result = chartIndicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_INDICATOR_DATA_PORT)); + if(result == false) + { + return result; + } + else + { + Print("Bound 'JsonAPIIndicator Data' socket on port ", CHART_INDICATOR_DATA_PORT); + } +#endif + return result; + } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ -int OnInit(){ +int OnInit() + { - // Setting up error reporting - mControl.SetAlert(true); - mControl.SetSound(false); - mControl.SetWriteFlag(false); - - /* 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); +// Setting up error reporting + mControl.SetAlert(true); + mControl.SetSound(false); + mControl.SetWriteFlag(false); - 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; - chartDataSocket.recv(chartMsg, true); - if(chartMsg.size()>0){ +// Stream live price data + StreamPriceData(); + + ZmqMsg request; + +// Get request from client via System socket. + sysSocket.recv(request,true); + +// Request recived + if(request.size()>0) + { + // Pull request to RequestHandler(). + RequestHandler(request); + } +#ifdef CHART_CONTROL +// Publish indicator values for the JsonAPIIndicator indicator + ZmqMsg chartMsg; + chartDataSocket.recv(chartMsg, true); + if(chartMsg.size()>0) + { double values[]; // Ensure that all indicators have finished intitailisation - for(int i=0;i= chartWindowTimerInterval) { - for(int i=0;i= 0) { - if(CopyBuffer(indicators[idx].indicatorHandle, i, fromDate, 1, values) < 0) { - if(mControl.mGetLastError()) - { - CJAVal message; - int lastError = mControl.mGetLastError(); - string desc = mControl.mGetDesc(); - mControl.Check(); - - message["error"]=(bool) true; - message["lastError"]=(string) lastError; - message["description"]=desc; - message["function"]=(string) __FUNCTION__; - string t=message.Serialize(); - if(debug) Print(t); - InformClientSocket(indicatorDataSocket,t); - } - } - results[i] = DoubleToString(values[0]); - } - } - - CJAVal message; - message["error"]=(bool) false; - message["id"] = (string) id; - message["data"].Set(results); - - string t=message.Serialize(); - if(debug) Print(t); - InformClientSocket(indicatorDataSocket,t); - -} - -//+------------------------------------------------------------------+ -//| Open new chart or add indicator to chart | -//+------------------------------------------------------------------+ - -void ChartControl(CJAVal &dataObject){ - - string actionType=dataObject["actionType"].ToStr(); - - if(actionType=="ADDINDICATOR") { - AddChartIndicator(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); - - int idx = chartWindowCount-1; - - chartWindows[idx].chartId = chartId; - - ENUM_TIMEFRAMES period = GetTimeframe(chartTF); - chartWindows[idx].id = ChartOpen(symbol, period); - ChartSetInteger(chartWindows[idx].id, CHART_AUTOSCROLL, false); - - CJAVal message; - message["error"]=(bool) false; - message["chartId"] = (string) chartId; - - string t=message.Serialize(); - if(debug) Print(t); - InformClientSocket(dataSocket,t); -} - -//+------------------------------------------------------------------+ -//| Add JsonAPIIndicator indicator to chart | -//+------------------------------------------------------------------+ -void AddChartIndicator(CJAVal &dataObject){ - - string chartIdStr=dataObject["chartId"].ToStr(); - string chartIndicatorId=dataObject["indicatorChartId"].ToStr(); - int chartIndicatorSubWindow=dataObject["chartIndicatorSubWindow"].ToInt(); - //string shortname = dataObject["style"]["shortname"].ToStr(); - - int chartIdx = GetChartWindowIdxByChartWindowId(chartIdStr); - long ChartId = chartWindows[chartIdx].id; - - double chartIndicatorHandle = iCustom(ChartSymbol(ChartId),ChartPeriod(ChartId),"JsonAPIIndicator",chartIndicatorId,"JsonAPI"); //linelabel,colorstyle,linetype,linestyle,linewidth); - - if(ChartIndicatorAdd(ChartId, chartIndicatorSubWindow, chartIndicatorHandle)) { - chartWindowIndicatorCount++; - ArrayResize(chartWindowIndicators,chartWindowIndicatorCount); - int indicatorIdx = chartWindowIndicatorCount-1; - chartWindowIndicators[indicatorIdx].indicatorId = chartIndicatorId; - chartWindowIndicators[indicatorIdx].indicatorHandle = chartIndicatorHandle; - } - if(!CheckError(__FUNCTION__)) { - CJAVal message; - message["error"]=(bool) false; - message["chartId"] = (string) chartIdStr; - - string t=message.Serialize(); - if(debug) Print(t); - InformClientSocket(dataSocket,t); - } -} //+------------------------------------------------------------------+ //| Account information | //+------------------------------------------------------------------+ -void GetAccountInfo(){ - - CJAVal info; - - info["error"] = false; - info["broker"] = AccountInfoString(ACCOUNT_COMPANY); - info["currency"] = AccountInfoString(ACCOUNT_CURRENCY); - info["server"] = AccountInfoString(ACCOUNT_SERVER); - info["trading_allowed"] = TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); - info["bot_trading"] = AccountInfoInteger(ACCOUNT_TRADE_EXPERT); - info["balance"] = AccountInfoDouble(ACCOUNT_BALANCE); - info["equity"] = AccountInfoDouble(ACCOUNT_EQUITY); - info["margin"] = AccountInfoDouble(ACCOUNT_MARGIN); - info["margin_free"] = AccountInfoDouble(ACCOUNT_MARGIN_FREE); - info["margin_level"] = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); - - string t=info.Serialize(); - if(debug) Print(t); - InformClientSocket(dataSocket,t); -} +void GetAccountInfo() + { + + CJAVal info; + + info["error"] = false; + info["broker"] = AccountInfoString(ACCOUNT_COMPANY); + info["currency"] = AccountInfoString(ACCOUNT_CURRENCY); + info["server"] = AccountInfoString(ACCOUNT_SERVER); + info["trading_allowed"] = TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); + info["bot_trading"] = AccountInfoInteger(ACCOUNT_TRADE_EXPERT); + info["balance"] = AccountInfoDouble(ACCOUNT_BALANCE); + info["equity"] = AccountInfoDouble(ACCOUNT_EQUITY); + info["margin"] = AccountInfoDouble(ACCOUNT_MARGIN); + info["margin_free"] = AccountInfoDouble(ACCOUNT_MARGIN_FREE); + info["margin_level"] = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); + + string t=info.Serialize(); + if(debug) + Print(t); + InformClientSocket(dataSocket,t); + } //+------------------------------------------------------------------+ //| Balance information | //+------------------------------------------------------------------+ -void GetBalanceInfo(){ - - CJAVal info; - info["balance"] = AccountInfoDouble(ACCOUNT_BALANCE); - info["equity"] = AccountInfoDouble(ACCOUNT_EQUITY); - info["margin"] = AccountInfoDouble(ACCOUNT_MARGIN); - info["margin_free"] = AccountInfoDouble(ACCOUNT_MARGIN_FREE); - - string t=info.Serialize(); - if(debug) Print(t); - InformClientSocket(dataSocket,t); -} +void GetBalanceInfo() + { + + CJAVal info; + info["balance"] = AccountInfoDouble(ACCOUNT_BALANCE); + info["equity"] = AccountInfoDouble(ACCOUNT_EQUITY); + info["margin"] = AccountInfoDouble(ACCOUNT_MARGIN); + info["margin_free"] = AccountInfoDouble(ACCOUNT_MARGIN_FREE); + + string t=info.Serialize(); + if(debug) + Print(t); + InformClientSocket(dataSocket,t); + } //+------------------------------------------------------------------+ //| Push historical data to ZMQ socket | //+------------------------------------------------------------------+ -bool PushHistoricalData(CJAVal &data){ - string t=data.Serialize(); - if(debug) Print(t); - InformClientSocket(dataSocket,t); - return true; -} - -//+------------------------------------------------------------------+ -//| Get historical data | -//+------------------------------------------------------------------+ -void HistoryInfo(CJAVal &dataObject){ - - string actionType = dataObject["actionType"].ToStr(); - string chartTF = dataObject["chartTF"].ToStr(); - string symbol=dataObject["symbol"].ToStr(); - - // Write CVS fle to local directory - if(actionType=="WRITE" && chartTF=="TICK"){ - - CJAVal data, d, msg; - MqlTick tickArray[]; - string fileName=symbol + "-" + chartTF + ".csv"; // file name - string directoryName="Data"; // directory name - string outputFile=directoryName+"\\"+fileName; - - ENUM_TIMEFRAMES period=GetTimeframe(chartTF); - datetime fromDate=(datetime)dataObject["fromDate"].ToInt(); - datetime toDate=TimeCurrent(); - if(dataObject["toDate"].ToInt()!=NULL) toDate=(datetime)dataObject["toDate"].ToInt(); - - Print("Fetching HISTORY"); - Print("1) Symbol: "+symbol); - Print("2) Timeframe: Ticks"); - Print("3) Date from: "+TimeToString(fromDate)); - if(dataObject["toDate"].ToInt()!=NULL)Print("4) Date to:"+TimeToString(toDate)); - - int tickCount = 0; - ulong fromDateM = StringToTime(fromDate); - ulong toDateM = StringToTime(toDate); - - tickCount=CopyTicksRange(symbol,tickArray,COPY_TICKS_ALL,1000*(ulong)fromDateM,1000*(ulong)toDateM); - if(tickCount < 0) { - mControl.mSetUserError(65541, GetErrorID(65541)); - } - CheckError(__FUNCTION__); - - Print("Preparing data of ", tickCount, " ticks for ", symbol); - int file_handle=FileOpen(outputFile, FILE_WRITE | FILE_CSV); - if(file_handle!=INVALID_HANDLE){ - msg["status"] = (string) "CONNECTED"; - msg["type"] = (string) "NORMAL"; - msg["data"] = (string) StringFormat("Writing to: %s\\%s", TerminalInfoString(TERMINAL_DATA_PATH), outputFile); - if(liveStream) InformClientSocket(liveSocket, msg.Serialize()); - ActionDoneOrError(ERR_SUCCESS , __FUNCTION__, "ERR_SUCCESS"); - //ActionDoneOrError(ERR_SUCCESS , __FUNCTION__, dataSocket); - // Inform client that file is avalable for writing - - PrintFormat("%s file is available for writing",fileName); - PrintFormat("File path: %s\\Files\\",TerminalInfoString(TERMINAL_DATA_PATH)); - //--- write the time and values of signals to the file - 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); - } - else { - mControl.mSetUserError(65538, GetErrorID(65538)); - CheckError(__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;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); + } + else + { + mControl.mSetUserError(65538, GetErrorID(65538)); + CheckError(__FUNCTION__); + } + } +//+------------------------------------------------------------------+ diff --git a/Include/MQL5-JSON_API/StartIndicator.mqh b/Include/MQL5-JSON_API/StartIndicator.mqh new file mode 100644 index 0000000..c4e50ce --- /dev/null +++ b/Include/MQL5-JSON_API/StartIndicator.mqh @@ -0,0 +1,244 @@ +//+------------------------------------------------------------------+ +//| StartIndicator.mqh | +//| Gunther Schulz | +//| https://github.com/khramkov/MQL5-JSON-API | +//+------------------------------------------------------------------+ +#property copyright "Gunther Schulz" +#property link "https://github.com/khramkov/MQL5-JSON-API" + +#define START_INDICATOR true + +int INDICATOR_DATA_PORT=15559; + +Socket indicatorDataSocket(context,ZMQ_PUSH); + +struct Indicator + { + long id; // Internal id + string indicatorId; // UUID + int indicatorHandle; // Internal id/handle + int indicatorParamCount; // Number of parameters to be passed to the indicator + int indicatorBufferCount; // Numnber of buffers to be returned bythe indicator + }; + +Indicator indicators[]; +int indicatorCount = 0; + +//+------------------------------------------------------------------+ +//| Check if string is a representation of a number | +//+------------------------------------------------------------------+ +bool IsNumberAsString(string str) + { +// MQL5 seems to return true if the values are the same, no matter the data type, in this case comparing str and dbl/int. +// (str "2.1" == double 2.1) will return true. + double dbl = StringToDouble(str); + int integer = StringToInteger(str); +// Compaing to both int and double to cover both cases + if(str==dbl || str==integer) + return true; + else + return false; + } + +//+------------------------------------------------------------------+ +//| Get index of indicator handler array by indicator id string | +//+------------------------------------------------------------------+ +int GetIndicatorIdxByIndicatorId(string indicatorId) + { + for(int i=0; i= 0) + { + if(CopyBuffer(indicators[idx].indicatorHandle, i, fromDate, 1, values) < 0) + { + if(mControl.mGetLastError()) + { + CJAVal message; + int lastError = mControl.mGetLastError(); + string desc = mControl.mGetDesc(); + mControl.Check(); + + message["error"]=(bool) true; + message["lastError"]=(string) lastError; + message["description"]=desc; + message["function"]=(string) __FUNCTION__; + string t=message.Serialize(); + if(debug) + Print(t); + InformClientSocket(indicatorDataSocket,t); + } + } + results[i] = DoubleToString(values[0]); + } + } + + CJAVal message; + message["error"]=(bool) false; + message["id"] = (string) id; + message["data"].Set(results); + + string t=message.Serialize(); + if(debug) + Print(t); + InformClientSocket(indicatorDataSocket,t); + + } +//+------------------------------------------------------------------+ diff --git a/Indicators/JsonAPIIndicator.mq5 b/Indicators/JsonAPIIndicator.mq5 index 09c9772..c2912d0 100644 --- a/Indicators/JsonAPIIndicator.mq5 +++ b/Indicators/JsonAPIIndicator.mq5 @@ -21,26 +21,20 @@ Context context("MQL5 JSON API"); Socket chartSubscriptionSocket(context,ZMQ_SUB); //--- input parameters -#property indicator_buffers 21 -#property indicator_plots 20 -#property indicator_label1 "JsonAPI" -#property indicator_type1 DRAW_NONE -#property indicator_type2 DRAW_NONE -#property indicator_type3 DRAW_NONE -//#property indicator_color3 CLR_NONE -#property indicator_type4 DRAW_NONE -#property indicator_type5 DRAW_NONE +#property indicator_buffers 31 +#property indicator_plots 30 input string IndicatorId=""; -input string ShortName="JsonAPI"; +input string ShortName="JsonAPIIndicator"; //--- indicator settings -double B0[], B1[], B2[], B3[], B4[], B5[], B6[], B7[], B8[], B9[], B10[], B11[], B12[], B13[], B14[], B15[], B16[], B17[], B18[], B19[], alive[]; -bool debug = true; -bool first = false; +double B0[], B1[], B2[], B3[], B4[], B5[], B6[], B7[], B8[], B9[], B10[]; +double B11[], B12[], B13[], B14[], B15[], B16[], B17[], B18[], B19[], B20[]; +double B21[], B22[], B23[], B24[], B25[], B26[], B27[], B28[], B29[]; +bool debug = false; int activeBufferCount = 0; - -int activeBufferCount = 0; +long mtChartId = 0; +bool setFormingCandleBlank = true; //+------------------------------------------------------------------+ //| Custom indicator initialization function | @@ -48,21 +42,24 @@ int activeBufferCount = 0; int OnInit() { +// TODO subscribe only to own IndicatorId topic +// Subscribe to all topics + chartSubscriptionSocket.setSubscribe(""); + chartSubscriptionSocket.setLinger(1000); +// https://www.randonomicon.com/zmq/2018/09/19/zmq-bind-vs-connect.html +// Number of messages to buffer in RAM. +// https://dzone.com/articles/zeromq-flow-control-and-other + chartSubscriptionSocket.setReceiveHighWaterMark(1000); + bool result = chartSubscriptionSocket.connect(StringFormat("tcp://%s:%d", HOST, CHART_SUB_PORT)); + if(result == false) + { + Print("Failed to subscrbe on port ", CHART_SUB_PORT); + } + else + { + Print("Accepting Chart Indicator data on port ", CHART_SUB_PORT); + } - bool result = chartSubscriptionSocket.connect(StringFormat("tcp://%s:%d", HOST, CHART_SUB_PORT)); - if (result == false) {Print("Failed to subscrbe on port ", CHART_SUB_PORT);} - else { - Print("Accepting Chart Indicator data on port ", CHART_SUB_PORT); - // TODO subscribe only to own IndicatorId topic - // Subscribe to all topics - chartSubscriptionSocket.setSubscribe(""); - //chartSubscriptionSocket.setLinger(1000); - chartSubscriptionSocket.setLinger(10000); - // Number of messages to buffer in RAM. - chartSubscriptionSocket.setReceiveHighWaterMark(5); // TODO confirm settings - } - - //--- indicator buffers mapping; ArraySetAsSeries(B0,true); ArraySetAsSeries(B1,true); @@ -84,46 +81,75 @@ int OnInit() ArraySetAsSeries(B17,true); ArraySetAsSeries(B18,true); ArraySetAsSeries(B19,true); - ArraySetAsSeries(alive,true); + ArraySetAsSeries(B20,true); + ArraySetAsSeries(B21,true); + ArraySetAsSeries(B22,true); + ArraySetAsSeries(B23,true); + ArraySetAsSeries(B24,true); + ArraySetAsSeries(B25,true); + ArraySetAsSeries(B26,true); + ArraySetAsSeries(B27,true); + ArraySetAsSeries(B28,true); + ArraySetAsSeries(B29,true); - SetIndexBuffer(0,B0,INDICATOR_DATA); - SetIndexBuffer(1,B1,INDICATOR_DATA); - SetIndexBuffer(2,B2,INDICATOR_DATA); - SetIndexBuffer(3,B3,INDICATOR_DATA); - SetIndexBuffer(4,B4,INDICATOR_DATA); - SetIndexBuffer(5,B5,INDICATOR_DATA); - SetIndexBuffer(6,B6,INDICATOR_DATA); - SetIndexBuffer(7,B7,INDICATOR_DATA); - SetIndexBuffer(8,B8,INDICATOR_DATA); - SetIndexBuffer(9,B9,INDICATOR_DATA); - SetIndexBuffer(10,B10,INDICATOR_DATA); - SetIndexBuffer(11,B11,INDICATOR_DATA); - SetIndexBuffer(12,B12,INDICATOR_DATA); - SetIndexBuffer(13,B13,INDICATOR_DATA); - SetIndexBuffer(14,B14,INDICATOR_DATA); - SetIndexBuffer(15,B15,INDICATOR_DATA); - SetIndexBuffer(16,B16,INDICATOR_DATA); - SetIndexBuffer(17,B17,INDICATOR_DATA); - SetIndexBuffer(18,B18,INDICATOR_DATA); - SetIndexBuffer(19,B19,INDICATOR_DATA); - SetIndexBuffer(20,alive,INDICATOR_CALCULATIONS); // If the buffer index changes, the line starting with "CopyBuffer(chartWindowIndicators[i].indicatorHandle," in JsonAPI.mq5 has to be updated + SetIndexBuffer(0,B0,INDICATOR_CALCULATIONS); + SetIndexBuffer(1,B1,INDICATOR_CALCULATIONS); + SetIndexBuffer(2,B2,INDICATOR_CALCULATIONS); + SetIndexBuffer(3,B3,INDICATOR_CALCULATIONS); + SetIndexBuffer(4,B4,INDICATOR_CALCULATIONS); + SetIndexBuffer(5,B5,INDICATOR_CALCULATIONS); + SetIndexBuffer(6,B6,INDICATOR_CALCULATIONS); + SetIndexBuffer(7,B7,INDICATOR_CALCULATIONS); + SetIndexBuffer(8,B8,INDICATOR_CALCULATIONS); + SetIndexBuffer(9,B9,INDICATOR_CALCULATIONS); + SetIndexBuffer(10,B10,INDICATOR_CALCULATIONS); + SetIndexBuffer(11,B11,INDICATOR_CALCULATIONS); + SetIndexBuffer(12,B12,INDICATOR_CALCULATIONS); + SetIndexBuffer(13,B13,INDICATOR_CALCULATIONS); + SetIndexBuffer(14,B14,INDICATOR_CALCULATIONS); + SetIndexBuffer(15,B15,INDICATOR_CALCULATIONS); + SetIndexBuffer(16,B16,INDICATOR_CALCULATIONS); + SetIndexBuffer(17,B17,INDICATOR_CALCULATIONS); + SetIndexBuffer(18,B18,INDICATOR_CALCULATIONS); + SetIndexBuffer(19,B19,INDICATOR_CALCULATIONS); + SetIndexBuffer(20,B20,INDICATOR_CALCULATIONS); + SetIndexBuffer(21,B21,INDICATOR_CALCULATIONS); + SetIndexBuffer(22,B22,INDICATOR_CALCULATIONS); + SetIndexBuffer(23,B23,INDICATOR_CALCULATIONS); + SetIndexBuffer(24,B24,INDICATOR_CALCULATIONS); + SetIndexBuffer(25,B25,INDICATOR_CALCULATIONS); + SetIndexBuffer(26,B26,INDICATOR_CALCULATIONS); + SetIndexBuffer(27,B27,INDICATOR_CALCULATIONS); + SetIndexBuffer(28,B28,INDICATOR_CALCULATIONS); + SetIndexBuffer(29,B29,INDICATOR_CALCULATIONS); - //--- IndicatorSetString(INDICATOR_SHORTNAME,ShortName); return(INIT_SUCCEEDED); } - -void SetStyle(int bufferIdx, string linelabel, color colorstyle, int linetype, int linestyle, int linewidth) { - PlotIndexSetString(bufferIdx,PLOT_LABEL,linelabel); - PlotIndexSetInteger(bufferIdx,PLOT_LINE_COLOR,0,colorstyle); - PlotIndexSetInteger(bufferIdx,PLOT_DRAW_TYPE,linetype); - PlotIndexSetInteger(bufferIdx,PLOT_LINE_STYLE,linestyle); - PlotIndexSetInteger(bufferIdx,PLOT_LINE_WIDTH,linewidth); -} - +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +// Print("INDI DEINIT ",reason); + } + + +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +void SetStyle(int bufferIdx, string linelabel, color colorstyle, int linetype, int linestyle, int linewidth) + { + PlotIndexSetString(bufferIdx,PLOT_LABEL,linelabel); + PlotIndexSetInteger(bufferIdx,PLOT_LINE_COLOR,0,colorstyle); + PlotIndexSetInteger(bufferIdx,PLOT_DRAW_TYPE,linetype); + PlotIndexSetInteger(bufferIdx,PLOT_LINE_STYLE,linestyle); + PlotIndexSetInteger(bufferIdx,PLOT_LINE_WIDTH,linewidth); + } + //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ @@ -138,151 +164,445 @@ int OnCalculate(const int rates_total, const long &volume[], const int &spread[]) { - // While a new candle is forming, set the current value to be empty - if(rates_total>prev_calculated){ - B0[0] = EMPTY_VALUE; - B1[0] = EMPTY_VALUE; - B2[0] = EMPTY_VALUE; - B3[0] = EMPTY_VALUE; - B4[0] = EMPTY_VALUE; - B5[0] = EMPTY_VALUE; - B6[0] = EMPTY_VALUE; - B7[0] = EMPTY_VALUE; - B8[0] = EMPTY_VALUE; - B9[0] = EMPTY_VALUE; - B10[0] = EMPTY_VALUE; - B11[0] = EMPTY_VALUE; - B12[0] = EMPTY_VALUE; - B13[0] = EMPTY_VALUE; - B14[0] = EMPTY_VALUE; - B15[0] = EMPTY_VALUE; - B16[0] = EMPTY_VALUE; - B17[0] = EMPTY_VALUE; - B18[0] = EMPTY_VALUE; - B19[0] = EMPTY_VALUE; - } - if(first==false) alive[0] = 1; - // ChartRedraw(0); - +// While a new candle is forming, set the current value to be empty + + + if(rates_total>prev_calculated && setFormingCandleBlank) + { + B0[0] = EMPTY_VALUE; + B1[0] = EMPTY_VALUE; + B2[0] = EMPTY_VALUE; + B3[0] = EMPTY_VALUE; + B4[0] = EMPTY_VALUE; + B5[0] = EMPTY_VALUE; + B6[0] = EMPTY_VALUE; + B7[0] = EMPTY_VALUE; + B8[0] = EMPTY_VALUE; + B9[0] = EMPTY_VALUE; + B10[0] = EMPTY_VALUE; + B11[0] = EMPTY_VALUE; + B12[0] = EMPTY_VALUE; + B13[0] = EMPTY_VALUE; + B14[0] = EMPTY_VALUE; + B15[0] = EMPTY_VALUE; + B16[0] = EMPTY_VALUE; + B17[0] = EMPTY_VALUE; + B18[0] = EMPTY_VALUE; + B19[0] = EMPTY_VALUE; + B20[0] = EMPTY_VALUE; + B21[0] = EMPTY_VALUE; + B22[0] = EMPTY_VALUE; + B23[0] = EMPTY_VALUE; + B24[0] = EMPTY_VALUE; + B25[0] = EMPTY_VALUE; + B26[0] = EMPTY_VALUE; + B27[0] = EMPTY_VALUE; + B28[0] = EMPTY_VALUE; + B29[0] = EMPTY_VALUE; + } + //--- return value of prev_calculated for next call return(rates_total); } -void SubscriptionHandler(ZmqMsg &chartMsg){ - CJAVal message; - // Get data from request - string msg=chartMsg.getData(); - if(debug) Print("Processing:"+msg); - // Deserialize msg to CJAVal array - if(!message.Deserialize(msg)){ - Alert("Deserialization Error"); - ExpertRemove(); - } - if(message["indicatorChartId"]==IndicatorId) { +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +void SubscriptionHandler(ZmqMsg &chartMsg) + { + CJAVal message; +// Get data from request + string msg=chartMsg.getData(); + if(debug) + Print("Processing:"+msg); +// Deserialize msg to CJAVal array + if(!message.Deserialize(msg)) + { + Alert("Deserialization Error"); + ExpertRemove(); + } + if(message["chartIndicatorId"]==IndicatorId) + { - if(message["action"]=="PLOT" && message["actionType"]=="DATA") { - int bufferIdx = message["indicatorBufferId"].ToInt(); - if (bufferIdx == 0) WriteToBuffer(message, B0); - if (bufferIdx == 1) WriteToBuffer(message, B1); - if (bufferIdx == 2) WriteToBuffer(message, B2); - if (bufferIdx == 3) WriteToBuffer(message, B3); - if (bufferIdx == 4) WriteToBuffer(message, B4); - if (bufferIdx == 5) WriteToBuffer(message, B5); - if (bufferIdx == 6) WriteToBuffer(message, B6); - if (bufferIdx == 7) WriteToBuffer(message, B7); - if (bufferIdx == 8) WriteToBuffer(message, B8); - if (bufferIdx == 9) WriteToBuffer(message, B9); - if (bufferIdx == 10) WriteToBuffer(message, B10); - if (bufferIdx == 11) WriteToBuffer(message, B11); - if (bufferIdx == 12) WriteToBuffer(message, B12); - if (bufferIdx == 13) WriteToBuffer(message, B13); - if (bufferIdx == 14) WriteToBuffer(message, B14); - if (bufferIdx == 15) WriteToBuffer(message, B15); - if (bufferIdx == 16) WriteToBuffer(message, B16); - if (bufferIdx == 17) WriteToBuffer(message, B17); - if (bufferIdx == 18) WriteToBuffer(message, B18); - if (bufferIdx == 19) WriteToBuffer(message, B19); - } - else if(message["action"]=="PLOT" && message["actionType"]=="ADDBUFFER") { - string linelabel = message["style"]["linelabel"].ToStr(); - string colorstyleStr = message["style"]["color"].ToStr(); - string linetypeStr = message["style"]["linetype"].ToStr(); - string linestyleStr = message["style"]["linestyle"].ToStr(); - int linewidth = message["style"]["linewidth"].ToInt(); - - color colorstyle = StringToColor(colorstyleStr); - int linetype = StringToEnumInt(linetypeStr); - int linestyle = StringToEnumInt(linestyleStr); - - /* - //if (aa == false) { - Print("SETBUFF ActCount ",activeBufferCount); - if (activeBufferCount == 0) {SetIndexBuffer(0,B1,INDICATOR_DATA);} // Two semicolons ar required! No idea why. Seems to be a timing problem, better to keep it in init() - - if (activeBufferCount == 1) {SetIndexBuffer(1,B2,INDICATOR_DATA);;} - if (activeBufferCount == 2) {SetIndexBuffer(2,B3,INDICATOR_DATA);;} - if (activeBufferCount == 3) {SetIndexBuffer(3,B4,INDICATOR_DATA);;} - if (activeBufferCount == 4) {SetIndexBuffer(4,B5,INDICATOR_DATA);;} + if(message["action"]=="PLOT" && message["actionType"]=="DATA") + { + int bufferIdx = message["indicatorBufferId"].ToInt(); + if(bufferIdx == 0) + { + WriteToBuffer(message, B0); + SetIndexBuffer(0,B0,INDICATOR_DATA); + } + if(bufferIdx == 1) + { + WriteToBuffer(message, B1); + SetIndexBuffer(1,B1,INDICATOR_DATA); + } + if(bufferIdx == 2) + { + WriteToBuffer(message, B2); + SetIndexBuffer(2,B2,INDICATOR_DATA); + } + if(bufferIdx == 3) + { + WriteToBuffer(message, B3); + SetIndexBuffer(3,B3,INDICATOR_DATA); + } + if(bufferIdx == 4) + { + WriteToBuffer(message, B4); + SetIndexBuffer(4,B4,INDICATOR_DATA); + } + if(bufferIdx == 5) + { + WriteToBuffer(message, B5); + SetIndexBuffer(5,B5,INDICATOR_DATA); + } + if(bufferIdx == 6) + { + WriteToBuffer(message, B6); + SetIndexBuffer(6,B6,INDICATOR_DATA); + } + if(bufferIdx == 7) + { + WriteToBuffer(message, B7); + SetIndexBuffer(7,B7,INDICATOR_DATA); + } + if(bufferIdx == 8) + { + WriteToBuffer(message, B8); + SetIndexBuffer(8,B8,INDICATOR_DATA); + } + if(bufferIdx == 9) + { + WriteToBuffer(message, B9); + SetIndexBuffer(9,B9,INDICATOR_DATA); + } + if(bufferIdx == 10) + { + WriteToBuffer(message, B10); + SetIndexBuffer(10,B10,INDICATOR_DATA); + } + if(bufferIdx == 11) + { + WriteToBuffer(message, B11); + SetIndexBuffer(11,B11,INDICATOR_DATA); + } + if(bufferIdx == 12) + { + WriteToBuffer(message, B12); + SetIndexBuffer(12,B12,INDICATOR_DATA); + } + if(bufferIdx == 13) + { + WriteToBuffer(message, B13); + SetIndexBuffer(13,B13,INDICATOR_DATA); + } + if(bufferIdx == 14) + { + WriteToBuffer(message, B14); + SetIndexBuffer(14,B14,INDICATOR_DATA); + } + if(bufferIdx == 15) + { + WriteToBuffer(message, B15); + SetIndexBuffer(15,B15,INDICATOR_DATA); + } + if(bufferIdx == 16) + { + WriteToBuffer(message, B16); + SetIndexBuffer(16,B16,INDICATOR_DATA); + } + if(bufferIdx == 17) + { + WriteToBuffer(message, B17); + SetIndexBuffer(17,B17,INDICATOR_DATA); + } + if(bufferIdx == 18) + { + WriteToBuffer(message, B18); + SetIndexBuffer(18,B18,INDICATOR_DATA); + } + if(bufferIdx == 19) + { + WriteToBuffer(message, B19); + SetIndexBuffer(19,B19,INDICATOR_DATA); + } + if(bufferIdx == 20) + { + WriteToBuffer(message, B20); + SetIndexBuffer(20,B20,INDICATOR_DATA); + } + if(bufferIdx == 21) + { + WriteToBuffer(message, B21); + SetIndexBuffer(21,B21,INDICATOR_DATA); + } + if(bufferIdx == 22) + { + WriteToBuffer(message, B22); + SetIndexBuffer(22,B22,INDICATOR_DATA); + } + if(bufferIdx == 23) + { + WriteToBuffer(message, B23); + SetIndexBuffer(23,B23,INDICATOR_DATA); + } + if(bufferIdx == 24) + { + WriteToBuffer(message, B24); + SetIndexBuffer(24,B24,INDICATOR_DATA); + } + if(bufferIdx == 25) + { + WriteToBuffer(message, B25); + SetIndexBuffer(25,B25,INDICATOR_DATA); + } + if(bufferIdx == 26) + { + WriteToBuffer(message, B26); + SetIndexBuffer(26,B26,INDICATOR_DATA); + } + if(bufferIdx == 27) + { + WriteToBuffer(message, B27); + SetIndexBuffer(27,B27,INDICATOR_DATA); + } + if(bufferIdx == 28) + { + WriteToBuffer(message, B28); + SetIndexBuffer(28,B28,INDICATOR_DATA); + } + if(bufferIdx == 29) + { + WriteToBuffer(message, B29); + SetIndexBuffer(29,B29,INDICATOR_DATA); + } + ChartRedraw(mtChartId); + } + else + if(message["action"]=="PLOT" && message["actionType"]=="ADDBUFFER") + { + string linelabel = message["style"]["linelabel"].ToStr(); + string colorstyleStr = message["style"]["color"].ToStr(); + string linetypeStr = message["style"]["linetype"].ToStr(); + string linestyleStr = message["style"]["linestyle"].ToStr(); + int linewidth = message["style"]["linewidth"].ToInt(); + setFormingCandleBlank = message["style"]["blankforming"].ToBool(); - //aa = true;} - */ - - SetStyle(activeBufferCount, linelabel, colorstyle, linetype, linestyle, linewidth); - activeBufferCount = activeBufferCount + 1; - } + color colorstyle = StringToColor(colorstyleStr); + int linetype = StringToEnumInt(linetypeStr); + int linestyle = StringToEnumInt(linestyleStr); + + SetStyle(activeBufferCount, linelabel, colorstyle, linetype, linestyle, linewidth); + activeBufferCount = activeBufferCount + 1; + + ClearBuffer(activeBufferCount-1); + } + } + } + +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +void Clear(double &buffer[]) + { + int bufferSize = ArraySize(buffer); + for(int i=0; i= EMPTY_VALUE) + val = EMPTY_VALUE; + buffer[i+offset] = val; + } + } } - // Set the most recent plotted value to nothing, as we do not have any data for yet unformed candles - buffer[0] = EMPTY_VALUE; -} //+------------------------------------------------------------------+ //| Check for new indicator data function | //+------------------------------------------------------------------+ -void CheckMessages(){ - // This is a workaround for Timer(). It is needed, because OnTimer() works if the indicator is manually added to a chart, but not with ChartIndicatorAdd() +void CheckMessages() + { +// This is a workaround for Timer(). It is needed, because OnTimer() works if the indicator is manually added to a chart, but not with ChartIndicatorAdd() - ZmqMsg chartMsg; + ZmqMsg chartMsg; - // Recieve chart instructions stream from client via live Chart socket. - chartSubscriptionSocket.recv(chartMsg,true); +// Recieve chart instructions stream from client via live Chart socket. + chartSubscriptionSocket.recv(chartMsg,true); - // Request recieved - if(chartMsg.size()>0){ - // Handle subscription SubscriptionHandler() - SubscriptionHandler(chartMsg); - ChartRedraw(ChartID()); +// Request recieved + if(chartMsg.size()>0) + { + // Handle subscription SubscriptionHandler() + SubscriptionHandler(chartMsg); + ChartRedraw(ChartID()); + } } -} //+------------------------------------------------------------------+ //| OnTimer() workaround function | @@ -293,6 +613,7 @@ void OnChartEvent(const int id, const double &dparam, const string &sparam) { - if(id==CHARTEVENT_CUSTOM+222) CheckMessages(); + if(id==CHARTEVENT_CUSTOM+222) + CheckMessages(); } //+---------------------------------------------------- diff --git a/changelog.md b/changelog.md index c4bb433..0671778 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +### Marth 6th +- fixed tick data retrival. When requesting tick data, every other tick was skipped +- refactored code + ### 30th April 2020 - add support for spreads