Files
MQL5-JSON-API-2/Experts/api.mq5
T

566 lines
42 KiB
Plaintext
Raw Normal View History

2019-05-05 11:58:16 +03:00
//+------------------------------------------------------------------+
//
// 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 <http://www.gnu.org/licenses/>.
//
//+------------------------------------------------------------------+
// TODO: Comissinos
// TODO: Experation
// TODO: Devitation
// TODO: Add comments
// TODO: Standard O/P reply
#property copyright "Copyright 2019, Nikolai Khramkov."
#property link "https://github.com/khramkov"
#property version "1.00"
#property description "MQL5 JSON API"
#property description "See github link for documentation"
#include <Trade/AccountInfo.mqh>
#include <Trade/Trade.mqh>
#include <Zmq/Zmq.mqh>
#include <json.mqh>
string HOST="*";
int SYS_PORT=15555;
int DATA_PORT=15556;
int LIVE_PORT=15557;
// ZeroMQ Cnnections
Context context("MQL5 JSON API");
Socket sysSocket(context,ZMQ_REP);
Socket dataSocket(context,ZMQ_PUSH);
Socket liveSocket(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));
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)+"...");
sysSocket.setLinger(1000);
// Number of messages to buffer in RAM.
sysSocket.setSendHighWaterMark(1);
dataSocket.setSendHighWaterMark(1);
liveSocket.setSendHighWaterMark(1);
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));
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)+"..");
}
//+------------------------------------------------------------------+
//| 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() and get reply.
string reply = RequestHandler(request);
// Pull reply to client via System socket.
InformClientSocket(sysSocket, reply);
}
// If live stream == true, push last candle to liveSocket.
if(liveStram)
{
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();
InformClientSocket(liveSocket,t);
lastBar=thisBar;
}
}
}
//+------------------------------------------------------------------+
//| Request handler |
//+------------------------------------------------------------------+
string RequestHandler(ZmqMsg &request)
{
string reply;
CJAVal message;
if(TerminalInfoInteger(TERMINAL_CONNECTED))
{
ResetLastError();
// Get data from reguest
string msg=request.getData();
if(debug==true) {Print("Processing:"+msg);}
// Deserialize msg to CJAVal array
if(!message.Deserialize(msg))
{
ActionDoneOrError(true, GetLastError(), "Deserialization Error", __FUNCTION__);
Alert("Deserialization Error");
ExpertRemove();
}
// 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(true, GetLastError(), "Wrong action command", __FUNCTION__);}
reply="OK";
}
// If terminal disconnected
else {reply="TERMINAL DISCONNECTED";}
return(reply);
}
//+------------------------------------------------------------------+
//| Reconfigure the script params |
//+------------------------------------------------------------------+
void ScriptConfiguration(CJAVal &dataObject)
{
ResetLastError();
string sym=dataObject["symbol"].ToStr();
ENUM_TIMEFRAMES tf=GetTimeframe(dataObject["chartTF"].ToStr());
if(SymbolInfoInteger(sym, SYMBOL_EXIST)==1)
{
ChartSetSymbolPeriod(0, sym, tf);
ActionDoneOrError(false, GetLastError(), "OK", __FUNCTION__);
}
else
{
ActionDoneOrError(true, GetLastError(), "Symbol name dosn't exist", __FUNCTION__);
}
}
//+------------------------------------------------------------------+
//| Account information |
//+------------------------------------------------------------------+
void GetAccountInfo()
{
CJAVal info;
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();
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();
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Get historical data |
//+------------------------------------------------------------------+
void HistoryInfo(CJAVal &dataObject)
{
CJAVal candles;
MqlRates rates[];
int copied;
string actionType=dataObject["actionType"].ToStr();
string symbol=dataObject["symbol"].ToStr();
ENUM_TIMEFRAMES period=GetTimeframe(dataObject["chartTF"].ToStr());
datetime fromDate=StringToTime(dataObject["fromDate"].ToStr());
if(debug==true)
{
Print("Fetching HISTORY");
Print("1) Symbol:"+symbol);
Print("2) Timeframe:"+EnumToString(period));
Print("3) Date from:"+TimeToString(fromDate));
}
copied=CopyRates(symbol,period,fromDate,TimeCurrent(),rates);
if(copied)
{
for(int i=0;i<copied;i++)
{
candles[i].Add(rates[i].time,TIME_DATE|TIME_MINUTES|TIME_SECONDS);
candles[i].Add(rates[i].open);
candles[i].Add(rates[i].high);
candles[i].Add(rates[i].low);
candles[i].Add(rates[i].close);
candles[i].Add(rates[i].tick_volume);
}
string t=candles.Serialize();
InformClientSocket(dataSocket,t);
}
}
//+------------------------------------------------------------------+
//| Fetch positions information |
//+------------------------------------------------------------------+
void GetPositions(CJAVal &dataObject)
{
if(debug==true) {Print("Fetching positions...");}
CPositionInfo myposition;
CJAVal data, position;
// get positions
int positionsTotal=PositionsTotal();
if(!positionsTotal) {data["positions"].Add(position);}
// go through positions in a loop
for(int i=0;i<positionsTotal;i++)
{
ResetLastError();
if (myposition.Select(PositionGetSymbol(i)))
{
position["id"] = PositionGetInteger(POSITION_IDENTIFIER);
position["magic"] = PositionGetInteger(POSITION_MAGIC);
position["symbol"] = PositionGetString(POSITION_SYMBOL);
position["type"] = EnumToString(ENUM_POSITION_TYPE(PositionGetInteger(POSITION_TYPE)));
position["time_setup"]=PositionGetInteger(POSITION_TIME);
position["open"] = PositionGetDouble(POSITION_PRICE_OPEN);
position["stoploss"] = PositionGetDouble(POSITION_SL);
position["takeprofit"] = PositionGetDouble(POSITION_TP);
position["volume"] = PositionGetDouble(POSITION_VOLUME);
data["positions"].Add(position);
}
else
{
data["error"]= GetLastError();
PrintFormat("Error when obtaining positions from the list to the cache. Error code: %d",GetLastError());
}
}
string t=data.Serialize();
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Fetch orders information |
//+------------------------------------------------------------------+
void GetOrders(CJAVal &dataObject)
{
if(debug==true) {Print("Fetching orders...");}
COrderInfo myorder;
CJAVal data, order;
if (HistorySelect(0,TimeCurrent())) // все ордера
{
int ordersTotal = OrdersTotal();
if(!ordersTotal) {data["orders"].Add(order);}
for(int i=0;i<ordersTotal;i++)
{
ResetLastError();
if (myorder.Select(OrderGetTicket(i)))
{
order["id"]= (string) myorder.Ticket();
order["magic"] = OrderGetInteger(ORDER_MAGIC);
order["symbol"] = OrderGetString(ORDER_SYMBOL);
order["type"] = EnumToString(ENUM_ORDER_TYPE(OrderGetInteger(ORDER_TYPE)));
order["time_setup"]=OrderGetInteger(ORDER_TIME_SETUP);
order["open"] = OrderGetDouble(ORDER_PRICE_OPEN);
order["stoploss"] = OrderGetDouble(ORDER_SL);
order["takeprofit"] = OrderGetDouble(ORDER_TP);
order["volume"] = OrderGetDouble(ORDER_VOLUME_INITIAL);
data["orders"].Add(order);
}
else
{
// call OrderGetTicket() was completed unsuccessfully
data["error"]= GetLastError();
PrintFormat("Error when obtaining an order from the list to the cache. Error code: %d",GetLastError());
}
}
}
string t=data.Serialize();
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Trading module |
//+------------------------------------------------------------------+
void TradingModule(CJAVal &dataObject)
{
CTrade trade;
string actionType = dataObject["actionType"].ToStr();
string symbol=dataObject["symbol"].ToStr();
int idNimber=dataObject["id"].ToInt();
double volume=dataObject["volume"].ToDbl();
double SL=dataObject["stoploss"].ToDbl();
double TP=dataObject["takeprofit"].ToDbl();
double price=NormalizeDouble(dataObject["price"].ToDbl(),_Digits);
datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);
double deviation=dataObject["deviation"].ToDbl();
if(actionType=="BUY" || actionType=="SELL")
{
ENUM_ORDER_TYPE orderType=ORDER_TYPE_BUY;
if(actionType=="ORDER_TYPE_SELL") {orderType=ORDER_TYPE_SELL;}
if(!trade.PositionOpen(symbol,orderType,price,volume,SL,TP))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="BUY_LIMIT" || actionType=="SELL_LIMIT" || actionType=="BUY_STOP" || actionType=="SELL_STOP")
{
if(actionType=="BUY_LIMIT")
{
if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="SELL_LIMIT")
{
if(!trade.SellLimit(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="BUY_STOP")
{
if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if (actionType=="SELL_STOP")
{
if(!trade.SellStop(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
}
else if(actionType=="POSITION_MODIFY")
{
if(!trade.PositionModify(idNimber,SL,TP))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="POSITION_PARTIAL")
{
if(!trade.PositionClosePartial(idNimber,volume))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="POSITION_CLOSE")
{
if(!trade.PositionClose(idNimber))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="ORDER_MODIFY")
{
if(!trade.OrderModify(idNimber,price,SL,TP,ORDER_TIME_GTC,expiration))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else if(actionType=="ORDER_CANCEL")
{
if(!trade.OrderDelete(idNimber))
{OrderDoneOrError(true, __FUNCTION__, trade);}
else
{OrderDoneOrError(false, __FUNCTION__, trade);}
}
else
{
CJAVal conformation;
conformation["error"]=(bool) true;
conformation["retcode"]=(int) 0;
conformation["desription"]=(string)"Wrong actionType command";
string t=conformation.Serialize();
InformClientSocket(dataSocket,t);
}
}
//+------------------------------------------------------------------+
//| Convetr chart timeframe from string to enum |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES GetTimeframe(string chartTF)
{
ENUM_TIMEFRAMES tf=PERIOD_M1;
if(chartTF=="1m") {tf=PERIOD_M1;}
else if(chartTF=="5m") {tf=PERIOD_M5;}
else if(chartTF=="15m") {tf=PERIOD_M15;}
else if(chartTF=="30m") {tf=PERIOD_M30;}
else if(chartTF=="1h") {tf=PERIOD_H1;}
else if(chartTF=="2h") {tf=PERIOD_H2;}
else if(chartTF=="3h") {tf=PERIOD_H3;}
else if(chartTF=="4h") {tf=PERIOD_H4;}
else if(chartTF=="6h") {tf=PERIOD_H6;}
else if(chartTF=="8h") {tf=PERIOD_H8;}
else if(chartTF=="12h") {tf=PERIOD_H12;}
else if(chartTF=="1d") {tf=PERIOD_D1;}
else if(chartTF=="1w") {tf=PERIOD_W1;}
else if(chartTF=="1M") {tf=PERIOD_MN1;}
else {} //error
return(tf);
}
//+------------------------------------------------------------------+
//| Trade conformation |
//+------------------------------------------------------------------+
void OrderDoneOrError(bool error, string funcName, CTrade &trade)
{
CJAVal conf;
conf["error"]=(bool) error;
conf["retcode"]=(int) trade.ResultRetcode();
conf["desription"]=(string) trade.ResultRetcodeDescription();
conf["deal"]=(int) trade.ResultDeal();
conf["order"]=(int) trade.ResultOrder();
conf["volume"]=(double) trade.ResultVolume();
conf["price"]=(double) trade.ResultPrice();
conf["bid"]=(double) trade.ResultBid();
conf["ask"]=(double) trade.ResultAsk();
conf["function"]=(string) funcName;
string t=conf.Serialize();
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Action conformation |
//+------------------------------------------------------------------+
void ActionDoneOrError(bool error, int lastError, string desc, string funcName)
{
CJAVal conf;
conf["error"]=(bool) error;
conf["lastError"]=(string) lastError;
conf["description"]=(string) desc;
conf["function"]=(string) funcName;
string t=conf.Serialize();
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Inform Client via socket |
//+------------------------------------------------------------------+
void InformClientSocket(Socket &workingSocket,string replyMessage)
{
if(debug==true) {Print(replyMessage);}
ZmqMsg pushReply(replyMessage);
workingSocket.send(pushReply,true); // true = non-blocking
}