//+------------------------------------------------------------------+ // // 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: Experation // TODO: Devitation // TODO: Add comments // TODO: Rewrite historical data code // TODO: Fetch trades // TODO: Candles data format // TODO: If no historical data push last date // TODO: Rewrite disconnected func #property copyright "Copyright 2019, Nikolai Khramkov." #property link "https://github.com/khramkov" #property version "1.12" #property description "MQL5 JSON API" #property description "See github link for documentation" #include #include #include #include string HOST="*"; int SYS_PORT=15555; int DATA_PORT=15556; int LIVE_PORT=15557; int STR_PORT=15558; // 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); // Global variables bool debug = true; bool liveStram = true; datetime lastBar = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { /* Bindinig ZMQ ports on init */ // OnTimer() function event genegation - 1 millisecond EventSetMillisecondTimer(1); sysSocket.bind(StringFormat("tcp://%s:%d",HOST,SYS_PORT)); dataSocket.bind(StringFormat("tcp://%s:%d",HOST,DATA_PORT)); liveSocket.bind(StringFormat("tcp://%s:%d",HOST,LIVE_PORT)); streamSocket.bind(StringFormat("tcp://%s:%d",HOST,STR_PORT)); Print("Binding 'System' socket on port "+IntegerToString(SYS_PORT)+"..."); Print("Binding 'Data' socket on port "+IntegerToString(DATA_PORT)+"..."); Print("Binding 'Live' socket on port "+IntegerToString(LIVE_PORT)+"..."); Print("Binding 'Streaming' socket on port "+IntegerToString(STR_PORT)+"..."); sysSocket.setLinger(1000); dataSocket.setLinger(1000); liveSocket.setLinger(1000); streamSocket.setLinger(1000); // Number of messages to buffer in RAM. sysSocket.setSendHighWaterMark(1); dataSocket.setSendHighWaterMark(3); liveSocket.setSendHighWaterMark(1); streamSocket.setSendHighWaterMark(100); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { /* Unbinding ZMQ ports on denit */ Print(__FUNCTION__," Deinitialization reason code = ",reason); sysSocket.unbind(StringFormat("tcp://%s:%d",HOST,SYS_PORT)); dataSocket.unbind(StringFormat("tcp://%s:%d",HOST,DATA_PORT)); liveSocket.unbind(StringFormat("tcp://%s:%d",HOST,LIVE_PORT)); streamSocket.unbind(StringFormat("tcp://%s:%d",HOST,STR_PORT)); Print("Unbinding 'System' socket on port "+IntegerToString(SYS_PORT)+".."); Print("Unbinding 'Data' socket on port "+IntegerToString(DATA_PORT)+".."); Print("Unbinding 'Live' socket on port "+IntegerToString(LIVE_PORT)+".."); Print("Unbinding 'Streaming' socket on port "+IntegerToString(STR_PORT)+"..."); } //+------------------------------------------------------------------+ //| Expert timer function | //+------------------------------------------------------------------+ void OnTimer() { 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); } // If live stream == true, push last candle to liveSocket. if(liveStram) { // Check if terminal connected to market if(TerminalInfoInteger(TERMINAL_CONNECTED)) { datetime thisBar=(datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_LASTBAR_DATE); if(lastBar!=thisBar) { MqlRates rates[1]; CJAVal candle; if(CopyRates(_Symbol,_Period,1,1,rates)!=1) { /*error processing */ }; candle[0] = (long) rates[0].time; candle[1] = (double) rates[0].open; candle[2] = (double) rates[0].high; candle[3] = (double) rates[0].low; candle[4] = (double) rates[0].close; candle[5] = (double) rates[0].tick_volume; string t=candle.Serialize(); if(debug) Print(t); InformClientSocket(liveSocket,t); lastBar=thisBar; } } // If disconnected from market // else InformClientSocket(liveSocket, "DISCONNECTED"); } } //+------------------------------------------------------------------+ //| Request handler | //+------------------------------------------------------------------+ void RequestHandler(ZmqMsg &request) { CJAVal message; ResetLastError(); // Get data from reguest string msg=request.getData(); if(debug) Print("Processing:"+msg); // Deserialize msg to CJAVal array if(!message.Deserialize(msg)) { ActionDoneOrError(65537, __FUNCTION__); Alert("Deserialization Error"); ExpertRemove(); } // Send response to System socket that request is received // Some historical data requests can take a lot of time InformClientSocket(sysSocket, "OK"); // Process action command string action = message["action"].ToStr(); if(action=="CONFIG") {ScriptConfiguration(message);} else if(action=="ACCOUNT") {GetAccountInfo();} else if(action=="BALANCE") {GetBalanceInfo();} else if(action=="HISTORY") {HistoryInfo(message);} else if(action=="TRADE") {TradingModule(message);} else if(action=="POSITIONS") {GetPositions(message);} else if(action=="ORDERS") {GetOrders(message);} // Action command error processing else ActionDoneOrError(65538, __FUNCTION__); } //+------------------------------------------------------------------+ //| Reconfigure the script params | //+------------------------------------------------------------------+ void ScriptConfiguration(CJAVal &dataObject) { string symb=dataObject["symbol"].ToStr(); ENUM_TIMEFRAMES tf=GetTimeframe(dataObject["chartTF"].ToStr()); // If the symbol and(or) TF are different from the chart values if(!(tf == _Period & symb == _Symbol)) { // Check if symbol exists if(SymbolInfoInteger(symb, SYMBOL_EXIST)) { // Set chart symbol and TF if(ChartSetSymbolPeriod(0, symb, tf)) // All done ActionDoneOrError(ERR_SUCCESS, __FUNCTION__); // Error Handling else ActionDoneOrError(ERR_MARKET_WRONG_PROPERTY, __FUNCTION__); } else ActionDoneOrError(ERR_MARKET_UNKNOWN_SYMBOL, __FUNCTION__); } // Nothing to change else ActionDoneOrError(ERR_SUCCESS, __FUNCTION__); } //+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+ //| Get historical data | //+------------------------------------------------------------------+ void HistoryInfo(CJAVal &dataObject) { string actionType = dataObject["actionType"].ToStr(); if(actionType=="DATA") { CJAVal c; MqlRates r[]; int copied; string symbol=dataObject["symbol"].ToStr(); ENUM_TIMEFRAMES period=GetTimeframe(dataObject["chartTF"].ToStr()); datetime fromDate=(datetime)dataObject["fromDate"].ToInt(); datetime toDate=TimeCurrent(); if(dataObject["toDate"]) toDate=(datetime)dataObject["toDate"].ToInt(); if(debug) { Print("Fetching HISTORY"); Print("1) Symbol:"+symbol); Print("2) Timeframe:"+EnumToString(period)); Print("3) Date from:"+TimeToString(fromDate)); } copied=CopyRates(symbol,period,fromDate,TimeCurrent(),r); if(copied) { for(int i=0;i