//+------------------------------------------------------------------+ // // 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: Close position // TODO: Close orders // TODO: Check If SymbolExist // TODO: Check TF according to request -> change // TODO: Check chart symbol according to request -> change // TODO: RETURN OK or error // TODO: Write comment about sockets // TODO: Add defauld dict check // TODO: Change description // TODO: Comissinos #property copyright "Copyright 2019, Nikolai Khramkov." #property link "https://github.com/khramkov" #property version "0.70" #include #include extern string PROJECT_NAME="Backtrader <-> Metatrader 5 interface"; extern string PROTOCOL="tcp"; extern string HOST="*"; extern int SYS_PORT=15555; extern int DATA_PORT=15556; extern int LIVE_PORT=15557; extern int MILLISECOND_TIMER=1; // 1 millisecond // ZeroMQ Cnnections Context context(PROJECT_NAME); Socket sysSocket(context,ZMQ_REP); Socket dataSocket(context,ZMQ_PUSH); Socket liveSocket(context,ZMQ_PUSH); // Global variables bool debug = true; datetime lastBar = 0; string symbol = _Symbol; ENUM_TIMEFRAMES period = _Period; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { /* Bindinig ZMQ ports on init */ // Set Millisecond Timer to get client socket input EventSetMillisecondTimer(MILLISECOND_TIMER); Print("[REP] Binding System Socket on port "+IntegerToString(SYS_PORT)+"..."); sysSocket.bind(StringFormat("%s://%s:%d",PROTOCOL,HOST,SYS_PORT)); Print("[PUSH] Binding Data Socket on port "+IntegerToString(DATA_PORT)+"..."); dataSocket.bind(StringFormat("%s://%s:%d",PROTOCOL,HOST,DATA_PORT)); Print("[PUSH] Binding Live Socket on port "+IntegerToString(LIVE_PORT)+"..."); liveSocket.bind(StringFormat("%s://%s:%d",PROTOCOL,HOST,LIVE_PORT)); // Maximum amount of time in milliseconds that the thread will try to send messages // after its socket has been closed (the default value of -1 means to linger forever): sysSocket.setLinger(1000); // How many messages do we want ZeroMQ to buffer in RAM before blocking the socket? // 3 messages only. sysSocket.setSendHighWaterMark(3); dataSocket.setSendHighWaterMark(3); liveSocket.setSendHighWaterMark(3); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { /* Closing ports on denit */ Print("[REP] Unbinding socket on port "+IntegerToString(SYS_PORT)+".."); sysSocket.unbind(StringFormat("%s://%s:%d",PROTOCOL,HOST,SYS_PORT)); Print("[PUSH] Unbinding socket on port "+IntegerToString(DATA_PORT)+".."); dataSocket.unbind(StringFormat("%s://%s:%d",PROTOCOL,HOST,DATA_PORT)); Print("[PUSH] Unbinding socket on port "+IntegerToString(LIVE_PORT)+".."); liveSocket.unbind(StringFormat("%s://%s:%d",PROTOCOL,HOST,LIVE_PORT)); } //+------------------------------------------------------------------+ //| Expert timer function | //+------------------------------------------------------------------+ void OnTimer() { ZmqMsg request; // Get client's response, but don't wait. sysSocket.recv(request,true); // Generating reply by passing request to ZmqMsg MessageHandler() function. ZmqMsg reply=MessageHandler(request); // Reply to client via REP socket. sysSocket.send(reply); } //+------------------------------------------------------------------+ //| Request handler | //+------------------------------------------------------------------+ ZmqMsg MessageHandler(ZmqMsg &request) { ZmqMsg reply; CJAVal message; if(request.size()>0) { string msg=request.getData(); if(debug==true) {Print("Processing request:"+msg);} if(!message.Deserialize(msg)) { Alert("Deserialization Error"); //ExpertRemove(); //return; } string action = message["action"].ToStr(); if(action=="CHECK") {CheckConnection(&dataSocket, &liveSocket);} else if(action=="CONFIG") {ConfigScript(&dataSocket, message);} else if(action=="ACCOUNT") {AccountInfo(&dataSocket);} else if(action=="BALANCE") {BalanceInfo(&dataSocket);} else if(action=="HISTORY") {HistoryInfo(&dataSocket, message);} else if(action=="POSITIONS_INFO") {GetPositionsInfo(&dataSocket);} else if(action=="ORDERS_INFO") {GetOrdersInfo(&dataSocket);} else if(action=="TRADE") {TradingModule(&dataSocket, message);} else {} // error processing // Construct response ZmqMsg ret("OK"); reply=ret; } else { // NO DATA RECEIVED ZmqMsg ret("FALSE"); reply=ret; } return(reply); } //+------------------------------------------------------------------+ //| Check sockets connection | //+------------------------------------------------------------------+ void CheckConnection (Socket &dataSocket, Socket &liveSocket) { InformClientSocket(dataSocket,"OK"); InformClientSocket(liveSocket,"OK"); } //+------------------------------------------------------------------+ //| Reconfigure the script params | //+------------------------------------------------------------------+ void ConfigScript(Socket &dataSocket, CJAVal &dataObject) { } //+------------------------------------------------------------------+ //| Change chart timeframe | //+------------------------------------------------------------------+ void CheckTimeframe(Socket &dataSocket, CJAVal &dataObject) { string chartTF=dataObject["chartTF"].ToStr(); if(chartTF=="1m") {period=PERIOD_M1;} else if(chartTF=="5m") {period=PERIOD_M5;} else if(chartTF=="15m") {period=PERIOD_M15;} else if(chartTF=="30m") {period=PERIOD_M30;} else if(chartTF=="1h") {period=PERIOD_H1;} else if(chartTF=="2h") {period=PERIOD_H2;} else if(chartTF=="3h") {period=PERIOD_H3;} else if(chartTF=="4h") {period=PERIOD_H4;} else if(chartTF=="6h") {period=PERIOD_H6;} else if(chartTF=="8h") {period=PERIOD_H8;} else if(chartTF=="12h") {period=PERIOD_H12;} else if(chartTF=="1d") {period=PERIOD_D1;} else if(chartTF=="1w") {period=PERIOD_W1;} else if(chartTF=="1M") {period=PERIOD_MN1;} } //+------------------------------------------------------------------+ //| Account information | //+------------------------------------------------------------------+ void AccountInfo(Socket &dataSocket) { CJAVal info; info["brocker"] = AccountInfoString(ACCOUNT_COMPANY); info["currency"] = AccountInfoString(ACCOUNT_CURRENCY); info["server"] = AccountInfoString(ACCOUNT_SERVER); 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); info["bot_trading"] = AccountInfoInteger(ACCOUNT_TRADE_EXPERT); string t=info.Serialize(); if(debug==true) {Print(t);} InformClientSocket(dataSocket,t); } //+------------------------------------------------------------------+ //| Balance information | //+------------------------------------------------------------------+ void BalanceInfo(Socket &dataSocket) { 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==true) {Print(t);} InformClientSocket(dataSocket,t); } //+------------------------------------------------------------------+ //| Get historical data | //+------------------------------------------------------------------+ void HistoryInfo(Socket &dataSocket, CJAVal &dataObject) { CJAVal candles; MqlRates rates[]; int copied; string actionType=dataObject["actionType"].ToStr(); string symbol=dataObject["symbol"].ToStr(); string chartTF=dataObject["chartTF"].ToStr(); datetime startTime=dataObject["startTime"].ToInt(); if(debug==true) { Print("Fetching HISTORY"); Print("1) Symbol:"+symbol); Print("2) Timeframe:"+chartTF); Print("3) Date from:"+TimeToString(startTime)); } copied=CopyRates(symbol,period,startTime,TimeCurrent(),rates); if(copied) { for(int i=0;i