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