//+------------------------------------------------------------------+
//
// 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