Files
Gunther Schulz 9a1e4c2ab5 refactor
2021-03-07 10:01:03 +01:00

960 lines
29 KiB
Plaintext

//+------------------------------------------------------------------+
//
// 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: Deviation
#property copyright "Copyright 2019, Nikolai Khramkov."
#property link "https://github.com/khramkov"
#property version "2.00"
#property description "MQL5 JSON API"
#property description "See github link for documentation"
#include <Trade/AccountInfo.mqh>
#include <Trade/DealInfo.mqh>
#include <Trade/Trade.mqh>
#include <Zmq/Zmq.mqh>
#include <Json.mqh>
#include <StringToEnumInt.mqh>
#include <ControlErrors.mqh>
// Set ports and host for ZeroMQ
string HOST="*";
int SYS_PORT=15555;
int DATA_PORT=15556;
int LIVE_PORT=15557;
int STR_PORT=15558;
// ZeroMQ Connections
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);
// Load MQL5-JSON-API includes
// Required:
#include <MQL5-JSON_API/HistoryInfo.mqh>
#include <MQL5-JSON_API/Broker.mqh>
// Optional:
#include <MQL5-JSON_API/StartIndicator.mqh>
#include <MQL5-JSON_API/ChartControl.mqh>
// Global variables \\
bool debug = false;
bool liveStream = true;
bool connectedFlag = true;
int deInitReason = -1;
double chartAttached = ChartID(); // Chart id where the expert is attached to
// Variables for handling price data stream
struct SymbolSubscription
{
string symbol;
string chartTf;
datetime lastBar;
};
SymbolSubscription symbolSubscriptions[];
int symbolSubscriptionCount = 0;
// Error handling
ControlErrors mControl;
//+------------------------------------------------------------------+
//| Bind ZMQ sockets to ports |
//+------------------------------------------------------------------+
bool BindSockets()
{
sysSocket.setLinger(1000);
dataSocket.setLinger(1000);
liveSocket.setLinger(1000);
streamSocket.setLinger(1000);
#ifdef START_INDICATOR
indicatorDataSocket.setLinger(1000);
#endif
#ifdef CHART_CONTROL
chartDataSocket.setLinger(1000);
chartIndicatorDataSocket.setLinger(1000);
#endif
// Number of messages to buffer in RAM.
sysSocket.setSendHighWaterMark(1000);
dataSocket.setSendHighWaterMark(1000);
liveSocket.setSendHighWaterMark(1000);
streamSocket.setSendHighWaterMark(1000);
#ifdef START_INDICATOR
indicatorDataSocket.setSendHighWaterMark(1000);
#endif
#ifdef CHART_CONTROL
chartDataSocket.setReceiveHighWaterMark(1000); // TODO confirm settings
chartIndicatorDataSocket.setReceiveHighWaterMark(1000);
#endif
bool result = false;
result = sysSocket.bind(StringFormat("tcp://%s:%d", HOST,SYS_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'System' socket on port ", SYS_PORT);
}
result = dataSocket.bind(StringFormat("tcp://%s:%d", HOST,DATA_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'Data' socket on port ", DATA_PORT);
}
result = liveSocket.bind(StringFormat("tcp://%s:%d", HOST,LIVE_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'Live' socket on port ", LIVE_PORT);
}
result = streamSocket.bind(StringFormat("tcp://%s:%d", HOST,STR_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'Streaming' socket on port ", STR_PORT);
}
#ifdef START_INDICATOR
result = indicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,INDICATOR_DATA_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'Indicator Data' socket on port ", INDICATOR_DATA_PORT);
}
#endif
#ifdef CHART_CONTROL
result = chartDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_DATA_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'Chart Data' socket on port ", CHART_DATA_PORT);
}
result = chartIndicatorDataSocket.bind(StringFormat("tcp://%s:%d", HOST,CHART_INDICATOR_DATA_PORT));
if(result == false)
{
return result;
}
else
{
Print("Bound 'JsonAPIIndicator Data' socket on port ", CHART_INDICATOR_DATA_PORT);
}
#endif
return result;
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Setting up error reporting
mControl.SetAlert(true);
mControl.SetSound(false);
mControl.SetWriteFlag(false);
/* Bindinig ZMQ ports on init */
// Skip reloading of the EA script when the reason to reload is a chart timeframe change
if(deInitReason != REASON_CHARTCHANGE)
{
EventSetMillisecondTimer(1);
int bindSocketsDelay = 65; // Seconds to wait if binding of sockets fails.
int bindAttemtps = 2; // Number of binding attemtps
Print("Binding sockets...");
for(int i=0; i<bindAttemtps; i++)
{
if(BindSockets())
return(INIT_SUCCEEDED);
else
{
Print("Binding sockets failed. Waiting ", bindSocketsDelay, " seconds to try again...");
Sleep(bindSocketsDelay*1000);
}
}
Print("Binding of sockets failed permanently.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
/* Unbinding ZMQ ports on denit */
// TODO Ports do not get freed immediately under Wine. How to properly close ports? There is a timeout of about 60 sec.
// https://forum.winehq.org/viewtopic.php?t=22758
// https://github.com/zeromq/cppzmq/issues/139
deInitReason = reason;
// Skip reloading of the EA script when the reason to reload is a chart timeframe change
if(reason != REASON_CHARTCHANGE)
{
Print(__FUNCTION__," Deinitialization reason: ", getUninitReasonText(reason));
Print("Unbinding 'System' socket on port ", SYS_PORT, "..");
sysSocket.unbind(StringFormat("tcp://%s:%d", HOST, SYS_PORT));
Print("Unbinding 'Data' socket on port ", DATA_PORT, "..");
dataSocket.unbind(StringFormat("tcp://%s:%d", HOST, DATA_PORT));
Print("Unbinding 'Live' socket on port ", LIVE_PORT, "..");
liveSocket.unbind(StringFormat("tcp://%s:%d", HOST, LIVE_PORT));
Print("Unbinding 'Streaming' socket on port ", STR_PORT, "..");
streamSocket.unbind(StringFormat("tcp://%s:%d", HOST, STR_PORT));
#ifdef START_INDICATOR
Print("Unbinding 'Indicator Data' socket on port ", INDICATOR_DATA_PORT, "..");
streamSocket.unbind(StringFormat("tcp://%s:%d", HOST, INDICATOR_DATA_PORT));
#endif
#ifdef CHART_CONTROL
Print("Unbinding 'Chart Data' socket on port ", CHART_DATA_PORT, "..");
streamSocket.unbind(StringFormat("tcp://%s:%d", HOST, CHART_DATA_PORT));
Print("Unbinding 'JsonAPIIndicator Data' socket on port ", CHART_INDICATOR_DATA_PORT, "..");
streamSocket.unbind(StringFormat("tcp://%s:%d", HOST, CHART_INDICATOR_DATA_PORT));
#endif
// Shutdown ZeroMQ Context
context.shutdown();
context.destroy(0);
// Reset
ResetSubscriptionsAndIndicators();
EventKillTimer();
}
}
//+------------------------------------------------------------------+
//| Check if subscribed to symbol and timeframe combination |
//+------------------------------------------------------------------+
bool HasChartSymbol(string symbol, string chartTF)
{
for(int i=0; i<ArraySize(symbolSubscriptions); i++)
{
if(symbolSubscriptions[i].symbol == symbol && symbolSubscriptions[i].chartTf == chartTF)
{
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Stream live price data |
//+------------------------------------------------------------------+
void StreamPriceData()
{
// If liveStream == true, push last candle to liveSocket.
if(liveStream)
{
CJAVal last;
if(TerminalInfoInteger(TERMINAL_CONNECTED))
{
connectedFlag=true;
for(int i=0; i<symbolSubscriptionCount; i++)
{
string symbol=symbolSubscriptions[i].symbol;
string chartTF=symbolSubscriptions[i].chartTf;
datetime lastBar=symbolSubscriptions[i].lastBar;
CJAVal Data;
ENUM_TIMEFRAMES period = GetTimeframe(chartTF);
datetime thisBar = 0;
float price;
MqlTick tick;
MqlRates rates[1];
int spread[1];
if(chartTF == "TICK")
{
if(SymbolInfoTick(symbol,tick) !=true) { /*mControl.Check();*/ }
thisBar=(datetime) tick.time_msc;
}
else
{
if(CopyRates(symbol,period,1,1,rates)!=1) { /*mControl.Check();*/ }
if(CopySpread(symbol,period,1,1,spread)!=1) { /*mControl.Check();*/; }
thisBar=(datetime)rates[0].time;
}
if(lastBar!=thisBar)
{
if(lastBar!=0) // skip first price data after startup/reset
{
if(chartTF == "TICK")
{
Data[0] = (long) tick.time_msc;
Data[1] = (double) tick.bid;
Data[2] = (double) tick.ask;
}
else
{
Data[0] = (long) rates[0].time;
Data[1] = (double) rates[0].open;
Data[2] = (double) rates[0].high;
Data[3] = (double) rates[0].low;
Data[4] = (double) rates[0].close;
Data[5] = (double) rates[0].tick_volume;
Data[6] = (int) spread[0];
}
last["status"] = (string) "CONNECTED";
last["symbol"] = (string) symbol;
last["timeframe"] = (string) chartTF;
last["data"].Set(Data);
string t=last.Serialize();
if(debug)
Print(t);
InformClientSocket(liveSocket,t);
symbolSubscriptions[i].lastBar=thisBar;
}
else
symbolSubscriptions[i].lastBar=thisBar;
}
}
}
else
{
// send disconnect message only once
if(connectedFlag)
{
last["status"] = (string) "DISCONNECTED";
string t=last.Serialize();
if(debug)
Print(t);
InformClientSocket(liveSocket,t);
connectedFlag=false;
}
}
}
}
//+------------------------------------------------------------------+
//| Expert timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
// Stream live price data
StreamPriceData();
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);
}
#ifdef CHART_CONTROL
// Publish indicator values for the JsonAPIIndicator indicator
ZmqMsg chartMsg;
chartDataSocket.recv(chartMsg, true);
if(chartMsg.size()>0)
{
double values[];
// Ensure that all indicators have finished intitailisation
for(int i=0; i<ArraySize(chartWindowIndicators); i++)
{
// Wait for CopyBuffer to return. Ensures that indicator has been initialized
CopyBuffer(chartWindowIndicators[i].indicatorHandle, 0, 0, 1, values);
}
chartIndicatorDataSocket.send(chartMsg,true);
}
// Trigger the indicator JsonAPIIndicator to check for new Messages
for(int i=0; i<ArraySize(chartWindows); i++)
{
long chartId = chartWindows[i].id;
EventChartCustom(chartId, 222, 222, 222.0);
}
#endif
}
//+------------------------------------------------------------------+
//| Request handler |
//+------------------------------------------------------------------+
void RequestHandler(ZmqMsg &request)
{
CJAVal incomingMessage;
ResetLastError();
// Get data from reguest
string msg=request.getData();
if(debug)
Print("Processing:"+msg);
if(!incomingMessage.Deserialize(msg))
{
mControl.mSetUserError(65537, GetErrorID(65537));
CheckError(__FUNCTION__);
}
// Send response to System socket that request was received
// Some historical data requests can take a lot of time
InformClientSocket(sysSocket, "OK");
// Process action command
string action = incomingMessage["action"].ToStr();
if(action=="CONFIG")
ScriptConfiguration(incomingMessage);
else
if(action=="ACCOUNT")
GetAccountInfo();
else
if(action=="BALANCE")
GetBalanceInfo();
else
if(action=="HISTORY")
HistoryInfo(incomingMessage);
else
if(action=="TRADE")
TradingModule(incomingMessage);
else
if(action=="POSITIONS")
GetPositions(incomingMessage);
else
if(action=="ORDERS")
GetOrders(incomingMessage);
else
if(action=="RESET")
ResetSubscriptionsAndIndicators();
else
#ifdef START_INDICATOR
if(action=="INDICATOR")
IndicatorControl(incomingMessage);
else
#endif
#ifdef CHART_CONTROL
if(action=="CHART")
ChartControl(incomingMessage);
else
#endif
{
mControl.mSetUserError(65538, GetErrorID(65538));
CheckError(__FUNCTION__);
}
}
//+------------------------------------------------------------------+
//| Reconfigure the script params |
//+------------------------------------------------------------------+
void ScriptConfiguration(CJAVal &dataObject)
{
string symbol=dataObject["symbol"].ToStr();
string chartTF=dataObject["chartTF"].ToStr();
ArrayResize(symbolSubscriptions, symbolSubscriptionCount+1);
symbolSubscriptions[symbolSubscriptionCount].symbol = symbol;
symbolSubscriptions[symbolSubscriptionCount].chartTf = chartTF;
// to initialze with value 0 skips the first price
symbolSubscriptions[symbolSubscriptionCount].lastBar = 0;
symbolSubscriptionCount++;
mControl.mResetLastError();
SymbolInfoString(symbol, SYMBOL_DESCRIPTION);
if(!CheckError(__FUNCTION__))
ActionDoneOrError(ERR_SUCCESS, __FUNCTION__, "ERR_SUCCESS");
}
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//| Push historical data to ZMQ socket |
//+------------------------------------------------------------------+
bool PushHistoricalData(CJAVal &data)
{
string t=data.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
return true;
}
//+------------------------------------------------------------------+
//| Convert chart timeframe from string to enum |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES GetTimeframe(string chartTF)
{
ENUM_TIMEFRAMES tf;
tf=NULL;
if(chartTF=="TICK")
tf=PERIOD_CURRENT;
if(chartTF=="M1")
tf=PERIOD_M1;
if(chartTF=="M5")
tf=PERIOD_M5;
if(chartTF=="M15")
tf=PERIOD_M15;
if(chartTF=="M30")
tf=PERIOD_M30;
if(chartTF=="H1")
tf=PERIOD_H1;
if(chartTF=="H2")
tf=PERIOD_H2;
if(chartTF=="H3")
tf=PERIOD_H3;
if(chartTF=="H4")
tf=PERIOD_H4;
if(chartTF=="H6")
tf=PERIOD_H6;
if(chartTF=="H8")
tf=PERIOD_H8;
if(chartTF=="H12")
tf=PERIOD_H12;
if(chartTF=="D1")
tf=PERIOD_D1;
if(chartTF=="W1")
tf=PERIOD_W1;
if(chartTF=="MN1")
tf=PERIOD_MN1;
//if tf == NULL an error will be raised in config function
return(tf);
}
//+------------------------------------------------------------------+
//| Trade confirmation |
//+------------------------------------------------------------------+
void OrderDoneOrError(bool error, string funcName, CTrade &trade)
{
CJAVal conf;
conf["error"]=(bool) error;
conf["retcode"]=(int) trade.ResultRetcode();
conf["desription"]=(string) GetRetcodeID(trade.ResultRetcode());
// 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();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Error reporting |
//+------------------------------------------------------------------+
bool CheckError(string funcName)
{
int lastError = mControl.mGetLastError();
if(lastError)
{
string desc = mControl.mGetDesc();
if(debug)
Print("Error handling source: ", funcName," description: ", desc);
Print("Error handling source: ", funcName," description: ", desc);
mControl.Check();
ActionDoneOrError(lastError, funcName, desc);
return true;
}
else
return false;
}
//+------------------------------------------------------------------+
//| Action confirmation |
//+------------------------------------------------------------------+
void ActionDoneOrError(int lastError, string funcName, string desc)
{
CJAVal conf;
conf["error"]=(bool)true;
if(lastError==0)
conf["error"]=(bool)false;
conf["lastError"]=(string) lastError;
conf["description"]=(string) desc;
conf["function"]=(string) funcName;
string t=conf.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Inform Client via socket |
//+------------------------------------------------------------------+
void InformClientSocket(Socket &workingSocket,string replyMessage)
{
// non-blocking
workingSocket.send(replyMessage,true);
// TODO: Array out of range error
mControl.mResetLastError();
//mControl.Check();
}
//+------------------------------------------------------------------+
//| Clear symbol subscriptions and indicators |
//+------------------------------------------------------------------+
void ResetSubscriptionsAndIndicators()
{
ArrayFree(symbolSubscriptions);
symbolSubscriptionCount=0;
bool error = false;
#ifdef START_INDICATOR
for(int i=0; i<indicatorCount; i++)
{
if(!IndicatorRelease(indicators[i].indicatorHandle))
error = true;
}
ArrayFree(indicators);
indicatorCount = 0;
#endif
#ifdef CHART_CONTROL
for(int i=0; i<chartWindowIndicatorCount; i++)
{
if(!IndicatorRelease(chartWindowIndicators[i].indicatorHandle))
error = true;
}
ArrayFree(chartWindowIndicators);
chartWindowIndicatorCount = 0;
for(int i=0; i<ArraySize(chartWindows); i++)
{
// TODO check if chart exists first: if(ChartGetInteger...
//if(!IndicatorRelease(chartWindows[i].indicatorHandle)) error = true;
if(chartWindows[i].id != 0)
ChartClose(chartWindows[i].id);
}
ArrayFree(chartWindows);
#endif
/*
if(ArraySize(symbolSubscriptions)!=0 || ArraySize(indicators)!=0 || ArraySize(chartWindows)!=0 || error){
// Set to only Alert. Fails too often, this happens when i.e. the backtrader script gets aborted unexpectedly
mControl.Check();
mControl.mSetUserError(65540, GetErrorID(65540));
CheckError(__FUNCTION__);
}
*/
ActionDoneOrError(ERR_SUCCESS, __FUNCTION__, "ERR_SUCCESS");
}
//+------------------------------------------------------------------+
//| Get retcode message by retcode id |
//+------------------------------------------------------------------+
string GetRetcodeID(int retcode)
{
switch(retcode)
{
case 10004:
return("TRADE_RETCODE_REQUOTE");
break;
case 10006:
return("TRADE_RETCODE_REJECT");
break;
case 10007:
return("TRADE_RETCODE_CANCEL");
break;
case 10008:
return("TRADE_RETCODE_PLACED");
break;
case 10009:
return("TRADE_RETCODE_DONE");
break;
case 10010:
return("TRADE_RETCODE_DONE_PARTIAL");
break;
case 10011:
return("TRADE_RETCODE_ERROR");
break;
case 10012:
return("TRADE_RETCODE_TIMEOUT");
break;
case 10013:
return("TRADE_RETCODE_INVALID");
break;
case 10014:
return("TRADE_RETCODE_INVALID_VOLUME");
break;
case 10015:
return("TRADE_RETCODE_INVALID_PRICE");
break;
case 10016:
return("TRADE_RETCODE_INVALID_STOPS");
break;
case 10017:
return("TRADE_RETCODE_TRADE_DISABLED");
break;
case 10018:
return("TRADE_RETCODE_MARKET_CLOSED");
break;
case 10019:
return("TRADE_RETCODE_NO_MONEY");
break;
case 10020:
return("TRADE_RETCODE_PRICE_CHANGED");
break;
case 10021:
return("TRADE_RETCODE_PRICE_OFF");
break;
case 10022:
return("TRADE_RETCODE_INVALID_EXPIRATION");
break;
case 10023:
return("TRADE_RETCODE_ORDER_CHANGED");
break;
case 10024:
return("TRADE_RETCODE_TOO_MANY_REQUESTS");
break;
case 10025:
return("TRADE_RETCODE_NO_CHANGES");
break;
case 10026:
return("TRADE_RETCODE_SERVER_DISABLES_AT");
break;
case 10027:
return("TRADE_RETCODE_CLIENT_DISABLES_AT");
break;
case 10028:
return("TRADE_RETCODE_LOCKED");
break;
case 10029:
return("TRADE_RETCODE_FROZEN");
break;
case 10030:
return("TRADE_RETCODE_INVALID_FILL");
break;
case 10031:
return("TRADE_RETCODE_CONNECTION");
break;
case 10032:
return("TRADE_RETCODE_ONLY_REAL");
break;
case 10033:
return("TRADE_RETCODE_LIMIT_ORDERS");
break;
case 10034:
return("TRADE_RETCODE_LIMIT_VOLUME");
break;
case 10035:
return("TRADE_RETCODE_INVALID_ORDER");
break;
case 10036:
return("TRADE_RETCODE_POSITION_CLOSED");
break;
case 10038:
return("TRADE_RETCODE_INVALID_CLOSE_VOLUME");
break;
case 10039:
return("TRADE_RETCODE_CLOSE_ORDER_EXIST");
break;
case 10040:
return("TRADE_RETCODE_LIMIT_POSITIONS");
break;
case 10041:
return("TRADE_RETCODE_REJECT_CANCEL");
break;
case 10042:
return("TRADE_RETCODE_LONG_ONLY");
break;
case 10043:
return("TRADE_RETCODE_SHORT_ONLY");
break;
case 10044:
return("TRADE_RETCODE_CLOSE_ONLY");
break;
default:
return("TRADE_RETCODE_UNKNOWN="+IntegerToString(retcode));
break;
}
}
//+------------------------------------------------------------------+
//| Get error message by error id |
//+------------------------------------------------------------------+
string GetErrorID(int error)
{
switch(error)
{
// Custom errors
case 65537:
return("ERR_DESERIALIZATION");
break;
case 65538:
return("ERR_WRONG_ACTION");
break;
case 65539:
return("ERR_WRONG_ACTION_TYPE");
break;
case 65540:
return("ERR_CLEAR_SUBSCRIPTIONS_FAILED");
break;
case 65541:
return("ERR_RETRIEVE_DATA_FAILED");
break;
case 65542:
return("ERR_CVS_FILE_CREATION_FAILED");
break;
default:
return("ERR_CODE_UNKNOWN="+IntegerToString(error));
break;
}
}
//+------------------------------------------------------------------+
//| Return a textual description of the deinitialization reason code |
//+------------------------------------------------------------------+
string getUninitReasonText(int reasonCode)
{
string text="";
//---
switch(reasonCode)
{
case REASON_ACCOUNT:
text="Account was changed";
break;
case REASON_CHARTCHANGE:
text="Symbol or timeframe was changed";
break;
case REASON_CHARTCLOSE:
text="Chart was closed";
break;
case REASON_PARAMETERS:
text="Input-parameter was changed";
break;
case REASON_RECOMPILE:
text="Program "+__FILE__+" was recompiled";
break;
case REASON_REMOVE:
text="Program "+__FILE__+" was removed from chart";
break;
case REASON_TEMPLATE:
text="New template was applied to chart";
break;
default:
text="Another reason";
}
//---
return text;
}
//+------------------------------------------------------------------+