32 Commits

Author SHA1 Message Date
Gunther Schulz 824975f2c9 Update changelog 2021-03-07 10:13:47 +01:00
Gunther Schulz 9a1e4c2ab5 refactor 2021-03-07 10:01:03 +01:00
Gunther Schulz c1633e7c80 refactor 2021-03-06 18:21:57 +01:00
Nikolai b17e82d99d Merge pull request #9 from Gunther-Schulz/indicator
Drawing custom indicator data to charts
2020-10-12 10:25:50 +03:00
Gunther Schulz d423745c93 some fixes 2020-10-10 17:20:08 +02:00
Gunther Schulz 1b2beab4c4 update README and changelog
minor update to JsonAPIIndicator
2020-06-13 17:34:11 +02:00
Gunther Schulz ae6a2367b1 fix formating 2020-04-30 19:03:23 +02:00
Gunther Schulz b550a423a1 changelog 2020-04-30 18:58:17 +02:00
Gunther Schulz de6b4f0546 add support for plot labels for chart indicator lines
cleanup
2020-04-30 18:55:49 +02:00
Gunther Schulz 2c77099430 update changelog 2020-04-30 16:26:36 +02:00
Gunther Schulz 99ea77ebfe cleanup 2020-04-30 10:49:01 +02:00
Gunther Schulz 4b2297fbfb allow ENUM as string as parameters for indicators
minor code optimization
2020-03-04 12:32:48 +01:00
Gunther Schulz 37cb0fe738 cleanup 2020-03-03 12:52:46 +01:00
Gunther Schulz cc0f9b7575 change error handling to utilize MQL5 standard error handling 2020-03-02 22:11:32 +01:00
Gunther Schulz 7099ac1cac add chart control
open and draw indicator lines on chart
2020-03-01 18:06:24 +01:00
Gunther Schulz 820329307f add indicator control 2020-02-23 14:03:25 +01:00
Gunther Schulz 1bad67048f add spread support for price candle data 2020-02-16 22:17:37 +01:00
Gunther Schulz f27bc3d1d9 add indicator
control any mt5 indicator
2020-02-16 12:51:36 +01:00
Gunther Schulz c1e136d64e minor README.md formating cleanup 2020-02-16 12:51:14 +01:00
Gunther Schulz dc60a0a0c9 update .gitattributes for Metatrader 5 file types 2020-02-16 12:22:02 +01:00
Gunther Schulz 34af70c043 add script to remove binary characters and add BOM 2020-02-16 12:00:27 +01:00
Nikolai 036c2d4cfd Update JsonAPI.mq5 2020-02-16 11:03:50 +03:00
Nikolai 7deedb72c5 Update JsonAPI.mq5 2020-02-16 11:01:55 +03:00
Nikolai 126c669c62 Bug fix 2020-02-16 11:01:49 +03:00
Nikolai 03088ea2ec Update README.md 2020-02-13 18:16:35 +03:00
Nikolai e14b83e023 v 2.0 2020-02-13 18:15:23 +03:00
Nikolai 598253cfc3 Update JsonAPI.mq5 2020-02-12 20:41:03 +03:00
Nikolai aee89843da Update JsonAPI.mq5 2020-02-12 20:34:41 +03:00
Nikolai 9457e57890 Update JsonAPI.mq5
Reconnect loop
2020-02-12 20:19:22 +03:00
Nikolai 01ac1bbc11 Merge pull request #6 from freedumb2000/ticks-support
Support for tick data
2020-01-28 23:30:33 +03:00
Gunther Schulz 081fdfbdfe add support for higher tick timestamp resolution 2020-01-15 10:21:18 +01:00
Gunther Schulz 4d06d0e060 remove all Lizar contrib files and dependencies
add changelog
2020-01-12 20:22:06 +01:00
17 changed files with 3139 additions and 1025 deletions
+3
View File
@@ -1,2 +1,5 @@
# Auto detect text files and perform LF normalization # Auto detect text files and perform LF normalization
* text=auto * text=auto
text *.mq5 eol=CRLF diff=c
text *.mqh eol=CRLF diff=c
+6 -1
View File
@@ -1,5 +1,10 @@
.DS_Store .DS_Store
*.mq5:CursorPos:$DATA
update_from_mt5\.sh *.mq5:LineFlags:$DATA
*.mqh:CursorPos:$DATA
*.mqh:LineFlags:$DATA
Binary file not shown.
+316
View File
@@ -0,0 +1,316 @@
//+------------------------------------------------------------------+
//| Broker.mqh |
//| Gunther Schulz |
//| https://github.com/khramkov/MQL5-JSON-API |
//+------------------------------------------------------------------+
#property copyright "Gunther Schulz"
#property link "https://github.com/khramkov/MQL5-JSON-API"
//+------------------------------------------------------------------+
//| Fetch positions information |
//+------------------------------------------------------------------+
void GetPositions(CJAVal &dataObject)
{
CPositionInfo myposition;
CJAVal data, position;
// Get positions
int positionsTotal=PositionsTotal();
// Create empty array if no positions
if(!positionsTotal)
data["positions"].Add(position);
// Go through positions in a loop
for(int i=0; i<positionsTotal; i++)
{
mControl.mResetLastError();
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["error"]=(bool) false;
data["positions"].Add(position);
}
CheckError(__FUNCTION__);
}
string t=data.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Fetch orders information |
//+------------------------------------------------------------------+
void GetOrders(CJAVal &dataObject)
{
mControl.mResetLastError();
COrderInfo myorder;
CJAVal data, order;
// Get orders
if(HistorySelect(0,TimeCurrent()))
{
int ordersTotal = OrdersTotal();
// Create empty array if no orders
if(!ordersTotal)
{
data["error"]=(bool) false;
data["orders"].Add(order);
}
for(int i=0; i<ordersTotal; i++)
{
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["error"]=(bool) false;
data["orders"].Add(order);
}
// Error handling
CheckError(__FUNCTION__);
}
}
string t=data.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Trading module |
//+------------------------------------------------------------------+
void TradingModule(CJAVal &dataObject)
{
mControl.mResetLastError();
CTrade trade;
string actionType = dataObject["actionType"].ToStr();
string symbol=dataObject["symbol"].ToStr();
SymbolInfoString(symbol, SYMBOL_DESCRIPTION);
CheckError(__FUNCTION__);
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);
double deviation=dataObject["deviation"].ToDbl();
string comment=dataObject["comment"].ToStr();
// Order expiration section
ENUM_ORDER_TYPE_TIME exp_type = ORDER_TIME_GTC;
datetime expiration = 0;
if(dataObject["expiration"].ToInt() != 0)
{
exp_type = ORDER_TIME_SPECIFIED;
expiration=dataObject["expiration"].ToInt();
}
// Market orders
if(actionType=="ORDER_TYPE_BUY" || actionType=="ORDER_TYPE_SELL")
{
ENUM_ORDER_TYPE orderType=ORDER_TYPE_BUY;
price = SymbolInfoDouble(symbol,SYMBOL_ASK);
if(actionType=="ORDER_TYPE_SELL")
{
orderType=ORDER_TYPE_SELL;
price=SymbolInfoDouble(symbol,SYMBOL_BID);
}
if(trade.PositionOpen(symbol,orderType,volume,price,SL,TP,comment))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Pending orders
else
if(actionType=="ORDER_TYPE_BUY_LIMIT" || actionType=="ORDER_TYPE_SELL_LIMIT" || actionType=="ORDER_TYPE_BUY_STOP" || actionType=="ORDER_TYPE_SELL_STOP")
{
if(actionType=="ORDER_TYPE_BUY_LIMIT")
{
if(trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
else
if(actionType=="ORDER_TYPE_SELL_LIMIT")
{
if(trade.SellLimit(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
else
if(actionType=="ORDER_TYPE_BUY_STOP")
{
if(trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
else
if(actionType=="ORDER_TYPE_SELL_STOP")
{
if(trade.SellStop(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
}
// Position modify
else
if(actionType=="POSITION_MODIFY")
{
if(trade.PositionModify(idNimber,SL,TP))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Position close partial
else
if(actionType=="POSITION_PARTIAL")
{
if(trade.PositionClosePartial(idNimber,volume))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Position close by id
else
if(actionType=="POSITION_CLOSE_ID")
{
if(trade.PositionClose(idNimber))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Position close by symbol
else
if(actionType=="POSITION_CLOSE_SYMBOL")
{
if(trade.PositionClose(symbol))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Modify pending order
else
if(actionType=="ORDER_MODIFY")
{
if(trade.OrderModify(idNimber,price,SL,TP,ORDER_TIME_GTC,expiration))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Cancel pending order
else
if(actionType=="ORDER_CANCEL")
{
if(trade.OrderDelete(idNimber))
{
OrderDoneOrError(false, __FUNCTION__, trade);
return;
}
}
// Action type dosen't exist
else
{
mControl.mSetUserError(65538, GetErrorID(65538));
CheckError(__FUNCTION__);
}
// This part of the code runs if order was not completed
OrderDoneOrError(true, __FUNCTION__, trade);
}
//+------------------------------------------------------------------+
//| TradeTransaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
ENUM_TRADE_TRANSACTION_TYPE trans_type=trans.type;
switch(trans.type)
{
case TRADE_TRANSACTION_REQUEST:
{
CJAVal data, req, res;
req["action"]=EnumToString(request.action);
req["order"]=(int) request.order;
req["symbol"]=(string) request.symbol;
req["volume"]=(double) request.volume;
req["price"]=(double) request.price;
req["stoplimit"]=(double) request.stoplimit;
req["sl"]=(double) request.sl;
req["tp"]=(double) request.tp;
req["deviation"]=(int) request.deviation;
req["type"]=EnumToString(request.type);
req["type_filling"]=EnumToString(request.type_filling);
req["type_time"]=EnumToString(request.type_time);
req["expiration"]=(int) request.expiration;
req["comment"]=(string) request.comment;
req["position"]=(int) request.position;
req["position_by"]=(int) request.position_by;
res["retcode"]=(int) result.retcode;
res["result"]=(string) GetRetcodeID(result.retcode);
res["deal"]=(int) result.order;
res["order"]=(int) result.order;
res["volume"]=(double) result.volume;
res["price"]=(double) result.price;
res["comment"]=(string) result.comment;
res["request_id"]=(int) result.request_id;
res["retcode_external"]=(int) result.retcode_external;
data["request"].Set(req);
data["result"].Set(res);
string t=data.Serialize();
if(debug)
Print(t);
InformClientSocket(streamSocket,t);
}
break;
default:
{} break;
}
}
//+------------------------------------------------------------------+
+145
View File
@@ -0,0 +1,145 @@
//+------------------------------------------------------------------+
//| ChartControl.mqh |
//| Gunther Schulz |
//| https://github.com/khramkov/MQL5-JSON-API |
//+------------------------------------------------------------------+
// EXPERIMENTAL - DO NOT USE ;)
#property copyright "Gunther Schulz"
#property link "https://github.com/khramkov/MQL5-JSON-API"
#define CHART_CONTROL true
int CHART_DATA_PORT=15560;
int CHART_INDICATOR_DATA_PORT=15562;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
Socket chartDataSocket(context,ZMQ_PULL);
Socket chartIndicatorDataSocket(context,ZMQ_PUB);
// Variables for controlling chart
struct ChartWindow
{
long id; // Internal id
string chartId; // UUID
};
ChartWindow chartWindows[];
int chartWindowCount = 0;
struct ChartWindowIndicator
{
long id; // Internal id
string indicatorId; // UUID
int indicatorHandle; // Internal id/handle
};
ChartWindowIndicator chartWindowIndicators[];
int chartWindowIndicatorCount = 0;
//+----------------------------------------------------------------------+
//| Function return index of chart window array by chart window id string|
//+----------------------------------------------------------------------+
int GetChartWindowIdxByChartWindowId(string chartWindowId)
{
for(int i=0; i<chartWindowCount; i++)
{
if(chartWindows[i].chartId == chartWindowId)
{
return i;
}
}
return -1;
}
//+------------------------------------------------------------------+
//| Open new chart or add indicator to chart |
//+------------------------------------------------------------------+
void ChartControl(CJAVal &dataObject)
{
string actionType=dataObject["actionType"].ToStr();
if(actionType=="ADDINDICATOR")
{
AddChartIndicator(dataObject);
}
else
if(actionType=="OPEN")
{
OpenChart(dataObject);
}
}
//+------------------------------------------------------------------+
//| Open new chart |
//+------------------------------------------------------------------+
void OpenChart(CJAVal &dataObject)
{
string chartId=dataObject["chartId"].ToStr();
string symbol=dataObject["symbol"].ToStr();
string chartTF=dataObject["chartTF"].ToStr();
chartWindowCount++;
ArrayResize(chartWindows,chartWindowCount);
int idx = chartWindowCount-1;
chartWindows[idx].chartId = chartId;
ENUM_TIMEFRAMES period = GetTimeframe(chartTF);
chartWindows[idx].id = ChartOpen(symbol, period);
ChartSetInteger(chartWindows[idx].id, CHART_AUTOSCROLL, false);
CJAVal message;
message["error"]=(bool) false;
message["chartId"] = (string) chartId;
message["mtChartId"] = (string) chartWindows[idx].id;
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
//+------------------------------------------------------------------+
//| Add JsonAPIIndicator indicator to chart |
//+------------------------------------------------------------------+
void AddChartIndicator(CJAVal &dataObject)
{
string chartIdStr=dataObject["chartId"].ToStr();
string chartIndicatorId=dataObject["chartIndicatorId"].ToStr();
int chartIndicatorSubWindow=dataObject["chartIndicatorSubWindow"].ToInt();
string shortName = dataObject["shortName"].ToStr();
int chartIdx = GetChartWindowIdxByChartWindowId(chartIdStr);
long chartId = chartWindows[chartIdx].id;
double chartIndicatorHandle = iCustom(ChartSymbol(chartId),ChartPeriod(chartId),"JsonAPIIndicator",chartIndicatorId,shortName); //linelabel,colorstyle,linetype,linestyle,linewidth);
if(ChartIndicatorAdd(chartId, chartIndicatorSubWindow, chartIndicatorHandle))
{
chartWindowIndicatorCount++;
ArrayResize(chartWindowIndicators,chartWindowIndicatorCount);
int indicatorIdx = chartWindowIndicatorCount-1;
chartWindowIndicators[indicatorIdx].indicatorId = chartIndicatorId;
chartWindowIndicators[indicatorIdx].indicatorHandle = chartIndicatorHandle;
}
if(!CheckError(__FUNCTION__))
{
CJAVal message;
message["error"]=(bool) false;
message["chartId"] = (string) chartIdStr;
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
}
//+------------------------------------------------------------------+
+307
View File
@@ -0,0 +1,307 @@
//+------------------------------------------------------------------+
//| HistoryInfo.mqh |
//| Gunther Schulz |
//| https://github.com/khramkov/MQL5-JSON-API |
//+------------------------------------------------------------------+
#property copyright "Gunther Schulz"
#property link "https://github.com/khramkov/MQL5-JSON-API"
//+------------------------------------------------------------------+
//| Get historical data |
//+------------------------------------------------------------------+
void HistoryInfo(CJAVal &dataObject)
{
string actionType = dataObject["actionType"].ToStr();
string chartTF = dataObject["chartTF"].ToStr();
string symbol=dataObject["symbol"].ToStr();
// bool correctTickHistory=dataObject["correctTickHistory"].ToBool();
// Write CVS fle to local directory
if(actionType=="WRITE" && chartTF=="TICK")
{
CJAVal data, d, msg;
MqlTick tickArray[];
string fileName=symbol + "-" + chartTF + ".csv"; // file name
string directoryName="Data"; // directory name
string outputFile=directoryName+"\\"+fileName;
ENUM_TIMEFRAMES period=GetTimeframe(chartTF);
datetime fromDate=(datetime)dataObject["fromDate"].ToInt();
datetime toDate=TimeCurrent();
if(dataObject["toDate"].ToInt()!=NULL)
toDate=(datetime)dataObject["toDate"].ToInt();
Print("Fetching HISTORY");
Print("1) Symbol: "+symbol);
Print("2) Timeframe: Ticks");
Print("3) Date from: "+TimeToString(fromDate));
if(dataObject["toDate"].ToInt()!=NULL)
Print("4) Date to:"+TimeToString(toDate));
int tickCount = 0;
ulong fromDateM = StringToTime(fromDate);
ulong toDateM = StringToTime(toDate);
tickCount=CopyTicksRange(symbol,tickArray,COPY_TICKS_ALL,1000*(ulong)fromDateM,1000*(ulong)toDateM);
if(tickCount < 0)
{
mControl.mSetUserError(65541, GetErrorID(65541));
}
CheckError(__FUNCTION__);
Print("Preparing data of ", tickCount, " ticks for ", symbol);
/*
if(correctTickHistory)
CorrectTicks(symbol,tickArray);
*/
int file_handle=FileOpen(outputFile, FILE_WRITE | FILE_CSV);
if(file_handle!=INVALID_HANDLE)
{
msg["status"] = (string) "CONNECTED";
msg["type"] = (string) "NORMAL";
msg["data"] = (string) StringFormat("Writing to: %s\\%s", TerminalInfoString(TERMINAL_DATA_PATH), outputFile);
if(liveStream)
InformClientSocket(liveSocket, msg.Serialize());
ActionDoneOrError(ERR_SUCCESS, __FUNCTION__, "ERR_SUCCESS");
//ActionDoneOrError(ERR_SUCCESS , __FUNCTION__, dataSocket);
// Inform client that file is avalable for writing
PrintFormat("%s file is available for writing",fileName);
PrintFormat("File path: %s\\Files\\",TerminalInfoString(TERMINAL_DATA_PATH));
//--- write the time and values of signals to the file
for(int i=0; i<tickCount; i++)
{
FileWrite(file_handle,tickArray[i].time_msc, ",", tickArray[i].bid, ",", tickArray[i].ask);
msg["status"] = (string) "CONNECTED";
msg["type"] = (string) "FLUSH";
msg["data"] = (string) tickArray[i].time_msc;
if(liveStream)
InformClientSocket(liveSocket, msg.Serialize());
}
//--- close the file
FileClose(file_handle);
PrintFormat("Data is written, %s file is closed",fileName);
msg["status"] = (string) "DISCONNECTED";
msg["type"] = (string) "NORMAL";
msg["data"] = (string) StringFormat("Writing to: %s\\%s", outputFile, " is finished");
if(liveStream)
InformClientSocket(liveSocket, msg.Serialize());
}
else
{
// File is not available for writing
mControl.mSetUserError(65542, GetErrorID(65542));
CheckError(__FUNCTION__);
}
connectedFlag=false;
}
// Write CVS fle to local directory
else
if(actionType=="WRITE" && chartTF!="TICK")
{
CJAVal c, d;
MqlRates r[];
int spread[];
string fileName=symbol + "-" + chartTF + ".csv"; // file name
string directoryName="Data"; // directory name
string outputFile=directoryName+"//"+fileName;
int barCount;
ENUM_TIMEFRAMES period=GetTimeframe(chartTF);
datetime fromDate=(datetime)dataObject["fromDate"].ToInt();
datetime toDate=TimeCurrent();
if(dataObject["toDate"].ToInt()!=NULL)
toDate=(datetime)dataObject["toDate"].ToInt();
Print("Fetching HISTORY");
Print("1) Symbol :"+symbol);
Print("2) Timeframe :"+EnumToString(period));
Print("3) Date from :"+TimeToString(fromDate));
if(dataObject["toDate"].ToInt()!=NULL)
Print("4) Date to:"+TimeToString(toDate));
barCount=CopyRates(symbol,period,fromDate,toDate,r);
if(CopySpread(symbol,period, fromDate, toDate, spread)!=1)
{
mControl.mSetUserError(65541, GetErrorID(65541));
}
Print("Preparing tick data of ", barCount, " ticks for ", symbol);
int file_handle=FileOpen(outputFile, FILE_WRITE | FILE_CSV);
if(file_handle!=INVALID_HANDLE)
{
ActionDoneOrError(ERR_SUCCESS, __FUNCTION__, "ERR_SUCCESS");;
PrintFormat("%s file is available for writing",outputFile);
PrintFormat("File path: %s\\Files\\",TerminalInfoString(TERMINAL_DATA_PATH));
//--- write the time and values of signals to the file
for(int i=0; i<barCount; i++)
FileWrite(file_handle,r[i].time, ",", r[i].open, ",", r[i].high, ",", r[i].low, ",", r[i].close, ",", r[i].tick_volume, spread[i]);
//--- close the file
FileClose(file_handle);
PrintFormat("Data is written, %s file is closed", outputFile);
}
else
{
mControl.mSetUserError(65542, GetErrorID(65542));
CheckError(__FUNCTION__);
}
}
else
if(actionType=="DATA" && chartTF=="TICK")
{
CJAVal data, d;
MqlTick tickArray[];
ENUM_TIMEFRAMES period=GetTimeframe(chartTF);
datetime fromDate=(datetime)dataObject["fromDate"].ToInt();
datetime toDate=TimeCurrent();
if(dataObject["toDate"].ToInt()!=NULL)
toDate=(datetime)dataObject["toDate"].ToInt();
if(debug)
{
Print("Fetching HISTORY");
Print("1) Symbol: "+symbol);
Print("2) Timeframe: Ticks");
Print("3) Date from: "+TimeToString(fromDate));
if(dataObject["toDate"].ToInt()!=NULL)
Print("4) Date to:"+TimeToString(toDate));
}
int tickCount = 0;
ulong fromDateM = StringToTime(fromDate);
ulong toDateM = StringToTime(toDate);
tickCount=CopyTicksRange(symbol,tickArray, COPY_TICKS_ALL, 1000*(ulong)fromDateM, 1000*(ulong)toDateM);
Print("Preparing tick data of ", tickCount, " ticks for ", symbol);
/*
if(correctTickHistory)
CorrectTicks(symbol,tickArray);
*/
if(tickCount)
{
for(int i=0; i<tickCount; i++)
{
data[i][0]=(long) tickArray[i].time_msc;
data[i][1]=(double) tickArray[i].bid;
data[i][2]=(double) tickArray[i].ask;
}
d["data"].Set(data);
}
else
{
d["data"].Add(data);
}
Print("Finished preparing tick data");
d["symbol"]=symbol;
d["timeframe"]=chartTF;
PushHistoricalData(d);
}
else
if(actionType=="DATA" && chartTF!="TICK")
{
CJAVal c, d;
MqlRates r[];
int spread[];
int barCount=0;
ENUM_TIMEFRAMES period=GetTimeframe(chartTF);
datetime fromDate=(datetime)dataObject["fromDate"].ToInt();
datetime toDate=TimeCurrent();
if(dataObject["toDate"].ToInt()!=NULL)
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));
if(dataObject["toDate"].ToInt()!=NULL)
Print("4) Date to:"+TimeToString(toDate));
}
barCount=CopyRates(symbol, period, fromDate, toDate, r);
if(CopySpread(symbol,period, fromDate, toDate, spread)!=1) { /*mControl.Check();*/ }
if(barCount)
{
for(int i=0; i<barCount; i++)
{
c[i][0]=(long) r[i].time;
c[i][1]=(double) r[i].open;
c[i][2]=(double) r[i].high;
c[i][3]=(double) r[i].low;
c[i][4]=(double) r[i].close;
c[i][5]=(double) r[i].tick_volume;
c[i][6]=(int) spread[i];
}
d["data"].Set(c);
}
else
{
d["data"].Add(c);
}
d["symbol"]=symbol;
d["timeframe"]=chartTF;
PushHistoricalData(d);
}
else
if(actionType=="TRADES")
{
CDealInfo tradeInfo;
CJAVal trades, data;
if(HistorySelect(0,TimeCurrent()))
{
// Get total deals in history
int total = HistoryDealsTotal();
ulong ticket; // deal ticket
for(int i=0; i<total; i++)
{
if((ticket=HistoryDealGetTicket(i))>0)
{
tradeInfo.Ticket(ticket);
data["ticket"]=(long) tradeInfo.Ticket();
data["time"]=(long) tradeInfo.Time();
data["price"]=(double) tradeInfo.Price();
data["volume"]=(double) tradeInfo.Volume();
data["symbol"]=(string) tradeInfo.Symbol();
data["type"]=(string) tradeInfo.TypeDescription();
data["entry"]=(long) tradeInfo.Entry();
data["profit"]=(double) tradeInfo.Profit();
trades["trades"].Add(data);
}
}
}
else
{
trades["trades"].Add(data);
}
string t=trades.Serialize();
if(debug)
Print(t);
InformClientSocket(dataSocket,t);
}
else
{
mControl.mSetUserError(65538, GetErrorID(65538));
CheckError(__FUNCTION__);
}
}
//+------------------------------------------------------------------+
+244
View File
@@ -0,0 +1,244 @@
//+------------------------------------------------------------------+
//| StartIndicator.mqh |
//| Gunther Schulz |
//| https://github.com/khramkov/MQL5-JSON-API |
//+------------------------------------------------------------------+
#property copyright "Gunther Schulz"
#property link "https://github.com/khramkov/MQL5-JSON-API"
#define START_INDICATOR true
int INDICATOR_DATA_PORT=15559;
Socket indicatorDataSocket(context,ZMQ_PUSH);
struct Indicator
{
long id; // Internal id
string indicatorId; // UUID
int indicatorHandle; // Internal id/handle
int indicatorParamCount; // Number of parameters to be passed to the indicator
int indicatorBufferCount; // Numnber of buffers to be returned bythe indicator
};
Indicator indicators[];
int indicatorCount = 0;
//+------------------------------------------------------------------+
//| Check if string is a representation of a number |
//+------------------------------------------------------------------+
bool IsNumberAsString(string str)
{
// MQL5 seems to return true if the values are the same, no matter the data type, in this case comparing str and dbl/int.
// (str "2.1" == double 2.1) will return true.
double dbl = StringToDouble(str);
int integer = StringToInteger(str);
// Compaing to both int and double to cover both cases
if(str==dbl || str==integer)
return true;
else
return false;
}
//+------------------------------------------------------------------+
//| Get index of indicator handler array by indicator id string |
//+------------------------------------------------------------------+
int GetIndicatorIdxByIndicatorId(string indicatorId)
{
for(int i=0; i<indicatorCount; i++)
{
if(indicators[i].indicatorId == indicatorId)
{
return i;
}
}
return -1;
}
//+------------------------------------------------------------------+
//| Start new indicator or request indicator data |
//+------------------------------------------------------------------+
void IndicatorControl(CJAVal &dataObject)
{
string actionType=dataObject["actionType"].ToStr();
if(actionType=="REQUEST")
{
GetIndicatorResult(dataObject);
}
else
if(actionType=="ATTACH")
{
StartIndicator(dataObject);
}
}
//+------------------------------------------------------------------+
//| Start new indicator instance |
//+------------------------------------------------------------------+
void StartIndicator(CJAVal &dataObject)
{
// TODO map Indicators Constants https://www.mql5.com/en/docs/constants/indicatorconstants
string symbol=dataObject["symbol"].ToStr();
string chartTF=dataObject["chartTF"].ToStr();
string id=dataObject["id"].ToStr();
string indicatorName=dataObject["name"].ToStr();
indicatorCount++;
ArrayResize(indicators,indicatorCount);
int idx = indicatorCount-1;
indicators[idx].indicatorId = id;
indicators[idx].indicatorBufferCount = dataObject["linecount"].ToInt();
double params[];
indicators[idx].indicatorParamCount = dataObject["params"].Size();
for(int i=0; i<indicators[idx].indicatorParamCount; i++)
{
// TODO test it. Is it ok to pass EnumInts as Doubles for params?
ArrayResize(params, i+1);
string paramStr = dataObject["params"][i].ToStr();
if(IsNumberAsString(paramStr))
params[i] = StringToDouble(paramStr);
else
{
params[i] = StringToEnumInt(paramStr);
mControl.mResetLastError(); // TODO find where the Error 4003 is craeted in StringToEnumInt
}
}
ENUM_TIMEFRAMES period = GetTimeframe(chartTF);
// Case construct for passing variable parameter count to the iCustom function is used, because MQL5 does not seem to support expanding an array to a function parameter list
switch(indicators[idx].indicatorParamCount)
{
case 0:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName);
break;
case 1:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0]);
break;
case 2:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1]);
break;
case 3:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2]);
break;
case 4:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3]);
break;
case 5:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4]);
break;
case 6:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4], params[5]);
break;
case 7:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
break;
case 8:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
break;
case 9:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]);
break;
case 10:
indicators[idx].indicatorHandle = iCustom(symbol,period,indicatorName, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9]);
break;
default:
// TODO error handling
break;
}
CJAVal message;
if(mControl.mGetLastError())
{
int lastError = mControl.mGetLastError();
string desc = mControl.mGetDesc();
mControl.Check();
message["error"]=(bool) true;
message["lastError"]=(string) lastError;
message["description"]=desc;
message["function"]=(string) __FUNCTION__;
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(indicatorDataSocket,t);
}
else
{
message["error"]=(bool) false;
message["id"] = (string) id;
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(indicatorDataSocket,t);
}
}
//+------------------------------------------------------------------+
//| Get indicator results |
//+------------------------------------------------------------------+
void GetIndicatorResult(CJAVal &dataObject)
{
datetime fromDate=dataObject["fromDate"].ToInt();
string id=dataObject["id"].ToStr();
string indicatorName=dataObject["indicatorName"].ToStr();
int idx = GetIndicatorIdxByIndicatorId(id);
double values[2];
CJAVal results;
// Cycle through all avaliable buffer positions
for(int i=0; i<indicators[idx].indicatorBufferCount; i++)
{
values[0] = 0.0;
values[1] = 0.0;
results[i] = 0.0;
if(idx >= 0)
{
if(CopyBuffer(indicators[idx].indicatorHandle, i, fromDate, 1, values) < 0)
{
if(mControl.mGetLastError())
{
CJAVal message;
int lastError = mControl.mGetLastError();
string desc = mControl.mGetDesc();
mControl.Check();
message["error"]=(bool) true;
message["lastError"]=(string) lastError;
message["description"]=desc;
message["function"]=(string) __FUNCTION__;
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(indicatorDataSocket,t);
}
}
results[i] = DoubleToString(values[0]);
}
}
CJAVal message;
message["error"]=(bool) false;
message["id"] = (string) id;
message["data"].Set(results);
string t=message.Serialize();
if(debug)
Print(t);
InformClientSocket(indicatorDataSocket,t);
}
//+------------------------------------------------------------------+
-201
View File
@@ -1,201 +0,0 @@
//+------------------------------------------------------------------+
//| OnTick(string symbol).mqh |
//| Copyright 2010, Lizar |
//| https://login.mql5.com/ru/users/Lizar |
//| modifications by Gunther Schulz based on Revision 2011.01.30 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Lizar"
#property link "https://login.mql5.com/ru/users/Lizar"
//+------------------------------------------------------------------+
//| The events enumeration is implemented as flags |
//| the events can be combined using the OR ("|") logical operation |
//+------------------------------------------------------------------+
enum ENUM_CHART_EVENT_SYMBOL
{
CHARTEVENT_NO =0, // Events disabled
CHARTEVENT_INIT =0, // "Initialization" event
CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
CHARTEVENT_TICK =0x00200000, // "New tick" event
CHARTEVENT_ALL =0xFFFFFFFF, // All events enabled
};
//---
#define CHART_EVENT_SYMBOL CHARTEVENT_TICK // frequency of calling OnTick()
int _handle_[];
int _symbols_total_ = 0; // total symbols
int _symbols_market_ = 0; // number of symbols in Market Watch
bool _market_watch_ = false; // use symbols from Market Watch
bool _testing_ = false; // In testing mode
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int LoadSymbol(string symbol)
// TODO only load when not already exists
{
//--- check if we work in Strategy Tester:
_testing_=((bool)MQL5InfoInteger(MQL5_TESTING) ||
(bool)MQL5InfoInteger(MQL5_OPTIMIZATION) ||
(bool)MQL5InfoInteger(MQL5_VISUAL_MODE));
//--- check settings
if(_testing_ )
{
Print("Error: Strategy Tester is not working. ");
return(1);
}
//--- Initialization of variables and arrays:
_symbols_total_=SymbolsTotal(false); // total symbols
ArrayResize(_handle_,_symbols_total_); // resize array for handles of "spys"
ArrayInitialize(_handle_,INVALID_HANDLE); // initalizae array for handles of "spys"
_symbols_total_=ArraySize(tickSymbols);
for(int i=0;i<_symbols_total_;i++)
if(!LoadAgent(i, symbol)) return(1);
return(0);
}
int UnloadAllSymbols()
{
//--- check if we work in Strategy Tester:
_testing_=((bool)MQL5InfoInteger(MQL5_TESTING) ||
(bool)MQL5InfoInteger(MQL5_OPTIMIZATION) ||
(bool)MQL5InfoInteger(MQL5_VISUAL_MODE));
//--- check settings
if(_testing_ )
{
Print("Error: Strategy Tester is not working. ");
return(1);
}
_symbols_total_=ArraySize(tickSymbols);
for(int i=0;i<_symbols_total_;i++)
if(!DeLoadAgent(i, tickSymbols[i])) return(false);
return(0);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//| Used only in Strategy Tester |
//+------------------------------------------------------------------+
void OnTick()
{
if(_testing_)
{
for(int i=0;i<_symbols_total_;i++)
{
string __symbol__=tickSymbols[i];
if(MathAbs(GlobalVariableGet(__symbol__+"_flag")-2)<0.1)
{
GlobalVariableSet(__symbol__+"_flag",1);
OnTick(__symbol__);
}
}
}
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Call of OnTick(string symbol) or OnChartEvent event handler:
if(id==CHARTEVENT_CUSTOM_LAST)
{
OnTick(sparam);
//--- synchronize "agents" with Market Watch if necessary:
}
else _OnChartEvent(id,lparam,dparam,sparam);
}
#define OnChartEvent _OnChartEvent // rendefine of OnChartEvent function
//+------------------------------------------------------------------+
//| Function for loading of "spys" |
//| INPUT: __id__ - id, corresponds to the symbol index in |
//| the list of symbols |
//| __symbol__ - symbol name |
//| OUTPUT: true - if successful |
//| false - if error |
//| REMARK: no. |
//+------------------------------------------------------------------+
bool LoadAgent(int __id__, string __symbol__)
{
_handle_[__id__]=iCustom(__symbol__,_Period,"Spy Control panel MCM",ChartID(),65534,CHART_EVENT_SYMBOL);
if(_handle_[__id__]==INVALID_HANDLE)
{
Print("Error in setting of agent for ",__symbol__);
return(false);
}
Print("The agent for ",__symbol__," is set.");
return(true);
}
//+------------------------------------------------------------------+
//| Function for release of the "spys" |
//| INPUT: __id__ - id, corresponds to the symbol index in |
//| the list of symbols |
//| __symbol__ - symbol name |
//| OUTPUT: true - if successful |
//| false - if error |
//| REMARK: no. |
//+------------------------------------------------------------------+
bool DeLoadAgent(int __id__, string __symbol__)
{
if(_handle_[__id__]!=INVALID_HANDLE)
{
if(!IndicatorRelease(_handle_[__id__]))
{
Print("Error deletion of agent for ",__symbol__);
return(false);
}
Print("The agent for ",__symbol__," is deleted.");
_handle_[__id__]=INVALID_HANDLE;
}
return(true);
}
//+------------------------------ end -------------------------------+
+382
View File
@@ -0,0 +1,382 @@
#define MIN_ENUM_VALUES 0
#define MAX_ENUM_VALUES 255
//+------------------------------------------------------------------+
//| StringToEnum : Convert a string to an ENUM value, |
//| it loops between min(0) and max(255), adjustable if needed. |
//| Non existing enum value defined as -1. If -1 is used as an |
//| enum value, code need to be adjusted to an other default. |
//| Parameters : |
//| in - string to convert |
//| out - ENUM value |
//| @return - int if conversion succeeded, false otherwise |
//| |
//| Based on: |
//| https://www.mql5.com/en/forum/61741/page3#comment_5491344 |
//+------------------------------------------------------------------+
template<typename ENUM>
int StringToEnum(string in,ENUM &out)
{
out=-1;
//---
for(int i=MIN_ENUM_VALUES;i<=MAX_ENUM_VALUES;i++)
{
ENUM enumValue=(ENUM)i;
if(in==EnumToString(enumValue))
{
out=enumValue;
break;
}
}
//---
return(out);
}
int StringToEnumInt(string indicatorConstantString)
{
int r = -1;
ENUM_ACCOUNT_INFO_DOUBLE a1;
r = StringToEnum(indicatorConstantString,a1);
//if(debug) Print("ENUM type: ENUM_ACCOUNT_INFO_DOUBLE");
if(r>=0)return r;
ENUM_ACCOUNT_INFO_INTEGER a2;
r = StringToEnum(indicatorConstantString,a2);
if(r>=0)return r;
ENUM_ACCOUNT_INFO_STRING a3;
r = StringToEnum(indicatorConstantString,a3);
if(r>=0)return r;
ENUM_ACCOUNT_MARGIN_MODE a4;
r = StringToEnum(indicatorConstantString,a4);
if(r>=0)return r;
ENUM_ACCOUNT_STOPOUT_MODE a5;
r = StringToEnum(indicatorConstantString,a5);
if(r>=0)return r;
ENUM_ACCOUNT_TRADE_MODE a6;
r = StringToEnum(indicatorConstantString,a6);
if(r>=0)return r;
ENUM_ALIGN_MODE a7;
r = StringToEnum(indicatorConstantString,a7);
if(r>=0)return r;
ENUM_ANCHOR_POINT a8;
r = StringToEnum(indicatorConstantString,a8);
if(r>=0)return r;
ENUM_APPLIED_PRICE a9;
r = StringToEnum(indicatorConstantString,a9);
if(r>=0)return r;
ENUM_APPLIED_PRICE a10;
r = StringToEnum(indicatorConstantString,a10);
if(r>=0)return r;
ENUM_APPLIED_VOLUME a11;
r = StringToEnum(indicatorConstantString,a11);
if(r>=0)return r;
ENUM_ARROW_ANCHOR a12;
r = StringToEnum(indicatorConstantString,a12);
if(r>=0)return r;
ENUM_BASE_CORNER a13;
r = StringToEnum(indicatorConstantString,a13);
if(r>=0)return r;
ENUM_BOOK_TYPE a14;
r = StringToEnum(indicatorConstantString,a14);
if(r>=0)return r;
ENUM_BORDER_TYPE a15;
r = StringToEnum(indicatorConstantString,a15);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_FREQUENCY a16;
r = StringToEnum(indicatorConstantString,a16);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_IMPACT a17;
r = StringToEnum(indicatorConstantString,a17);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_IMPORTANCE a18;
r = StringToEnum(indicatorConstantString,a18);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_MULTIPLIER a19;
r = StringToEnum(indicatorConstantString,a19);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_SECTOR a20;
r = StringToEnum(indicatorConstantString,a20);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_TIMEMODE a21;
r = StringToEnum(indicatorConstantString,a21);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_TYPE a22;
r = StringToEnum(indicatorConstantString,a22);
if(r>=0)return r;
ENUM_CALENDAR_EVENT_UNIT a23;
r = StringToEnum(indicatorConstantString,a23);
if(r>=0)return r;
ENUM_CHART_EVENT a24;
r = StringToEnum(indicatorConstantString,a24);
if(r>=0)return r;
ENUM_CHART_MODE a25;
r = StringToEnum(indicatorConstantString,a25);
if(r>=0)return r;
ENUM_CHART_POSITION a26;
r = StringToEnum(indicatorConstantString,a26);
if(r>=0)return r;
ENUM_CHART_PROPERTY_DOUBLE a27;
r = StringToEnum(indicatorConstantString,a27);
if(r>=0)return r;
ENUM_CHART_PROPERTY_INTEGER a28;
r = StringToEnum(indicatorConstantString,a28);
if(r>=0)return r;
ENUM_CHART_PROPERTY_STRING a29;
r = StringToEnum(indicatorConstantString,a29);
if(r>=0)return r;
ENUM_CHART_VOLUME_MODE a30;
r = StringToEnum(indicatorConstantString,a30);
if(r>=0)return r;
ENUM_CL_DEVICE_TYPE a31;
r = StringToEnum(indicatorConstantString,a31);
if(r>=0)return r;
ENUM_COLOR_FORMAT a32;
r = StringToEnum(indicatorConstantString,a32);
if(r>=0)return r;
ENUM_CRYPT_METHOD a33;
r = StringToEnum(indicatorConstantString,a33);
if(r>=0)return r;
ENUM_CUSTOMIND_PROPERTY_DOUBLE a34;
r = StringToEnum(indicatorConstantString,a34);
if(r>=0)return r;
ENUM_CHART_PROPERTY_INTEGER a35;
r = StringToEnum(indicatorConstantString,a35);
if(r>=0)return r;
ENUM_CUSTOMIND_PROPERTY_STRING a36;
r = StringToEnum(indicatorConstantString,a36);
if(r>=0)return r;
ENUM_DATABASE_EXPORT_FLAGS a37;
r = StringToEnum(indicatorConstantString,a37);
if(r>=0)return r;
ENUM_DATABASE_FIELD_TYPE a38;
r = StringToEnum(indicatorConstantString,a38);
if(r>=0)return r;
ENUM_DATABASE_OPEN_FLAGS a39;
r = StringToEnum(indicatorConstantString,a39);
if(r>=0)return r;
ENUM_DATABASE_PRINT_FLAGS a40;
r = StringToEnum(indicatorConstantString,a40);
if(r>=0)return r;
ENUM_DATATYPE a41;
r = StringToEnum(indicatorConstantString,a41);
if(r>=0)return r;
ENUM_DAY_OF_WEEK a42;
r = StringToEnum(indicatorConstantString,a42);
if(r>=0)return r;
ENUM_DEAL_ENTRY a43;
r = StringToEnum(indicatorConstantString,a43);
if(r>=0)return r;
ENUM_DEAL_PROPERTY_DOUBLE a44;
r = StringToEnum(indicatorConstantString,a44);
if(r>=0)return r;
ENUM_DEAL_PROPERTY_INTEGER a45;
r = StringToEnum(indicatorConstantString,a45);
if(r>=0)return r;
ENUM_DEAL_PROPERTY_STRING a46;
r = StringToEnum(indicatorConstantString,a46);
if(r>=0)return r;
ENUM_DEAL_REASON a47;
r = StringToEnum(indicatorConstantString,a47);
if(r>=0)return r;
ENUM_DEAL_TYPE a48;
r = StringToEnum(indicatorConstantString,a48);
if(r>=0)return r;
ENUM_DRAW_TYPE a49;
r = StringToEnum(indicatorConstantString,a49);
if(r>=0)return r;
ENUM_DX_BUFFER_TYPE a50;
r = StringToEnum(indicatorConstantString,a50);
if(r>=0)return r;
ENUM_DX_FORMAT a51;
r = StringToEnum(indicatorConstantString,a51);
if(r>=0)return r;
ENUM_DX_HANDLE_TYPE a52;
r = StringToEnum(indicatorConstantString,a52);
if(r>=0)return r;
ENUM_DX_PRIMITIVE_TOPOLOGY a53;
r = StringToEnum(indicatorConstantString,a53);
if(r>=0)return r;
ENUM_DX_SHADER_TYPE a54;
r = StringToEnum(indicatorConstantString,a54);
if(r>=0)return r;
ENUM_ELLIOT_WAVE_DEGREE a55;
r = StringToEnum(indicatorConstantString,a55);
if(r>=0)return r;
ENUM_FILESELECT_FLAGS a56;
r = StringToEnum(indicatorConstantString,a56);
if(r>=0)return r;
ENUM_FILE_POSITION a57;
r = StringToEnum(indicatorConstantString,a57);
if(r>=0)return r;
ENUM_FILE_PROPERTY_INTEGER a58;
r = StringToEnum(indicatorConstantString,a58);
if(r>=0)return r;
ENUM_GANN_DIRECTION a59;
r = StringToEnum(indicatorConstantString,a59);
if(r>=0)return r;
ENUM_INDEXBUFFER_TYPE a60;
r = StringToEnum(indicatorConstantString,a60);
if(r>=0)return r;
ENUM_INDICATOR a61;
r = StringToEnum(indicatorConstantString,a61);
if(r>=0)return r;
ENUM_INIT_RETCODE a62;
r = StringToEnum(indicatorConstantString,a62);
if(r>=0)return r;
ENUM_LICENSE_TYPE a63;
r = StringToEnum(indicatorConstantString,a63);
if(r>=0)return r;
ENUM_LINE_STYLE a64;
r = StringToEnum(indicatorConstantString,a64);
if(r>=0)return r;
ENUM_MA_METHOD a65;
r = StringToEnum(indicatorConstantString,a65);
if(r>=0)return r;
ENUM_MQL_INFO_INTEGER a66;
r = StringToEnum(indicatorConstantString,a66);
if(r>=0)return r;
ENUM_MQL_INFO_STRING a67;
r = StringToEnum(indicatorConstantString,a67);
if(r>=0)return r;
ENUM_OBJECT a68;
r = StringToEnum(indicatorConstantString,a68);
if(r>=0)return r;
ENUM_OBJECT_PROPERTY_DOUBLE a69;
r = StringToEnum(indicatorConstantString,a69);
if(r>=0)return r;
ENUM_OBJECT_PROPERTY_INTEGER a70;
r = StringToEnum(indicatorConstantString,a70);
if(r>=0)return r;
ENUM_OBJECT_PROPERTY_STRING a71;
r = StringToEnum(indicatorConstantString,a71);
if(r>=0)return r;
ENUM_OPENCL_HANDLE_TYPE a72;
r = StringToEnum(indicatorConstantString,a72);
if(r>=0)return r;
ENUM_OPENCL_PROPERTY_INTEGER a73;
r = StringToEnum(indicatorConstantString,a73);
if(r>=0)return r;
ENUM_PLOT_PROPERTY_STRING a74;
r = StringToEnum(indicatorConstantString,a74);
if(r>=0)return r;
ENUM_POINTER_TYPE a75;
r = StringToEnum(indicatorConstantString,a75);
if(r>=0)return r;
ENUM_POSITION_PROPERTY_DOUBLE a76;
r = StringToEnum(indicatorConstantString,a76);
if(r>=0)return r;
ENUM_ORDER_PROPERTY_INTEGER a77;
r = StringToEnum(indicatorConstantString,a77);
if(r>=0)return r;
ENUM_PLOT_PROPERTY_STRING a78;
r = StringToEnum(indicatorConstantString,a78);
if(r>=0)return r;
ENUM_PROGRAM_TYPE a79;
r = StringToEnum(indicatorConstantString,a79);
if(r>=0)return r;
ENUM_SERIESMODE a80;
r = StringToEnum(indicatorConstantString,a80);
if(r>=0)return r;
ENUM_SERIES_INFO_INTEGER a81;
r = StringToEnum(indicatorConstantString,a81);
if(r>=0)return r;
ENUM_SIGNAL_BASE_DOUBLE a82;
r = StringToEnum(indicatorConstantString,a82);
if(r>=0)return r;
ENUM_SIGNAL_BASE_INTEGER a83;
r = StringToEnum(indicatorConstantString,a83);
if(r>=0)return r;
ENUM_SIGNAL_BASE_STRING a84;
r = StringToEnum(indicatorConstantString,a84);
if(r>=0)return r;
ENUM_SIGNAL_INFO_DOUBLE a85;
r = StringToEnum(indicatorConstantString,a85);
if(r>=0)return r;
ENUM_SIGNAL_INFO_INTEGER a86;
r = StringToEnum(indicatorConstantString,a86);
if(r>=0)return r;
ENUM_SIGNAL_INFO_STRING a87;
r = StringToEnum(indicatorConstantString,a87);
if(r>=0)return r;
ENUM_STATISTICS a88;
r = StringToEnum(indicatorConstantString,a88);
if(r>=0)return r;
ENUM_STO_PRICE a89;
r = StringToEnum(indicatorConstantString,a89);
if(r>=0)return r;
ENUM_SYMBOL_CALC_MODE a90;
r = StringToEnum(indicatorConstantString,a90);
if(r>=0)return r;
ENUM_SYMBOL_CHART_MODE a91;
r = StringToEnum(indicatorConstantString,a91);
if(r>=0)return r;
ENUM_SYMBOL_INFO_DOUBLE a92;
r = StringToEnum(indicatorConstantString,a92);
if(r>=0)return r;
ENUM_SYMBOL_INFO_INTEGER a93;
r = StringToEnum(indicatorConstantString,a93);
if(r>=0)return r;
ENUM_SYMBOL_INFO_STRING a94;
r = StringToEnum(indicatorConstantString,a94);
if(r>=0)return r;
ENUM_STATISTICS a95;
r = StringToEnum(indicatorConstantString,a95);
if(r>=0)return r;
ENUM_STO_PRICE a96;
r = StringToEnum(indicatorConstantString,a96);
if(r>=0)return r;
ENUM_SYMBOL_CALC_MODE a97;
r = StringToEnum(indicatorConstantString,a97);
if(r>=0)return r;
ENUM_SYMBOL_CHART_MODE a98;
r = StringToEnum(indicatorConstantString,a98);
if(r>=0)return r;
ENUM_SYMBOL_INFO_DOUBLE a99;
r = StringToEnum(indicatorConstantString,a99);
if(r>=0)return r;
ENUM_SYMBOL_INFO_INTEGER a100;
r = StringToEnum(indicatorConstantString,a100);
if(r>=0)return r;
ENUM_SYMBOL_INFO_STRING a101;
r = StringToEnum(indicatorConstantString,a101);
if(r>=0)return r;
ENUM_SYMBOL_OPTION_MODE a102;
r = StringToEnum(indicatorConstantString,a102);
if(r>=0)return r;
ENUM_SYMBOL_OPTION_RIGHT a103;
r = StringToEnum(indicatorConstantString,a103);
if(r>=0)return r;
ENUM_SYMBOL_ORDER_GTC_MODE a104;
r = StringToEnum(indicatorConstantString,a104);
if(r>=0)return r;
ENUM_SYMBOL_SWAP_MODE a105;
r = StringToEnum(indicatorConstantString,a105);
if(r>=0)return r;
ENUM_SYMBOL_TRADE_EXECUTION a106;
r = StringToEnum(indicatorConstantString,a106);
if(r>=0)return r;
ENUM_SYMBOL_TRADE_MODE a107;
r = StringToEnum(indicatorConstantString,a107);
if(r>=0)return r;
ENUM_TERMINAL_INFO_DOUBLE a108;
r = StringToEnum(indicatorConstantString,a108);
if(r>=0)return r;
ENUM_TERMINAL_INFO_INTEGER a109;
r = StringToEnum(indicatorConstantString,a109);
if(r>=0)return r;
ENUM_TERMINAL_INFO_STRING a110;
r = StringToEnum(indicatorConstantString,a110);
if(r>=0)return r;
ENUM_TIMEFRAMES a111;
r = StringToEnum(indicatorConstantString,a111);
if(r>=0)return r;
ENUM_TRADE_REQUEST_ACTIONS a112;
r = StringToEnum(indicatorConstantString,a112);
if(r>=0)return r;
ENUM_TRADE_TRANSACTION_TYPE a113;
r = StringToEnum(indicatorConstantString,a113);
if(r>=0)return r;
return(-1);
}
+449
View File
@@ -0,0 +1,449 @@
//+------------------------------------------------------------------+
//| ControlErrors.mqh |
//| Copyright KlimMalgin |
//| The library should be located in directory: |
//| MetaTrader 5/MQL5/Include/ |
//| https://www.mql5.com/en/articles/70 |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link ""
class ControlErrors
{
private:
// Flags that define what types of reports need to be enabled
bool _PlaySound; // Play or don't play a sound when an error occurs.
bool _PrintInfo; // Add error details to the journal of Expert Advisors
bool _AlertInfo; // Generate Alert with error details
bool _WriteFile; // Record reports on errors into a file or not
// A structure for storing error data elements that use this structure
struct Code
{
int code; // Error code
string desc; // Description of the error code
};
Code Errors[]; // Array that contains error codes and their descriptions
Code _UserError; // Stores information about a custome error
Code _Error; // Stores information about the last error of any type
// Different service properties
short _CountErrors; // Number of errors stored in array Errors[]
string _PlaySoundFile; // File that will be played for an alert sound
string _DataPath; // Path to the log storing directory
public:
// Constructor
ControlErrors(void);
// Methods for setting flags
void SetSound(bool value); // Play or don't play a sound when an error occurs
void SetPrint(bool value); // Enter error data the the journal of Expert Advisors or not
void SetAlert(bool value); // Generate an Alert message or not
void SetWriteFlag(bool flag); // Set the writing flag. true - keep logs, false - do not keep
// Methods for working with errors
int mGetLastError(); // Returns contents of the system variable _LastError
int mGetError(); // Returns code of the last obtained error
int mGetTypeError(); // Returns error type (Custom = 1 ore predefined = 0)
void mResetLastError(); // Resets the contents of the system variable _LastError
void mSetUserError(ushort value, string desc = ""); // Sets the custom error
void mResetUserError(); // Resets class fields that contain information about the custom error
void mResetError(); // Resets the structure that contains information about the last error
string mGetDesc(int nErr = 0); // Returns error description by the number, or that of the current error of no number
int Check(string st = ""); // Method to check the current system state for errors
// Alert methods (Alert, Print, Sound)
void mAlert(string message = "");
void mPrint(string message = "");
void mSound();
// Various service methods
void SetPlaySoundFile(string file); // Method sets the file name to play an sound
void SetWritePath(string path); // Set the path to store logs
int mFileWrite(string message = "");// Record into a file the available information about the last error
};
void ControlErrors::ControlErrors(void)
{
SetAlert(false);
SetPrint(false);
SetSound(false);
SetWriteFlag(false);
SetPlaySoundFile("alert.wav");
SetWritePath("LogErrors.txt");
_CountErrors = 150;
ArrayResize(Errors, _CountErrors);
// Return codes of a trade server
Errors[0].code = 10004;Errors[0].desc = "Requote";
Errors[1].code = 10006;Errors[1].desc = "Request rejected";
Errors[2].code = 10007;Errors[2].desc = "Request canceled by trader";
Errors[3].code = 10008;Errors[3].desc = "Order placed";
Errors[4].code = 10009;Errors[4].desc = "Request is completed";
Errors[5].code = 10010;Errors[5].desc = "Request is partially completed";
Errors[6].code = 10011;Errors[6].desc = "Request processing error";
Errors[7].code = 10012;Errors[7].desc = "Request canceled by timeout";
Errors[8].code = 10013;Errors[8].desc = "Invalid request";
Errors[9].code = 10014;Errors[9].desc = "Invalid volume in the request";
Errors[10].code = 10015;Errors[10].desc = "Invalid price in the request";
Errors[11].code = 10016;Errors[11].desc = "Invalid stops in the request";
Errors[12].code = 10017;Errors[12].desc = "Trade is disabled";
Errors[13].code = 10018;Errors[13].desc = "Market is closed";
Errors[14].code = 10019;Errors[14].desc = "There is not enough money to fulfill the request";
Errors[15].code = 10020;Errors[15].desc = "Prices changed";
Errors[16].code = 10021;Errors[16].desc = "There are no quotes to process the request";
Errors[17].code = 10022;Errors[17].desc = "Invalid order expiration date in the request";
Errors[18].code = 10023;Errors[18].desc = "Order state changed";
Errors[19].code = 10024;Errors[19].desc = "Too frequent requests";
Errors[20].code = 10025;Errors[20].desc = "No changes in request";
Errors[21].code = 10026;Errors[21].desc = "Autotrading disabled by server";
Errors[22].code = 10027;Errors[22].desc = "Autotrading disabled by client terminal";
Errors[23].code = 10028;Errors[23].desc = "Request locked for processing";
Errors[24].code = 10029;Errors[24].desc = "Order or position frozen";
Errors[25].code = 10030;Errors[25].desc = "Invalid order filling type";
// Common Errors
Errors[26].code = 4001;Errors[26].desc = "Unexpected internal error";
Errors[27].code = 4002;Errors[27].desc = "Wrong parameter in the inner call of the client terminal function";
Errors[28].code = 4003;Errors[28].desc = "Wrong parameter when calling the system function";
Errors[29].code = 4004;Errors[29].desc = "Not enough memory to perform the system function";
Errors[30].code = 4005;Errors[30].desc = "The structure contains objects of strings and/or dynamic arrays and/or structure of such objects and/or classes";
Errors[31].code = 4006;Errors[31].desc = "Array of a wrong type, wrong size, or a damaged object of a dynamic array";
Errors[32].code = 4007;Errors[32].desc = "Not enough memory for the relocation of an array, or an attempt to change the size of a static array";
Errors[33].code = 4008;Errors[33].desc = "Not enough memory for the relocation of string";
Errors[34].code = 4009;Errors[34].desc = "Not initialized string";
Errors[35].code = 4010;Errors[35].desc = "Invalid date and/or time";
Errors[36].code = 4011;Errors[36].desc = "Requested array size exceeds 2 GB";
Errors[37].code = 4012;Errors[37].desc = "Wrong pointer";
Errors[38].code = 4013;Errors[38].desc = "Wrong type of pointer";
Errors[39].code = 4014;Errors[39].desc = "System function is not allowed to call";
// Charts
Errors[40].code = 4101;Errors[40].desc = "Wrong chart ID";
Errors[41].code = 4102;Errors[41].desc = "Chart does not respond";
Errors[42].code = 4103;Errors[42].desc = "Chart not found";
Errors[43].code = 4104;Errors[43].desc = "No Expert Advisor in the chart that could handle the event";
Errors[44].code = 4105;Errors[44].desc = "Chart opening error";
Errors[45].code = 4106;Errors[45].desc = "Failed to change chart symbol and period";
Errors[46].code = 4107;Errors[46].desc = "Wrong parameter for timer";
Errors[47].code = 4108;Errors[47].desc = "Failed to create timer";
Errors[48].code = 4109;Errors[48].desc = "Wrong chart property ID";
Errors[49].code = 4110;Errors[49].desc = "Error creating screenshots";
Errors[50].code = 4111;Errors[50].desc = "Error navigating through chart";
Errors[51].code = 4112;Errors[51].desc = "Error applying template";
Errors[52].code = 4113;Errors[52].desc = "Subwindow containing the indicator was not found";
// Graphical Objects
Errors[53].code = 4201;Errors[53].desc = "Error working with a graphical object";
Errors[54].code = 4202;Errors[54].desc = "Graphical object was not found";
Errors[55].code = 4203;Errors[55].desc = "Wrong ID of a graphical object property";
Errors[56].code = 4204;Errors[56].desc = "Unable to get date corresponding to the value";
Errors[57].code = 4205;Errors[57].desc = "Unable to get value corresponding to the date";
// MarketInfo
Errors[58].code = 4301;Errors[58].desc = "Unknown symbol";
Errors[59].code = 4302;Errors[59].desc = "Symbol is not selected in MarketWatch";
Errors[60].code = 4303;Errors[60].desc = "Wrong identifier of a symbol property";
Errors[61].code = 4304;Errors[61].desc = "Time of the last tick is not known (no ticks)";
// History Access
Errors[62].code = 4401;Errors[62].desc = "Requested history not found";
Errors[63].code = 4402;Errors[63].desc = "Wrong ID of the history property";
// Global_Variables
Errors[64].code = 4501;Errors[64].desc = "Global variable of the client terminal is not found";
Errors[65].code = 4502;Errors[65].desc = "Global variable of the client terminal with the same name already exists";
Errors[66].code = 4510;Errors[66].desc = "Email sending failed";
Errors[67].code = 4511;Errors[67].desc = "Sound playing failed";
Errors[68].code = 4512;Errors[68].desc = "Wrong identifier of the program property";
Errors[69].code = 4513;Errors[69].desc = "Wrong identifier of the terminal property";
Errors[70].code = 4514;Errors[70].desc = "File sending via ftp failed";
// Custom Indicator Buffers
Errors[71].code = 4601;Errors[71].desc = "Not enough memory for the distribution of indicator buffers";
Errors[72].code = 4602;Errors[72].desc = "Wrong indicator buffer index";
// Custom Indicator Properties
Errors[73].code = 4603;Errors[73].desc = "Wrong ID of the custom indicator property";
// Account
Errors[74].code = 4701;Errors[74].desc = "Wrong account property ID";
Errors[75].code = 4751;Errors[75].desc = "Wrong trade property ID;";
Errors[76].code = 4752;Errors[76].desc = "Trading by Expert Advisors prohibited";
Errors[77].code = 4753;Errors[77].desc = "Position not found";
Errors[78].code = 4754;Errors[78].desc = "Order not found";
Errors[79].code = 4755;Errors[79].desc = "Deal not found";
Errors[80].code = 4756;Errors[80].desc = "Trade request sending failed";
Errors[81].code = 4757;Errors[81].desc = "Timeout exceeded when selecting (searching) specified data";
// Indicators
Errors[82].code = 4801;Errors[82].desc = "Unknown symbol";
Errors[83].code = 4802;Errors[83].desc = "Indicator cannot be created";
Errors[84].code = 4803;Errors[84].desc = "Not enough memory to add the indicator";
Errors[85].code = 4804;Errors[85].desc = "The indicator cannot be applied to another indicator";
Errors[86].code = 4805;Errors[86].desc = "Error applying an indicator to chart";
Errors[87].code = 4806;Errors[87].desc = "Requested data not found";
Errors[88].code = 4807;Errors[88].desc = "Wrong index of the requested indicator buffer";
Errors[89].code = 4808;Errors[89].desc = "Wrong number of parameters when creating an indicator";
Errors[90].code = 4809;Errors[90].desc = "No parameters when creating an indicator";
Errors[91].code = 4810;Errors[91].desc = "The first parameter in the array must be the name of the custom indicator";
Errors[92].code = 4811;Errors[92].desc = "Invalid parameter type in the array when creating an indicator";
// Depth of Market
Errors[93].code = 4901;Errors[93].desc = "Depth Of Market can not be added";
Errors[94].code = 4902;Errors[94].desc = "Depth Of Market can not be removed";
Errors[95].code = 4903;Errors[95].desc = "The data from Depth Of Market can not be obtained";
Errors[96].code = 4904;Errors[96].desc = "Error in subscribing to receive new data from Depth Of Market";
// File Operations
Errors[97].code = 5001;Errors[97].desc = "More than 64 files cannot be opened at the same time";
Errors[98].code = 5002;Errors[98].desc = "Invalid file name";
Errors[99].code = 5003;Errors[99].desc = "Too long file name";
Errors[100].code = 5004;Errors[100].desc = "File opening error";
Errors[101].code = 5005;Errors[101].desc = "Not enough memory for cache to read";
Errors[102].code = 5006;Errors[102].desc = "File deleting error";
Errors[103].code = 5007;Errors[103].desc = "A file with this handle was closed, or was not opened at all";
Errors[104].code = 5008;Errors[104].desc = "Wrong file handle";
Errors[105].code = 5009;Errors[105].desc = "The file must be opened for writing";
Errors[106].code = 5010;Errors[106].desc = "The file must be opened for reading";
Errors[107].code = 5011;Errors[107].desc = "The file must be opened as a binary one";
Errors[108].code = 5012;Errors[108].desc = "The file must be opened as a text";
Errors[109].code = 5013;Errors[109].desc = "The file must be opened as a text or CSV";
Errors[110].code = 5014;Errors[110].desc = "The file must be opened as CSV";
Errors[111].code = 5015;Errors[111].desc = "File reading error";
Errors[112].code = 5016;Errors[112].desc = "String size must be specified, because the file is opened as binary";
Errors[113].code = 5017;Errors[113].desc = "A text file must be for string arrays, for other arrays - binary";
Errors[114].code = 5018;Errors[114].desc = "This is not a file, this is a directory";
Errors[115].code = 5019;Errors[115].desc = "File does not exist";
Errors[116].code = 5020;Errors[116].desc = "File can not be rewritten";
Errors[117].code = 5021;Errors[117].desc = "Wrong directory name";
Errors[118].code = 5022;Errors[118].desc = "Directory does not exist";
Errors[119].code = 5023;Errors[119].desc = "This is a file, not a directory";
Errors[120].code = 5024;Errors[120].desc = "The directory cannot be removed";
// String Casting
Errors[121].code = 5030;Errors[121].desc = "No date in the string";
Errors[122].code = 5031;Errors[122].desc = "Wrong date in the string";
Errors[123].code = 5032;Errors[123].desc = "Wrong time in the string";
Errors[124].code = 5033;Errors[124].desc = "Error converting string to date";
Errors[125].code = 5034;Errors[125].desc = "Not enough memory for the string";
Errors[126].code = 5035;Errors[126].desc = "The string length is less than expected";
Errors[127].code = 5036;Errors[127].desc = "Too large number, more than ULONG_MAX";
Errors[128].code = 5037;Errors[128].desc = "Invalid format string";
Errors[129].code = 5038;Errors[129].desc = "Amount of format specifiers more than the parameters";
Errors[130].code = 5039;Errors[130].desc = "Amount of parameters more than the format specifiers";
Errors[131].code = 5040;Errors[131].desc = "Damaged parameter of string type";
Errors[132].code = 5041;Errors[132].desc = "Position outside the string";
Errors[133].code = 5042;Errors[133].desc = "0 added to the string end, a useless operation";
Errors[134].code = 5043;Errors[134].desc = "Unknown data type when converting to a string";
Errors[135].code = 5044;Errors[135].desc = "Damaged string object";
// Operations with Arrays
Errors[136].code = 5050;Errors[136].desc = "Copying incompatible arrays. String array can be copied only to a string array, and a numeric array - in numeric array only";
Errors[137].code = 5051;Errors[137].desc = "The receiving array is declared as AS_SERIES, and it is of insufficient size";
Errors[138].code = 5052;Errors[138].desc = "Too small array, the starting position is outside the array";
Errors[139].code = 5053;Errors[139].desc = "An array of zero length";
Errors[140].code = 5054;Errors[140].desc = "Must be a numeric array";
Errors[141].code = 5055;Errors[141].desc = "Must be a one-dimensional array";
Errors[142].code = 5056;Errors[142].desc = "Timeseries cannot be used";
Errors[143].code = 5057;Errors[143].desc = "Must be an array of type double";
Errors[144].code = 5058;Errors[144].desc = "Must be an array of type float";
Errors[145].code = 5059;Errors[145].desc = "Must be an array of type long";
Errors[146].code = 5060;Errors[146].desc = "Must be an array of type int";
Errors[147].code = 5061;Errors[147].desc = "Must be an array of type short";
Errors[148].code = 5062;Errors[148].desc = "Must be an array of type char";
}
void ControlErrors::SetAlert(bool value)
{
_AlertInfo = value;
}
void ControlErrors::SetPrint(bool value)
{
_PrintInfo = value;
}
void ControlErrors::SetSound(bool value)
{
_PlaySound = value;
}
void ControlErrors::SetWriteFlag(bool flag)
{
_WriteFile = flag;
}
void ControlErrors::SetWritePath(string path)
{
_DataPath = path;
}
void ControlErrors::SetPlaySoundFile(string file)
{
_PlaySoundFile = file;
}
int ControlErrors::mGetLastError(void)
{
_Error.code = GetLastError();
_Error.desc = mGetDesc(_Error.code);
return _Error.code;
}
void ControlErrors::mResetLastError(void)
{
ResetLastError();
}
int ControlErrors::mGetError(void)
{
return _Error.code;
}
void ControlErrors::mResetError(void)
{
_Error.code = 0;
_Error.desc = "";
}
int ControlErrors::mGetTypeError(void)
{
if (mGetError() < ERR_USER_ERROR_FIRST)
{
return 0;
}
else if (mGetError() >= ERR_USER_ERROR_FIRST)
{
return 1;
}
return -1;
}
void ControlErrors::mSetUserError(ushort value, string desc = "")
{
SetUserError(value);
_UserError.code = value;
_UserError.desc = desc;
}
void ControlErrors::mResetUserError(void)
{
_UserError.code = 0;
_UserError.desc = "";
}
string ControlErrors::mGetDesc(int nErr=0)
{
int ErrorNumber = 0;
string ReturnDesc = "";
ErrorNumber = (mGetError()>0)?mGetError():ErrorNumber;
ErrorNumber = (nErr>0)?nErr:ErrorNumber;
if ((ErrorNumber > 0) && (ErrorNumber < ERR_USER_ERROR_FIRST))
{
for (int i = 0;i<_CountErrors;i++)
{
if (Errors[i].code == ErrorNumber)
{
ReturnDesc = Errors[i].desc;
break;
}
}
}
else if (ErrorNumber > ERR_USER_ERROR_FIRST)
{
ReturnDesc = (_UserError.desc=="")?"Custom error":_UserError.desc;
}
if (ReturnDesc == ""){return "Unknown error code: "+(string)ErrorNumber;}
return ReturnDesc;
}
void ControlErrors::mAlert(string message="")
{
if (_AlertInfo == true)
{
if (message == "")
{
if (mGetError() > 0)
{
Alert("Error ",mGetError()," - ",mGetDesc());
}
}
else
{
Alert(message);
}
}
}
void ControlErrors::mPrint(string message="")
{
if (_PrintInfo == true)
{
if (message == "")
{
if (mGetError() > 0)
{
Print("Error ",mGetError()," - ",mGetDesc());
}
}
else
{
Print(message);
}
}
}
void ControlErrors::mSound(void)
{
if (_PlaySound == true)
{
PlaySound(_PlaySoundFile);
}
}
int ControlErrors::Check(string st="")
{
int errNum = 0;
errNum = mGetLastError();
mFileWrite();
mAlert(st);
mPrint(st);
mSound();
mResetError();
mResetLastError();
mResetUserError();
return errNum;
}
int ControlErrors::mFileWrite(string message = "")
{
int handle = 0,
_return = 0;
datetime time = TimeCurrent();
string text = (message != "")?message:time+" - Error "+mGetError()+" "+mGetDesc();
if (_WriteFile == true)
{
handle = FileOpen(_DataPath,FILE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
if (handle != INVALID_HANDLE)
{
ulong size = FileSize(handle);
FileSeek(handle,size,SEEK_SET);
_return = FileWrite(handle,text);
FileClose(handle);
}
}
return _return;
}
+619
View File
@@ -0,0 +1,619 @@
//+------------------------------------------------------------------+
//| JsonAPIIndicator.mq5 |
//| Copyright 2020, Gunther Schulz |
//| https://www.guntherschulz.de |
//+------------------------------------------------------------------+
#property copyright "2020 Gunther Schulz"
#property link "https://www.guntherschulz.de"
#property version "1.00"
#include <StringToEnumInt.mqh>
#include <Zmq/Zmq.mqh>
#include <Json.mqh>
// Set ports and host for ZeroMQ
string HOST="localhost";
int CHART_SUB_PORT=15562;
// ZeroMQ Cnnections
Context context("MQL5 JSON API");
Socket chartSubscriptionSocket(context,ZMQ_SUB);
//--- input parameters
#property indicator_buffers 31
#property indicator_plots 30
input string IndicatorId="";
input string ShortName="JsonAPIIndicator";
//--- indicator settings
double B0[], B1[], B2[], B3[], B4[], B5[], B6[], B7[], B8[], B9[], B10[];
double B11[], B12[], B13[], B14[], B15[], B16[], B17[], B18[], B19[], B20[];
double B21[], B22[], B23[], B24[], B25[], B26[], B27[], B28[], B29[];
bool debug = false;
int activeBufferCount = 0;
long mtChartId = 0;
bool setFormingCandleBlank = true;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// TODO subscribe only to own IndicatorId topic
// Subscribe to all topics
chartSubscriptionSocket.setSubscribe("");
chartSubscriptionSocket.setLinger(1000);
// https://www.randonomicon.com/zmq/2018/09/19/zmq-bind-vs-connect.html
// Number of messages to buffer in RAM.
// https://dzone.com/articles/zeromq-flow-control-and-other
chartSubscriptionSocket.setReceiveHighWaterMark(1000);
bool result = chartSubscriptionSocket.connect(StringFormat("tcp://%s:%d", HOST, CHART_SUB_PORT));
if(result == false)
{
Print("Failed to subscrbe on port ", CHART_SUB_PORT);
}
else
{
Print("Accepting Chart Indicator data on port ", CHART_SUB_PORT);
}
//--- indicator buffers mapping;
ArraySetAsSeries(B0,true);
ArraySetAsSeries(B1,true);
ArraySetAsSeries(B2,true);
ArraySetAsSeries(B3,true);
ArraySetAsSeries(B4,true);
ArraySetAsSeries(B5,true);
ArraySetAsSeries(B6,true);
ArraySetAsSeries(B7,true);
ArraySetAsSeries(B8,true);
ArraySetAsSeries(B9,true);
ArraySetAsSeries(B10,true);
ArraySetAsSeries(B11,true);
ArraySetAsSeries(B12,true);
ArraySetAsSeries(B13,true);
ArraySetAsSeries(B14,true);
ArraySetAsSeries(B15,true);
ArraySetAsSeries(B16,true);
ArraySetAsSeries(B17,true);
ArraySetAsSeries(B18,true);
ArraySetAsSeries(B19,true);
ArraySetAsSeries(B20,true);
ArraySetAsSeries(B21,true);
ArraySetAsSeries(B22,true);
ArraySetAsSeries(B23,true);
ArraySetAsSeries(B24,true);
ArraySetAsSeries(B25,true);
ArraySetAsSeries(B26,true);
ArraySetAsSeries(B27,true);
ArraySetAsSeries(B28,true);
ArraySetAsSeries(B29,true);
SetIndexBuffer(0,B0,INDICATOR_CALCULATIONS);
SetIndexBuffer(1,B1,INDICATOR_CALCULATIONS);
SetIndexBuffer(2,B2,INDICATOR_CALCULATIONS);
SetIndexBuffer(3,B3,INDICATOR_CALCULATIONS);
SetIndexBuffer(4,B4,INDICATOR_CALCULATIONS);
SetIndexBuffer(5,B5,INDICATOR_CALCULATIONS);
SetIndexBuffer(6,B6,INDICATOR_CALCULATIONS);
SetIndexBuffer(7,B7,INDICATOR_CALCULATIONS);
SetIndexBuffer(8,B8,INDICATOR_CALCULATIONS);
SetIndexBuffer(9,B9,INDICATOR_CALCULATIONS);
SetIndexBuffer(10,B10,INDICATOR_CALCULATIONS);
SetIndexBuffer(11,B11,INDICATOR_CALCULATIONS);
SetIndexBuffer(12,B12,INDICATOR_CALCULATIONS);
SetIndexBuffer(13,B13,INDICATOR_CALCULATIONS);
SetIndexBuffer(14,B14,INDICATOR_CALCULATIONS);
SetIndexBuffer(15,B15,INDICATOR_CALCULATIONS);
SetIndexBuffer(16,B16,INDICATOR_CALCULATIONS);
SetIndexBuffer(17,B17,INDICATOR_CALCULATIONS);
SetIndexBuffer(18,B18,INDICATOR_CALCULATIONS);
SetIndexBuffer(19,B19,INDICATOR_CALCULATIONS);
SetIndexBuffer(20,B20,INDICATOR_CALCULATIONS);
SetIndexBuffer(21,B21,INDICATOR_CALCULATIONS);
SetIndexBuffer(22,B22,INDICATOR_CALCULATIONS);
SetIndexBuffer(23,B23,INDICATOR_CALCULATIONS);
SetIndexBuffer(24,B24,INDICATOR_CALCULATIONS);
SetIndexBuffer(25,B25,INDICATOR_CALCULATIONS);
SetIndexBuffer(26,B26,INDICATOR_CALCULATIONS);
SetIndexBuffer(27,B27,INDICATOR_CALCULATIONS);
SetIndexBuffer(28,B28,INDICATOR_CALCULATIONS);
SetIndexBuffer(29,B29,INDICATOR_CALCULATIONS);
//---
IndicatorSetString(INDICATOR_SHORTNAME,ShortName);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Print("INDI DEINIT ",reason);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SetStyle(int bufferIdx, string linelabel, color colorstyle, int linetype, int linestyle, int linewidth)
{
PlotIndexSetString(bufferIdx,PLOT_LABEL,linelabel);
PlotIndexSetInteger(bufferIdx,PLOT_LINE_COLOR,0,colorstyle);
PlotIndexSetInteger(bufferIdx,PLOT_DRAW_TYPE,linetype);
PlotIndexSetInteger(bufferIdx,PLOT_LINE_STYLE,linestyle);
PlotIndexSetInteger(bufferIdx,PLOT_LINE_WIDTH,linewidth);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// While a new candle is forming, set the current value to be empty
if(rates_total>prev_calculated && setFormingCandleBlank)
{
B0[0] = EMPTY_VALUE;
B1[0] = EMPTY_VALUE;
B2[0] = EMPTY_VALUE;
B3[0] = EMPTY_VALUE;
B4[0] = EMPTY_VALUE;
B5[0] = EMPTY_VALUE;
B6[0] = EMPTY_VALUE;
B7[0] = EMPTY_VALUE;
B8[0] = EMPTY_VALUE;
B9[0] = EMPTY_VALUE;
B10[0] = EMPTY_VALUE;
B11[0] = EMPTY_VALUE;
B12[0] = EMPTY_VALUE;
B13[0] = EMPTY_VALUE;
B14[0] = EMPTY_VALUE;
B15[0] = EMPTY_VALUE;
B16[0] = EMPTY_VALUE;
B17[0] = EMPTY_VALUE;
B18[0] = EMPTY_VALUE;
B19[0] = EMPTY_VALUE;
B20[0] = EMPTY_VALUE;
B21[0] = EMPTY_VALUE;
B22[0] = EMPTY_VALUE;
B23[0] = EMPTY_VALUE;
B24[0] = EMPTY_VALUE;
B25[0] = EMPTY_VALUE;
B26[0] = EMPTY_VALUE;
B27[0] = EMPTY_VALUE;
B28[0] = EMPTY_VALUE;
B29[0] = EMPTY_VALUE;
}
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SubscriptionHandler(ZmqMsg &chartMsg)
{
CJAVal message;
// Get data from request
string msg=chartMsg.getData();
if(debug)
Print("Processing:"+msg);
// Deserialize msg to CJAVal array
if(!message.Deserialize(msg))
{
Alert("Deserialization Error");
ExpertRemove();
}
if(message["chartIndicatorId"]==IndicatorId)
{
if(message["action"]=="PLOT" && message["actionType"]=="DATA")
{
int bufferIdx = message["indicatorBufferId"].ToInt();
if(bufferIdx == 0)
{
WriteToBuffer(message, B0);
SetIndexBuffer(0,B0,INDICATOR_DATA);
}
if(bufferIdx == 1)
{
WriteToBuffer(message, B1);
SetIndexBuffer(1,B1,INDICATOR_DATA);
}
if(bufferIdx == 2)
{
WriteToBuffer(message, B2);
SetIndexBuffer(2,B2,INDICATOR_DATA);
}
if(bufferIdx == 3)
{
WriteToBuffer(message, B3);
SetIndexBuffer(3,B3,INDICATOR_DATA);
}
if(bufferIdx == 4)
{
WriteToBuffer(message, B4);
SetIndexBuffer(4,B4,INDICATOR_DATA);
}
if(bufferIdx == 5)
{
WriteToBuffer(message, B5);
SetIndexBuffer(5,B5,INDICATOR_DATA);
}
if(bufferIdx == 6)
{
WriteToBuffer(message, B6);
SetIndexBuffer(6,B6,INDICATOR_DATA);
}
if(bufferIdx == 7)
{
WriteToBuffer(message, B7);
SetIndexBuffer(7,B7,INDICATOR_DATA);
}
if(bufferIdx == 8)
{
WriteToBuffer(message, B8);
SetIndexBuffer(8,B8,INDICATOR_DATA);
}
if(bufferIdx == 9)
{
WriteToBuffer(message, B9);
SetIndexBuffer(9,B9,INDICATOR_DATA);
}
if(bufferIdx == 10)
{
WriteToBuffer(message, B10);
SetIndexBuffer(10,B10,INDICATOR_DATA);
}
if(bufferIdx == 11)
{
WriteToBuffer(message, B11);
SetIndexBuffer(11,B11,INDICATOR_DATA);
}
if(bufferIdx == 12)
{
WriteToBuffer(message, B12);
SetIndexBuffer(12,B12,INDICATOR_DATA);
}
if(bufferIdx == 13)
{
WriteToBuffer(message, B13);
SetIndexBuffer(13,B13,INDICATOR_DATA);
}
if(bufferIdx == 14)
{
WriteToBuffer(message, B14);
SetIndexBuffer(14,B14,INDICATOR_DATA);
}
if(bufferIdx == 15)
{
WriteToBuffer(message, B15);
SetIndexBuffer(15,B15,INDICATOR_DATA);
}
if(bufferIdx == 16)
{
WriteToBuffer(message, B16);
SetIndexBuffer(16,B16,INDICATOR_DATA);
}
if(bufferIdx == 17)
{
WriteToBuffer(message, B17);
SetIndexBuffer(17,B17,INDICATOR_DATA);
}
if(bufferIdx == 18)
{
WriteToBuffer(message, B18);
SetIndexBuffer(18,B18,INDICATOR_DATA);
}
if(bufferIdx == 19)
{
WriteToBuffer(message, B19);
SetIndexBuffer(19,B19,INDICATOR_DATA);
}
if(bufferIdx == 20)
{
WriteToBuffer(message, B20);
SetIndexBuffer(20,B20,INDICATOR_DATA);
}
if(bufferIdx == 21)
{
WriteToBuffer(message, B21);
SetIndexBuffer(21,B21,INDICATOR_DATA);
}
if(bufferIdx == 22)
{
WriteToBuffer(message, B22);
SetIndexBuffer(22,B22,INDICATOR_DATA);
}
if(bufferIdx == 23)
{
WriteToBuffer(message, B23);
SetIndexBuffer(23,B23,INDICATOR_DATA);
}
if(bufferIdx == 24)
{
WriteToBuffer(message, B24);
SetIndexBuffer(24,B24,INDICATOR_DATA);
}
if(bufferIdx == 25)
{
WriteToBuffer(message, B25);
SetIndexBuffer(25,B25,INDICATOR_DATA);
}
if(bufferIdx == 26)
{
WriteToBuffer(message, B26);
SetIndexBuffer(26,B26,INDICATOR_DATA);
}
if(bufferIdx == 27)
{
WriteToBuffer(message, B27);
SetIndexBuffer(27,B27,INDICATOR_DATA);
}
if(bufferIdx == 28)
{
WriteToBuffer(message, B28);
SetIndexBuffer(28,B28,INDICATOR_DATA);
}
if(bufferIdx == 29)
{
WriteToBuffer(message, B29);
SetIndexBuffer(29,B29,INDICATOR_DATA);
}
ChartRedraw(mtChartId);
}
else
if(message["action"]=="PLOT" && message["actionType"]=="ADDBUFFER")
{
string linelabel = message["style"]["linelabel"].ToStr();
string colorstyleStr = message["style"]["color"].ToStr();
string linetypeStr = message["style"]["linetype"].ToStr();
string linestyleStr = message["style"]["linestyle"].ToStr();
int linewidth = message["style"]["linewidth"].ToInt();
setFormingCandleBlank = message["style"]["blankforming"].ToBool();
color colorstyle = StringToColor(colorstyleStr);
int linetype = StringToEnumInt(linetypeStr);
int linestyle = StringToEnumInt(linestyleStr);
SetStyle(activeBufferCount, linelabel, colorstyle, linetype, linestyle, linewidth);
activeBufferCount = activeBufferCount + 1;
ClearBuffer(activeBufferCount-1);
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Clear(double &buffer[])
{
int bufferSize = ArraySize(buffer);
for(int i=0; i<bufferSize; i++)
{
buffer[i] = EMPTY_VALUE;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ClearBuffer(int bufferIdx)
{
switch(bufferIdx)
{
case 0:
{
Clear(B0);
}
case 1:
{
Clear(B1);
}
case 2:
{
Clear(B2);
}
case 3:
{
Clear(B3);
}
case 4:
{
Clear(B4);
}
case 5:
{
Clear(B5);
}
case 6:
{
Clear(B6);
}
case 7:
{
Clear(B7);
}
case 8:
{
Clear(B8);
}
case 9:
{
Clear(B9);
}
case 10:
{
Clear(B10);
}
case 11:
{
Clear(B11);
}
case 12:
{
Clear(B12);
}
case 13:
{
Clear(B13);
}
case 14:
{
Clear(B14);
}
case 15:
{
Clear(B15);
}
case 16:
{
Clear(B16);
}
case 17:
{
Clear(B17);
}
case 18:
{
Clear(B18);
}
case 19:
{
Clear(B19);
}
case 20:
{
Clear(B20);
}
case 21:
{
Clear(B21);
}
case 22:
{
Clear(B22);
}
case 23:
{
Clear(B23);
}
case 24:
{
Clear(B24);
}
case 25:
{
Clear(B25);
}
case 26:
{
Clear(B26);
}
case 27:
{
Clear(B27);
}
case 28:
{
Clear(B28);
}
case 29:
{
Clear(B29);
}
break;
default:
{} break;
}
}
//+------------------------------------------------------------------+
//| Update indicator buffer function |
//+------------------------------------------------------------------+
void WriteToBuffer(CJAVal &message, double &buffer[])
{
int bufferSize = ArraySize(buffer);
int messageDataSize = message["data"].Size();
// calculate the buffer offset
MqlRates r[];
mtChartId =(datetime)message["mtChartId"].ToInt();
datetime fromDate=(datetime)message["fromDate"].ToInt();
datetime toDate=TimeCurrent();
ENUM_TIMEFRAMES period = ChartPeriod(mtChartId);
string symbol = ChartSymbol(mtChartId);
int rateCount;
rateCount = CopyRates(symbol, period, fromDate, toDate, r);
int offset = rateCount - 1;
// write to buffer
for(int i=0; i<messageDataSize; i++)
{
// don't add more elements than the automatically sized buffer array can
if(i+offset<bufferSize)
{
double val = message["data"][i].ToDbl();
if(val >= EMPTY_VALUE)
val = EMPTY_VALUE;
buffer[i+offset] = val;
}
}
}
//+------------------------------------------------------------------+
//| Check for new indicator data function |
//+------------------------------------------------------------------+
void CheckMessages()
{
// This is a workaround for Timer(). It is needed, because OnTimer() works if the indicator is manually added to a chart, but not with ChartIndicatorAdd()
ZmqMsg chartMsg;
// Recieve chart instructions stream from client via live Chart socket.
chartSubscriptionSocket.recv(chartMsg,true);
// Request recieved
if(chartMsg.size()>0)
{
// Handle subscription SubscriptionHandler()
SubscriptionHandler(chartMsg);
ChartRedraw(ChartID());
}
}
//+------------------------------------------------------------------+
//| OnTimer() workaround function |
//+------------------------------------------------------------------+
// Gets triggered by the OnTimer() function of the JsonAPI Expert script
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id==CHARTEVENT_CUSTOM+222)
CheckMessages();
}
//+----------------------------------------------------
+618 -376
View File
@@ -1,376 +1,618 @@
# Metaquotes MQL5 - JSON - API # Metaquotes MQL5 - JSON - API
### Development state: first stable release ### Development state: stable release version 2.0
Tested on macOS Mojave / Windows 10 in Parallels Desktop container. Tested on macOS Mojave / Windows 10 in Parallels Desktop container.
Working in production on Debian 10 / Wine 4. Tested on Manjaro Linux / Windows 10 in VirtualBox
An issue was found because of REP/REQ socket. Architecture changes are in development. Working in production on Debian 10 / Wine 4.
## Table of Contents ## Table of Contents
* [About the Project](#about-the-project)
* [Installation](#installation) - [About the Project](#about-the-project)
* [Documentation](#documentation) - [Installation](#installation)
* [Usage](#usage) - [Documentation](#documentation)
* [Live data and streaming events](#live-data-and-streaming-events) - [Usage](#usage)
* [Error handling](#error-handling) - [Live data and streaming events](#live-data-and-streaming-events)
* [License](#license) - [Streaming MT5 indicator data](#streaming-mt5-indicator-data)
- [Plot values to MT5 charts](#plot-values-to-mt5-charts)
## About the Project - [The JsonAPIIndicator](#the-jsonapiindicator)
- [Error handling](#error-handling)
This project was developed to work as a server for the Backtrader Python trading framework. It is based on ZeroMQ sockets and uses JSON format to communicate. But now it has grown to the independent project. You can use it with any programming language that has [ZeroMQ binding](http://zeromq.org/bindings:_start). - [License](#license)
## About the Project
Backtrader Python client is located here: [Python Backtrader - Metaquotes MQL5 ](https://github.com/khramkov/Backtrader-MQL5-API)
This project was developed to work as a server for the Backtrader Python trading framework. It is based on ZeroMQ sockets and uses JSON format to communicate. But now it has grown to the independent project. You can use it with any programming language that has [ZeroMQ binding](http://zeromq.org/bindings:_start).
In development:
* Devitation Backtrader Python client is located here: [Python Backtrader - Metaquotes MQL5 ](https://github.com/khramkov/Backtrader-MQL5-API)
* Stop limit orders
Thanks to the participation of [Gunther Schulz](https://github.com/Gunther-Schulz), the project moved to a new level.
## Installation
New features:
1. Install ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq)
2. Put `include/Json.mqh` from this repo to your MetaEditor `include` directoty. - Support for multiple datastreams in parallel for any combination of symbols and timeframes independently of the timeframe and symbol of the attached chart
3. Download and compile `experts/JsonAPI.mq5` script. - Support for tick data
4. Check if Metatrader 5 automatic trading is allowed. - Support for direct download as CSV files
5. Attach the script to a chart in Metatrader 5. - Automatic retry binding to sockets. When running under Wine in Linux, sockets will be blocked for 60 seconds if closed uncleanly. This can happen if the client is still connected while the EA gets reloaded.
6. Allow DLL import in dialog window. - Skip re-initialization on chart timeframe change
7. Check if the ports are free to use. (default:`15555`,`15556`, `15557`,`15558`) - Support for spread data (ask/bid)
- Support for plotting to charts in MT5 by streaming values from the client
## Documentation - Support for processing client data with MT5 indicators
The script uses four ZeroMQ sockets: In development:
1. `System socket` - recives requests from client and replies 'OK' - Devitation
2. `Data socket` - pushes data to client depending on the request via System socket. - Stop limit orders
3. `Live socket` - automatically pushes last candle when it closes. - Drawing of chart objects
4. `Streaming socket` - automatically pushes last transaction info every time it happens.
## Installation
The idea is to send requests via `System socket` and recieve results/errors via `Data socket`. Event handlers should be created for `Live socket` and `Streaming socket` because the server sends data to theese sockets automatically. See examples in [Live data and streaming events](#live-data-and-streaming-events) section.
1. Install ZeroMQ for MQL5 [https://github.com/dingmaotu/mql-zmq](https://github.com/dingmaotu/mql-zmq)
`System socket` request uses default JSON dictionary: 2. Put the following files from this repo to your MetaEditor Iinclude` directory
- `Include/Json.mqh`
``` - `Include/controlerrors.mqh`
{ - `Include/StringToEnumInt.mqh`
"action": null, 3. Put the `Indicators/JsonAPIIndicator.mq5` file from this repo to your MetaEditor `Indicators` directory
"actionType": null, 4. Download and compile `experts/JsonAPI.mq5` script.
"symbol": null, 5. Check if Metatrader 5 automatic trading is allowed.
"chartTF": null, 6. Attach the `JsonAPI.mq5` script to a chart in Metatrader 5.
"fromDate": null, 7. Allow DLL import in dialog window.
"toDate": null, 8. Check if the ports are free to use. (default:`15555`,`15556`, `15557`,`15558`, `15559`, `15560`,`15562`)
"id": null,
"magic": null, ## Documentation
"volume": null,
"price": null, The script uses seven ZeroMQ sockets:
"stoploss": null,
"takeprofit": null, 1. `System socket` - Recives requests from client and replies 'OK'.
"expiration": null, 2. `Data socket` - Pushes data to client depending on the request via System socket.
"deviation": null, 3. `Live socket` - Automatically pushes last candle when it closes.
"comment": null 4. `Streaming socket` - Automatically pushes last transaction info every time it happens.
} 5. `Indicator data socket` - automatically pushes indicator result values to the client.
``` 6. `Chart Data Socket` - Recieves values to be plotted to a specific chart.
Check out the available combinations of `action` and `actionType`: 7. `Chart Indicator Socket` - Only for internal communication. Passes values to be plotted TO the supplied JsonAPIIndicator indicator
action | actionType | Description | The idea is to send requests via `System socket` and recieve results/errors via `Data socket`. Event handlers should be created for `Live socket` and `Streaming socket` because the server sends data to theese sockets automatically. See examples in [Live data and streaming events](#live-data-and-streaming-events) section.
-----------|----------------------|----------------------------|
CONFIG | null | Set script configuration | `System socket` request uses default JSON dictionary:
ACCOUNT | null | Get account settings |
BALANCE | null | Get current balance | ```
POSITIONS | null | Get current open positions | {
ORDERS | null | Get current open orders | "action": null,
HISTORY | DATA | Get data history | "actionType": null,
HISTORY | TRADES | Get trades history | "symbol": null,
TRADE | ORDER_TYPE_BUY | Buy market | "chartTF": null,
TRADE | ORDER_TYPE_SELL | Sell market | "fromDate": null,
TRADE | ORDER_TYPE_BUY_LIMIT | Buy limit | "toDate": null,
TRADE | ORDER_TYPE_SELL_LIMIT| Sell limit | "id": null,
TRADE | ORDER_TYPE_BUY_STOP | Buy stop | "magic": null,
TRADE | ORDER_TYPE_SELL_STOP | Sell stop | "volume": null,
TRADE | POSITION_MODIFY | Position modify | "price": null,
TRADE | POSITION_PARTIAL | Position close partial | "stoploss": null,
TRADE | POSITION_CLOSE_ID | Position close by id | "takeprofit": null,
TRADE | POSITION_CLOSE_SYMBOL| Positions close by symbol | "expiration": null,
TRADE | ORDER_MODIFY | Order modify | "deviation": null,
TRADE | ORDER_CANCEL | Order cancel | "comment": null,
"chartId": None,
Python 3 API class example: "indicatorChartId": None,
"chartIndicatorSubWindow": None,
``` python "style": None,
import zmq }
```
class MTraderAPI:
def __init__(self, host=None): Check out the available combinations of `action` and `actionType`:
self.HOST = host or 'localhost'
self.SYS_PORT = 15555 # REP/REQ port | action | actionType | Description |
self.DATA_PORT = 15556 # PUSH/PULL port | --------- | --------------------- | --------------------------------- |
self.LIVE_PORT = 15557 # PUSH/PULL port | CONFIG | null | Set script configuration |
self.EVENTS_PORT = 15558 # PUSH/PULL port | RESET | null | Reset subscribed symbols |
| ACCOUNT | null | Get account settings |
# ZeroMQ timeout in seconds | BALANCE | null | Get current balance |
sys_timeout = 1 | POSITIONS | null | Get current open positions |
data_timeout = 10 | ORDERS | null | Get current open orders |
| INDICATOR | ATTACH | Attach an indicator and return ID |
# initialise ZMQ context | INDICATOR | REQUEST | Get indicator data |
context = zmq.Context() | CHART | OPEN | Open a new chart window |
| CHART | ADDINDICATOR | Attach JsonAPIIndicator indicator |
# connect to server sockets | HISTORY | DATA | Get data history |
try: | HISTORY | TRADES | Get trades history |
self.sys_socket = context.socket(zmq.REQ) | HISTORY | WRITE | Download history data as CSV |
# set port timeout | TRADE | ORDER_TYPE_BUY | Buy market |
self.sys_socket.RCVTIMEO = sys_timeout * 1000 | TRADE | ORDER_TYPE_SELL | Sell market |
self.sys_socket.connect('tcp://{}:{}'.format(self.HOST, self.SYS_PORT)) | TRADE | ORDER_TYPE_BUY_LIMIT | Buy limit |
| TRADE | ORDER_TYPE_SELL_LIMIT | Sell limit |
self.data_socket = context.socket(zmq.PULL) | TRADE | ORDER_TYPE_BUY_STOP | Buy stop |
# set port timeout | TRADE | ORDER_TYPE_SELL_STOP | Sell stop |
self.data_socket.RCVTIMEO = data_timeout * 1000 | TRADE | POSITION_MODIFY | Position modify |
self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_PORT)) | TRADE | POSITION_PARTIAL | Position close partial |
except zmq.ZMQError: | TRADE | POSITION_CLOSE_ID | Position close by id |
raise zmq.ZMQBindError("Binding ports ERROR") | TRADE | POSITION_CLOSE_SYMBOL | Positions close by symbol |
| TRADE | ORDER_MODIFY | Order modify |
def _send_request(self, data: dict) -> None: | TRADE | ORDER_CANCEL | Order cancel |
"""Send request to server via ZeroMQ System socket"""
try: Python 3 API class example:
self.sys_socket.send_json(data)
msg = self.sys_socket.recv_string() ```python
# terminal received the request import zmq
assert msg == 'OK', 'Something wrong on server side'
except AssertionError as err: class MTraderAPI:
raise zmq.NotDone(err) def __init__(self, host=None):
except zmq.ZMQError: self.HOST = host or 'localhost'
raise zmq.NotDone("Sending request ERROR") self.SYS_PORT = 15555 # REP/REQ port
self.DATA_PORT = 15556 # PUSH/PULL port
def _pull_reply(self): self.LIVE_PORT = 15557 # PUSH/PULL port
"""Get reply from server via Data socket with timeout""" self.EVENTS_PORT = 15558 # PUSH/PULL port
try: self.INDICATOR_DATA_PORT = 15559 # REP/REQ port
msg = self.data_socket.recv_json() self.CHART_DATA_PORT = 15560 # PUSH port
except zmq.ZMQError:
raise zmq.NotDone('Data socket timeout ERROR') # ZeroMQ timeout in seconds
return msg sys_timeout = 1
data_timeout = 10
def live_socket(self, context=None):
"""Connect to socket in a ZMQ context""" # initialise ZMQ context
try: context = zmq.Context()
context = context or zmq.Context.instance()
socket = context.socket(zmq.PULL) # connect to server sockets
socket.connect('tcp://{}:{}'.format(self.HOST, self.LIVE_PORT)) try:
except zmq.ZMQError: self.sys_socket = context.socket(zmq.REQ)
raise zmq.ZMQBindError("Live port connection ERROR") # set port timeout
return socket self.sys_socket.RCVTIMEO = sys_timeout * 1000
self.sys_socket.connect('tcp://{}:{}'.format(self.HOST, self.SYS_PORT))
def streaming_socket(self, context=None):
"""Connect to socket in a ZMQ context""" self.data_socket = context.socket(zmq.PULL)
try: # set port timeout
context = context or zmq.Context.instance() self.data_socket.RCVTIMEO = data_timeout * 1000
socket = context.socket(zmq.PULL) self.data_socket.connect('tcp://{}:{}'.format(self.HOST, self.DATA_PORT))
socket.connect('tcp://{}:{}'.format(self.HOST, self.EVENTS_PORT))
except zmq.ZMQError: self.indicator_data_socket = context.socket(zmq.PULL)
raise zmq.ZMQBindError("Data port connection ERROR") # set port timeout
return socket self.indicator_data_socket.RCVTIMEO = data_timeout * 1000
self.indicator_data_socket.connect(
def construct_and_send(self, **kwargs) -> dict: "tcp://{}:{}".format(self.HOST, self.INDICATOR_DATA_PORT)
"""Construct a request dictionary from default and send it to server""" )
self.chart_data_socket = context.socket(zmq.PUSH)
# default dictionary # set port timeout
request = { # TODO check if port is listening and error handling
"action": None, self.chart_data_socket.connect(
"actionType": None, "tcp://{}:{}".format(self.HOST, self.CHART_DATA_PORT)
"symbol": None, )
"chartTF": None,
"fromDate": None, except zmq.ZMQError:
"toDate": None, raise zmq.ZMQBindError("Binding ports ERROR")
"id": None,
"magic": None, def _send_request(self, data: dict) -> None:
"volume": None, """Send request to server via ZeroMQ System socket"""
"price": None, try:
"stoploss": None, self.sys_socket.send_json(data)
"takeprofit": None, msg = self.sys_socket.recv_string()
"expiration": None, # terminal received the request
"deviation": None, assert msg == 'OK', 'Something wrong on server side'
"comment": None except AssertionError as err:
} raise zmq.NotDone(err)
except zmq.ZMQError:
# update dict values if exist raise zmq.NotDone("Sending request ERROR")
for key, value in kwargs.items():
if key in request: def _pull_reply(self):
request[key] = value """Get reply from server via Data socket with timeout"""
else: try:
raise KeyError('Unknown key in **kwargs ERROR') msg = self.data_socket.recv_json()
except zmq.ZMQError:
# send dict to server raise zmq.NotDone('Data socket timeout ERROR')
self._send_request(request) return msg
# return server reply def _indicator_pull_reply(self):
return self._pull_reply() """Get reply from server via Data socket with timeout"""
``` try:
## Usage msg = self.indicator_data_socket.recv_json()
All examples will be on Python 3. Lets create an instance of MetaTrader API class: except zmq.ZMQError:
raise zmq.NotDone("Indicator Data socket timeout ERROR")
``` python if self.debug:
api = MTraderAPI() print("ZMQ INDICATOR DATA REPLY: ", msg)
``` return msg
First of all we should configure the script `symbol` and `timeframe`. Live data stream will be configured to the seme params. def live_socket(self, context=None):
"""Connect to socket in a ZMQ context"""
``` python try:
rep = api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="M5") context = context or zmq.Context.instance()
print(rep) socket = context.socket(zmq.PULL)
``` socket.connect('tcp://{}:{}'.format(self.HOST, self.LIVE_PORT))
except zmq.ZMQError:
Get information about the trading account. raise zmq.ZMQBindError("Live port connection ERROR")
return socket
``` python
rep = api.construct_and_send(action="ACCOUNT") def streaming_socket(self, context=None):
print(rep) """Connect to socket in a ZMQ context"""
``` try:
context = context or zmq.Context.instance()
Get historical data. `fromDate` should be in timestamp format. The data will be loaded to the last candle if `toDate` is `None`. Notice, that the script sends the last unclosed candle too. You should delete it manually. socket = context.socket(zmq.PULL)
socket.connect('tcp://{}:{}'.format(self.HOST, self.EVENTS_PORT))
There are some issues: except zmq.ZMQError:
raise zmq.ZMQBindError("Data port connection ERROR")
- MetaTrader keeps historical data in cache. But when you make a request for the first time, MetaTrader downloads the data from a broker. This operation can exceed `Data socket` timeout. It depends on your broker. Second request will be handeled quickly. return socket
- It takes 6-7 seconds to process `50000` M1 candles. It was tested on Windows 10 in Parallels Desktop container with 4 cores and 4GB RAM. So if you need more data there are three ways to handle it. 1) Increase `Data socket` timeout. 2) You can load data partially using `fromDate` and `toDate`. 3) You can use more powerfull hardware.
def _push_chart_data(self, data: dict) -> None:
``` python """Send message for chart control to server via ZeroMQ chart data socket"""
rep = api.construct_and_send(action="HISTORY", actionType="DATA", symbol="EURUSD", chartTF="M5", fromDate=1555555555) try:
print(rep) if self.debug:
``` print("ZMQ PUSH CHART DATA: ", data, " -> ", data)
self.chart_data_socket.send_json(data)
History data reply example: except zmq.ZMQError:
raise zmq.NotDone("Sending request ERROR")
```
{'data': [[1560782340, 1.12271, 1.12288, 1.12269, 1.12277, 46.0],[1560782400, 1.12278, 1.12299, 1.12276, 1.12297, 43.0],[1560782460, 1.12296, 1.12302, 1.12293, 1.123, 23.0]]} def construct_and_send(self, **kwargs) -> dict:
``` """Construct a request dictionary from default and send it to server"""
Buy market order. # default dictionary
request = {
``` python "action": None,
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_BUY", symbol="EURUSD", "volume"=0.1, "stoploss"=1.1, "takeprofit"=1.3) "actionType": None,
print(rep) "symbol": None,
``` "chartTF": None,
"fromDate": None,
Sell limit order. Remember to switch SL/TP depending on BUY/SELL, or you will get `invalid stops` error. "toDate": None,
"id": None,
- BUY: SL < price < TP "magic": None,
- SELL: SL > price > TP "volume": None,
"price": None,
``` python "stoploss": None,
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_SELL_LIMIT", symbol="EURUSD", "volume"=0.1, "price"=1.2, "stoploss"=1.3, "takeprofit"=1.1) "takeprofit": None,
print(rep) "expiration": None,
``` "deviation": None,
All pending orders are set to `Good till cancel` by default. If you want to set an expiration date, pass the date in timestamp format to `expiration` param. "comment": None,
"chartId": None,
``` python "indicatorChartId": None,
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_SELL_LIMIT", symbol="EURUSD", "volume"=0.1, "price"=1.2, "expiration"=1560782460) "chartIndicatorSubWindow": None,
print(rep) "style": None,
``` }
## Live data and streaming events
# update dict values if exist
Event handler example for `Live socket` and `Data socket`. for key, value in kwargs.items():
if key in request:
``` python request[key] = value
import zmq else:
import threading raise KeyError('Unknown key in **kwargs ERROR')
api = MTraderAPI() # send dict to server
self._send_request(request)
def _t_livedata(): # return server reply
socket = api.live_socket() return self._pull_reply()
while True:
try: def indicator_construct_and_send(self, **kwargs) -> dict:
last_candle = socket.recv_json() """Construct a request dictionary from default and send it to server"""
except zmq.ZMQError:
raise zmq.NotDone("Live data ERROR") # default dictionary
print(last_candle) request = {
"action": None,
"actionType": None,
def _t_streaming_events(): "id": None,
socket = api.streaming_socket() "symbol": None,
while True: "chartTF": None,
try: "fromDate": None,
trans = socket.recv_json() "toDate": None,
request, reply = trans.values() "name": None,
except zmq.ZMQError: "params": None,
raise zmq.NotDone("Streaming data ERROR") "linecount": None,
print(request) }
print(reply)
# update dict values if exist
for key, value in kwargs.items():
if key in request:
t = threading.Thread(target=_t_livedata, daemon=True) request[key] = value
t.start() else:
raise KeyError("Unknown key in **kwargs ERROR")
t = threading.Thread(target=_t_streaming_events, daemon=True)
t.start() # send dict to server
self._send_request(request)
while True:
pass # return server reply
``` return self._indicator_pull_reply()
def chart_data_construct_and_send(self, **kwargs) -> dict:
There are only two variants of `Live socket` data. When everything is ok, the script sends data on candle close: """Construct a request dictionary from default and send it to server"""
``` # default dictionary
{"status":"CONNECTED","data":[1560780120,1.12186,1.12194,1.12186,1.12191,15.00000]} message = {
``` "action": None,
"actionType": None,
If the terminal has lost connection to the market: "chartId": None,
"indicatorChartId": None,
``` "data": None,
{"status":"DISCONNECTED"} }
```
# update dict values if exist
When the terminal reconnects to the market, it sends the last closed candle again. So you should update your historical data. Make the `action="HISTORY"` request with `fromDate` equal to your last candle timestamp before disconnect. for key, value in kwargs.items():
if key in message:
`OnTradeTransaction` function is called when a trade transaction event occurs. `Streaming socket` sends `TRADE_TRANSACTION_REQUEST` data every time it happens. Try to create and modify orders in the MQL5 terminal manually and check the expert logging tab for better understanding. Also see [MQL5 docs](https://www.mql5.com/en/docs/event_handlers/ontradetransaction). message[key] = value
else:
`TRADE_TRANSACTION_REQUEST` request data: raise KeyError("Unknown key in **kwargs ERROR")
``` # send dict to server
{ self._push_chart_data(message)
'action': 'TRADE_ACTION_DEAL', ```
'order': 501700843,
'symbol': 'EURUSD', ## Usage
'volume': 0.1,
'price': 1.12181, All examples will be on Python 3. Lets create an instance of MetaTrader API class:
'stoplimit': 0.0,
'sl': 1.1, ```python
'tp': 1.13, api = MTraderAPI()
'deviation': 10, ```
'type': 'ORDER_TYPE_BUY',
'type_filling': 'ORDER_FILLING_FOK', First of all we should configure the script `symbol` and `timeframe`. Live data stream will be configured to the same params. You can use any number of `symbols` and `timeframes`. The server subscribes to these sembols and will transmit them through the `Live data` socket
'type_time': 'ORDER_TIME_GTC',
'expiration': 0, ```python
'comment': None, print(api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="M5"))
'position': 0, print(api.construct_and_send(action="CONFIG", symbol="AUDUSD", chartTF="M1"))
'position_by': 0 ...
} ```
```
`TRADE_TRANSACTION_REQUEST` result data: There is also `tick` data. You can subscribe for `tick` and `candle` data at the same `symbol`.
``` ```python
{ print(api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="TICK"))
'retcode': 10009, print(api.construct_and_send(action="CONFIG", symbol="EURUSD", chartTF="M1"))
'result': 'TRADE_RETCODE_DONE', ```
'deal': 501700843,
'order': 501700843, If you want to stop `Live data`, you should reset server subscriptions.
'volume': 0.1,
'price': 1.12181, ```python
'comment': None, rep = api.construct_and_send(action="RESET")
'request_id': 8, print(rep)
'retcode_external': 0 ```
}
``` Get information about the trading account.
## Error handling ```python
First of all, when you send a command via `System socket`, you should always receive back `"OK"` message via `System socket`. It means that your command was received and deserialized. rep = api.construct_and_send(action="ACCOUNT")
print(rep)
All data that come through `Data socket` have an `error` param. This param will have `true` key if somethng goes wrong. Also, there will be `description` and `function` params. They will hold information about error and the name of the function with error. ```
This information also applies to the trade commannds. See [MQL5 docs](https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes) for possible server answers. Get historical data. `fromDate` should be in timestamp format. The data will be loaded to the last candle if `toDate` is `None`. Notice, that the script sends the last unclosed candle too. You should delete it manually.
## License There are some issues:
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.
- MetaTrader keeps historical data in cache. But when you make a request for the first time, MetaTrader downloads the data from a broker. This operation can exceed `Data socket` timeout. It depends on your broker. Second request will be handeled quickly.
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 `LICENSE` for more information. - It takes 6-7 seconds to process `50000` M1 candles. It was tested on Windows 10 in Parallels Desktop container with 4 cores and 4GB RAM. So if you need more data there are three ways to handle it. 1) Increase `Data socket` timeout. 2) You can load data partially using `fromDate` and `toDate`. 3) You can use more powerfull hardware.
```python
rep = api.construct_and_send(action="HISTORY", actionType="DATA", symbol="EURUSD", chartTF="M5", fromDate=1555555555)
print(rep)
```
History data reply example:
```
{'data': [[1560782340, 1.12271, 1.12288, 1.12269, 1.12277, 46.0],[1560782400, 1.12278, 1.12299, 1.12276, 1.12297, 43.0],[1560782460, 1.12296, 1.12302, 1.12293, 1.123, 23.0]]}
```
Buy market order.
```python
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_BUY", symbol="EURUSD", "volume"=0.1, "stoploss"=1.1, "takeprofit"=1.3)
print(rep)
```
Sell limit order. Remember to switch SL/TP depending on BUY/SELL, or you will get `invalid stops` error.
- BUY: SL < price < TP
- SELL: SL > price > TP
```python
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_SELL_LIMIT", symbol="EURUSD", "volume"=0.1, "price"=1.2, "stoploss"=1.3, "takeprofit"=1.1)
print(rep)
```
All pending orders are set to `Good till cancel` by default. If you want to set an expiration date, pass the date in timestamp format to `expiration` param.
```python
rep = api.construct_and_send(action="TRADE", actionType="ORDER_TYPE_SELL_LIMIT", symbol="EURUSD", "volume"=0.1, "price"=1.2, "expiration"=1560782460)
print(rep)
```
## Live data and streaming events
Event handler example for `Live socket` and `Data socket`.
```python
import zmq
import threading
api = MTraderAPI()
def _t_livedata():
socket = api.live_socket()
while True:
try:
last_candle = socket.recv_json()
except zmq.ZMQError:
raise zmq.NotDone("Live data ERROR")
print(last_candle)
def _t_streaming_events():
socket = api.streaming_socket()
while True:
try:
trans = socket.recv_json()
request, reply = trans.values()
except zmq.ZMQError:
raise zmq.NotDone("Streaming data ERROR")
print(request)
print(reply)
t = threading.Thread(target=_t_livedata, daemon=True)
t.start()
t = threading.Thread(target=_t_streaming_events, daemon=True)
t.start()
while True:
pass
```
There are only two variants of `Live socket` data. When everything is ok, the script sends subscribed data on new even. You can divide streams by symbol and timeframe names:
```
{"status":"CONNECTED","symbol":"EURUSD","timeframe":"TICK","data":[1581611172734,1.08515,1.08521]}
{"status":"CONNECTED","symbol":"EURUSD","timeframe":"M1","data":[1581611100,1.08525,1.08525,1.08520,1.08520,10.00000]}
```
If the terminal has lost connection to the market:
```
{"status":"DISCONNECTED"}
```
When the terminal reconnects to the market, it sends the last closed candle again. So you should update your historical data. Make the `action="HISTORY"` request with `fromDate` equal to your last candle timestamp before disconnect.
`OnTradeTransaction` function is called when a trade transaction event occurs. `Streaming socket` sends `TRADE_TRANSACTION_REQUEST` data every time it happens. Try to create and modify orders in the MQL5 terminal manually and check the expert logging tab for better understanding. Also see [MQL5 docs](https://www.mql5.com/en/docs/event_handlers/ontradetransaction).
`TRADE_TRANSACTION_REQUEST` request data:
```
{
'action': 'TRADE_ACTION_DEAL',
'order': 501700843,
'symbol': 'EURUSD',
'volume': 0.1,
'price': 1.12181,
'stoplimit': 0.0,
'sl': 1.1,
'tp': 1.13,
'deviation': 10,
'type': 'ORDER_TYPE_BUY',
'type_filling': 'ORDER_FILLING_FOK',
'type_time': 'ORDER_TIME_GTC',
'expiration': 0,
'comment': None,
'position': 0,
'position_by': 0
}
```
`TRADE_TRANSACTION_REQUEST` result data:
```
{
'retcode': 10009,
'result': 'TRADE_RETCODE_DONE',
'deal': 501700843,
'order': 501700843,
'volume': 0.1,
'price': 1.12181,
'comment': None,
'request_id': 8,
'retcode_external': 0
}
```
## Streaming MT5 indicator data
Open a chart window and attach a MT5 indicator.
Parameters:
- `id` - a unique id string.
- `symbol` - chart symbol to open and atatch the indicator to.
- `chartTF` - timeframe to set the chart at.
- `name` - the name of the MT5 indicator to attach.
- `params` - the initialisation paramaters that the specified indicator expects.
- `linecount` - the number of buffers the indicator returns. In the example below MACD is used and it return the values for "macd" and "signal".
```python
print(api.indicator_construct_and_send(action='INDICATOR', actionType='ATTACH', id='4df306ea-e8e6-439b-8004-b86ba4bcc8c3', symbol='EURUSD', chartTF='M1', name='Examples/MACD', 'params'=['12', '26', '9', 'PRICE_CLOSE'], 'linecount'=2))
```
Stream the calculated result values of a previously attached indicator.
Parameters:
- `id` - id string of a previously attached indicator.
- `fromDate` - timestamp for which a result value is requested.
```python
print(api.indicator_construct_and_send(action='INDICATOR', actionType='REQUEST', id='4df306ea-e8e6-439b-8004-b86ba4bcc8c3', 'fromDate'=1591993860))
```
Example of the result:
```python
{'error': False, 'id': '4df306ea-e8e6-439b-8004-b86ba4bcc8c3', 'data': ['0.00008204', '0.00001132']}
```
The data field holds a list with results of the calculated indicator buffers.
## Plot values to MT5 charts
Open a new chart window to plot values to.
Parameters:
- `chartId` - a unique id string to reference the new chart window.
- `fromDate` - timestamp for which a result value is requested.
- `symbol` - chart symbol to open and atatch the indicator to.
- `chartTF` - timeframe to set the chart at.
```python
print(api.construct_and_send(action='CHART', actionType='OPEN', symbol='EURUSD', chartTF='M1', chartId='cbb82988-3193-4dda-9cea-c27faaf7835b'))
```
A common scenario would be to stream vlaues calculated by the client indictor to be plotted in MT5. This is done by attaching the supplied MT5 indicator `JsonAPIIndicator` and passing values to be plotted to it.
Initialize a plot line object by attaching a new instance of `JsonAPIIndicator`, ready to recieve values to be plotted.
Parameters:
- `chartId` - id string of a previously opened chart.
- `indicatorChartId`: a unique id string to reference the new plot line object.
- `chartIndicatorSubWindow`: chart sub window to plot to (https://www.mql5.c.om/en/docs/chart_operations/chartindicatoradd)
- `style`: style settings for the plot. `shortname` and `linelabel` can be any string value. `linewidth` expects an int. All other paramters require constants supported by MQL5.
Supported are the following style paramers (with the corresponding MQL5 constants in braces): `color` (PLOT_LINE_COLOR), `linetype` (PLOT_DRAW_TYPE), `linestyle` (PLOT_LINE_STYLE).
```python
print(api.construct_and_send(action='CHART', actionType='ADDINDICATOR', chartId='cbb82988-3193-4dda-9cea-c27faaf7835b', indicatorChartId='5f2c1ab5-6b36-498f-96ac-3982a4a3551a', chartIndicatorSubWindow=1, style={shortname='BT-BollingerBands', linelabel='Middle', color='clrYellow', linetype='DRAW_LINE', linestyle='STYLE_SOLID', linewidth=1))
```
Stream values to a plot line object (draw a line).
Parameters:
- `chartId` - id string of a previously opened chart.
- `indicatorChartId`: id string of a previously initialized plot line object.
- `data`: list of values to plot. The last value in a list (`values[-1]`) corresponds to the most recent candle. If the size of the list of values passsed is >= 1, and the number of historic candles to plot is `n` then `values[n-1]` is the most recent candle and `values[0]` is the oldest candle.
```python
# Plot line with historic data
values=[1.1225948211353751, 1.1226243406054506, 1.1226266123404378]
print(api.chart_data_construct_and_send(action='PLOT', chartId='cbb82988-3193-4dda-9cea-c27faaf7835b', indicatorChartId='5f2c1ab5-6b36-498f-96ac-3982a4a3551a', chartIndicatorSubWindow=1, data=values))
n=len(values)
print(f'The value for the oldest candle: {values[0]} - The value for the most recent candle: {values[n-1]}')
# Extend the plotted line with the most recent values as new candles are created
print(api.chart_data_construct_and_send(action='PLOT', chartId='cbb82988-3193-4dda-9cea-c27faaf7835b', indicatorChartId='5f2c1ab5-6b36-498f-96ac-3982a4a3551a', chartIndicatorSubWindow=1, data=[1.122618120966847]))
print(api.chart_data_construct_and_send(action='PLOT', chartId='cbb82988-3193-4dda-9cea-c27faaf7835b', indicatorChartId='5f2c1ab5-6b36-498f-96ac-3982a4a3551a', chartIndicatorSubWindow=1, data=[1.1226254106923093]))
```
## The JsonAPIIndicator
The supplied indicator `JsonAPIIndicator` does not do any calculations by itself. It simply plots
incoming data to a chart which can be passed by via JSON interface to the `Chart Data Socket`. The indicator is controlled by the expert script `JsonAPI.mq5` locally via port `15562`.
## Error handling
First of all, when you send a command via `System socket`, you should always receive back `"OK"` message via `System socket`. It means that your command was received and deserialized.
All data that come through `Data socket` have an `error` param. This param will have `true` key if somethng goes wrong. Also, there will be `description` and `function` params. They will hold information about error and the name of the function with error.
This information also applies to the trade commannds. See [MQL5 docs](https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes) for possible server answers.
## License
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 `LICENSE` for more information.
+22
View File
@@ -0,0 +1,22 @@
### Marth 6th
- fixed tick data retrival. When requesting tick data, every other tick was skipped
- refactored code
### 30th April 2020
- add support for spreads
- add support for plotting custom indicator data to charts
- add support for streaming MT5 indicator data
- new error reporting
### 16th February 2020
- add support for candle ask/bid price spread
### 11th January 2020
- add support for multiple datastreams in parallel for any combination of symbols and timeframes independently of the timeframe and symbol of the attached chart
- add support for tick data
- add support for direct download as CSV files
- add one automatic retry binding to sockets. When running under Wine in Linux, sockets will be blocked for 60 seconds if closed uncleanly. This can happen if the client is still connected while the EA gets reloaded.
- skip re-initialization on chart timeframe change
Executable
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# https://www.commandlinefu.com/commands/view/11560/delete-all-non-printing-characters-from-a-file
# http://www.skybert.net/bash/adding-utf-8-bom-from-the-command-line/
# Remove non-printable ASCII characters
tr -cd '[:print:]\n\r' < ./Experts/JsonAPI.mq5 > ./Experts/JsonAPI.clean.mq5
# Add UTF-8 BOM
sed -i '1s/^/\xef\xbb\xbf/' ./Experts/JsonAPI.clean.mq5
mv ./Experts/JsonAPI.clean.mq5 ./Experts/JsonAPI.mq5
# Remove non-printable ASCII characters
tr -cd '[:print:]\n\r' < ./Indicators/JsonAPIIndicator.mq5 > ./Indicators/JsonAPIIndicator.clean.mq5
# Add UTF-8 BOM
sed -i '1s/^/\xef\xbb\xbf/' ./Indicators/JsonAPIIndicator.clean.mq5
mv ./Indicators/JsonAPIIndicator.clean.mq5 ./Indicators/JsonAPIIndicator.mq5
# Remove non-printable ASCII characters
tr -cd '[:print:]\n\r' < ./Include/StringToEnumInt.mqh > ./Include/StringToEnumInt.clean.mqh
# Add UTF-8 BOM
sed -i '1s/^/\xef\xbb\xbf/' ./Include/StringToEnumInt.clean.mqh
mv ./Include/StringToEnumInt.clean.mqh ./Include/StringToEnumInt.mqh
# Remove non-printable ASCII characters
tr -cd '[:print:]\n\r' < ./Include/controlerrors.mqh > ./Include/controlerrors.clean.mqh
# Add UTF-8 BOM
sed -i '1s/^/\xef\xbb\xbf/' ./Include/controlerrors.clean.mqh
mv ./Include/controlerrors.clean.mqh ./Include/controlerrors.mqh
@@ -1,73 +0,0 @@
//+------------------------------------------------------------------+
//| OnTick(string symbol).mq5 |
//| Copyright 2010, Lizar |
//| https://login.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION "1.00 Build 1 (01 Fab 2011)"
#property copyright "Copyright 2010, Lizar"
#property link "https://login.mql5.com/ru/users/Lizar"
#property version VERSION
#property description "Template of the Expert Advisor"
#property description "with multicurrency OnTick(string symbol) event handler"
//+------------------------------------------------------------------+
//| MULTICURRENCY MODE SETTINGS |
//| of OnTick(string symbol) event handler |
//| |
//| 1.1 List of symbols needed to proceed in the events: |
#define SYMBOLS_TRADING "EURUSD","GBPUSD","USDJPY","USDCHF"
//| 1.2 If you want all symbols from Market Watch, use this: |
//#define SYMBOLS_TRADING "MARKET_WATCH"
//| Note: Select only one way from 1.1 or 1.2. |
//| |
//| 2. Event type for OnTick(string symbol): |
#define CHART_EVENT_SYMBOL CHARTEVENT_TICK
//| Note: the event type must corresponds to the |
//| ENUM_CHART_EVENT_SYMBOL enumeration. |
//| |
//| 3. Include file: |
#include <OnTick(string symbol).mqh>
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Expert initialization function |
//| This function must be declared, even if it empty. |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Add your code here...
return(0);
}
//+------------------------------------------------------------------+
//| Expert multi tick function |
//| Use this function instead of the standard OnTick() function |
//+------------------------------------------------------------------+
void OnTick(string symbol)
{
//--- Add your code here...
Print("New event on symbol: ",symbol);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//| This function must be declared, even if it empty. |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, // event id
const long& lparam, // event param of long type
const double& dparam, // event param of double type
const string& sparam) // event param of string type
{
//--- Add your code here...
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Add your code here...
}
//+------------------------------ end -------------------------------+
@@ -1,214 +0,0 @@
//+------------------------------------------------------------------+
//| OnTick(string symbol).mqh |
//| Copyright 2010, Lizar |
//| https://login.mql5.com/ru/users/Lizar |
//| Revision 2011.01.30 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Lizar"
#property link "https://login.mql5.com/ru/users/Lizar"
//+------------------------------------------------------------------+
//| The events enumeration is implemented as flags |
//| the events can be combined using the OR ("|") logical operation |
//+------------------------------------------------------------------+
enum ENUM_CHART_EVENT_SYMBOL
{
CHARTEVENT_NO =0, // Events disabled
CHARTEVENT_INIT =0, // "Initialization" event
CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
CHARTEVENT_TICK =0x00200000, // "New tick" event
CHARTEVENT_ALL =0xFFFFFFFF, // All events enabled
};
//---
string _symbol_[]={SYMBOLS_TRADING};
int _handle_[];
int _symbols_total_ = 0; // total symbols
int _symbols_market_ = 0; // number of symbols in Market Watch
bool _market_watch_ = false; // use symbols from Market Watch
bool _testing_ = false; // In testing mode
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- check if we work in Strategy Tester:
_testing_=((bool)MQL5InfoInteger(MQL5_TESTING) ||
(bool)MQL5InfoInteger(MQL5_OPTIMIZATION) ||
(bool)MQL5InfoInteger(MQL5_VISUAL_MODE));
//--- Check do we need to use the symbols from Market Watch
if(_symbol_[0]=="MARKET_WATCH") _market_watch_=true;
//--- check settings
if(_testing_ && _market_watch_)
{
Print("Error: Please specify list of symbols in SYMBOLS_TRADING for use in Strategy Tester ");
return(1);
}
//--- Initialization of variables and arrays:
_symbols_total_=SymbolsTotal(false); // total symbols
ArrayResize(_handle_,_symbols_total_); // resize array for handles of "spys"
ArrayInitialize(_handle_,INVALID_HANDLE); // initalizae array for handles of "spys"
//--- Load "spys":
if(_market_watch_) SynchronizationTradingTools();
else
{
_symbols_total_=ArraySize(_symbol_);
for(int i=0;i<_symbols_total_;i++)
if(!LoadAgent(i, _symbol_[i])) return(1);
}
//--- Execute OnInit function of Expert Advisor
_OnInit();
return(0);
}
#define OnInit _OnInit // rendefine of OnInit function
//+------------------------------------------------------------------+
//| Expert tick function |
//| Used only in Strategy Tester |
//+------------------------------------------------------------------+
void OnTick()
{
if(_testing_)
{
for(int i=0;i<_symbols_total_;i++)
{
string __symbol__=_symbol_[i];
if(MathAbs(GlobalVariableGet(__symbol__+"_flag")-2)<0.1)
{
GlobalVariableSet(__symbol__+"_flag",1);
OnTick(__symbol__);
}
}
}
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Call of OnTick(string symbol) or OnChartEvent event handler:
if(id==CHARTEVENT_CUSTOM_LAST)
{
OnTick(sparam);
//--- synchronize "agents" with Market Watch if necessary:
if(_market_watch_) SynchronizationTradingTools();
}
else _OnChartEvent(id,lparam,dparam,sparam);
}
#define OnChartEvent _OnChartEvent // rendefine of OnChartEvent function
//+------------------------------------------------------------------+
//| Function for synchronization of "spys" with symbols |
//| from "Market Watch" |
//| INPUT: no. |
//| OUTPUT: no. |
//| REMARK: no. |
//+------------------------------------------------------------------+
void SynchronizationTradingTools()
{
if(_symbols_market_!=SymbolsTotal(true))
{
_symbols_market_=0;
for(int i=0;i<_symbols_total_;i++)
{
long __symbol_select__;
string __symbol__=SymbolName(i,false);
if(!SymbolInfoInteger(__symbol__,SYMBOL_SELECT,__symbol_select__)) return;
if(_handle_[i]==INVALID_HANDLE && __symbol_select__)
{
if(!LoadAgent(i,__symbol__)) return;
}
else if(_handle_[i]!=INVALID_HANDLE && !__symbol_select__)
{
if(!DeLoadAgent(i,__symbol__)) return;
}
}
_symbols_market_=SymbolsTotal(true);
}
}
//+------------------------------------------------------------------+
//| Function for loading of "spys" |
//| INPUT: __id__ - id, corresponds to the symbol index in |
//| the list of symbols |
//| __symbol__ - symbol name |
//| OUTPUT: true - if successful |
//| false - if error |
//| REMARK: no. |
//+------------------------------------------------------------------+
bool LoadAgent(int __id__, string __symbol__)
{
_handle_[__id__]=iCustom(__symbol__,_Period,"Spy Control panel MCM",ChartID(),65534,CHART_EVENT_SYMBOL);
if(_handle_[__id__]==INVALID_HANDLE)
{
Print("Error in setting of agent for ",__symbol__);
return(false);
}
Print("The agent for ",__symbol__," is set.");
return(true);
}
//+------------------------------------------------------------------+
//| Function for release of the "spys" |
//| INPUT: __id__ - id, corresponds to the symbol index in |
//| the list of symbols |
//| __symbol__ - symbol name |
//| OUTPUT: true - if successful |
//| false - if error |
//| REMARK: no. |
//+------------------------------------------------------------------+
bool DeLoadAgent(int __id__, string __symbol__)
{
if(_handle_[__id__]!=INVALID_HANDLE)
{
if(!IndicatorRelease(_handle_[__id__]))
{
Print("Error deletion of agent for ",__symbol__);
return(false);
}
Print("The agent for ",__symbol__," is deleted.");
_handle_[__id__]=INVALID_HANDLE;
}
return(true);
}
//+------------------------------ end -------------------------------+
@@ -1,160 +0,0 @@
//+------------------------------------------------------------------+
//| Spy Control panel MCM.mq5 |
//| Copyright 2010, Lizar |
//| https://login.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION "1.00 Build 4 (30 Jan 2011)"
#property copyright "Copyright 2010, Lizar"
#property link "https://login.mql5.com/ru/users/Lizar"
#property version VERSION
#property description "MCM Control panel agent."
#property description "It can be attached to the chart (any timeframe) of the symbol needed"
#property description "and generate the NewBar and/or NewTick custom events for the chart"
#property indicator_chart_window
//+------------------------------------------------------------------+
//| The events enumeration is implemented as flags |
//| the events can be combined using the OR ("|") logical operation |
//+------------------------------------------------------------------+
enum ENUM_CHART_EVENT_SYMBOL
{
CHARTEVENT_INIT =0, // "Initialization" event
CHARTEVENT_NO =0, // Events disabled
CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on 1-m chart
CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on 2-m chart
CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on 3-m chart
CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on 4-m chart
CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on 5-m chart
CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on 6-m chart
CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on 10-m chart
CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on 12-m chart
CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on 15-m chart
CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on 20-m chart
CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on 30-m chart
CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on hourly chart
CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
CHARTEVENT_TICK =0x00200000, // "New tick" event
CHARTEVENT_ALL =0xFFFFFFFF, // All events enabled
};
input long chart_id; // chart id
input ushort custom_event_id; // event id
input ENUM_CHART_EVENT_SYMBOL flag_event=CHARTEVENT_NO; // event flag.
MqlDateTime time, prev_time;
bool testing=false;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
testing=((bool)MQL5InfoInteger(MQL5_TESTING) ||
(bool)MQL5InfoInteger(MQL5_OPTIMIZATION) ||
(bool) MQL5InfoInteger(MQL5_VISUAL_MODE));
if(testing)
{
GlobalVariableTemp(_Symbol+"_flag");
GlobalVariableTemp(_Symbol+"_custom_id");
GlobalVariableTemp(_Symbol+"_event");
GlobalVariableTemp(_Symbol+"_price");
}
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total, // size of price[] array
const int prev_calculated, // bars calculated at previous call
const int begin, // begin
const double& price[] // array for calculation
)
{
double price_current=price[rates_total-1];
TimeCurrent(time);
if(prev_calculated==0)
{
EventCustom(CHARTEVENT_INIT,price_current);
prev_time=time;
return(rates_total);
}
//--- new tick
if((flag_event & CHARTEVENT_TICK)!=0) EventCustom(CHARTEVENT_TICK,price_current);
//--- check change time
if(time.min==prev_time.min &&
time.hour==prev_time.hour &&
time.day==prev_time.day &&
time.mon==prev_time.mon) return(rates_total);
//--- new minute
if((flag_event & CHARTEVENT_NEWBAR_M1)!=0) EventCustom(CHARTEVENT_NEWBAR_M1,price_current);
if(time.min%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_M2)!=0) EventCustom(CHARTEVENT_NEWBAR_M2,price_current);
if(time.min%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_M3)!=0) EventCustom(CHARTEVENT_NEWBAR_M3,price_current);
if(time.min%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_M4)!=0) EventCustom(CHARTEVENT_NEWBAR_M4,price_current);
if(time.min%5 ==0 && (flag_event & CHARTEVENT_NEWBAR_M5)!=0) EventCustom(CHARTEVENT_NEWBAR_M5,price_current);
if(time.min%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_M6)!=0) EventCustom(CHARTEVENT_NEWBAR_M6,price_current);
if(time.min%10==0 && (flag_event & CHARTEVENT_NEWBAR_M10)!=0) EventCustom(CHARTEVENT_NEWBAR_M10,price_current);
if(time.min%12==0 && (flag_event & CHARTEVENT_NEWBAR_M12)!=0) EventCustom(CHARTEVENT_NEWBAR_M12,price_current);
if(time.min%15==0 && (flag_event & CHARTEVENT_NEWBAR_M15)!=0) EventCustom(CHARTEVENT_NEWBAR_M15,price_current);
if(time.min%20==0 && (flag_event & CHARTEVENT_NEWBAR_M20)!=0) EventCustom(CHARTEVENT_NEWBAR_M20,price_current);
if(time.min%30==0 && (flag_event & CHARTEVENT_NEWBAR_M30)!=0) EventCustom(CHARTEVENT_NEWBAR_M30,price_current);
if(time.min!=0) {prev_time=time; return(rates_total);}
//--- new hour
if((flag_event & CHARTEVENT_NEWBAR_H1)!=0) EventCustom(CHARTEVENT_NEWBAR_H1,price_current);
if(time.hour%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_H2)!=0) EventCustom(CHARTEVENT_NEWBAR_H2,price_current);
if(time.hour%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_H3)!=0) EventCustom(CHARTEVENT_NEWBAR_H3,price_current);
if(time.hour%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_H4)!=0) EventCustom(CHARTEVENT_NEWBAR_H4,price_current);
if(time.hour%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_H6)!=0) EventCustom(CHARTEVENT_NEWBAR_H6,price_current);
if(time.hour%8 ==0 && (flag_event & CHARTEVENT_NEWBAR_H8)!=0) EventCustom(CHARTEVENT_NEWBAR_H8,price_current);
if(time.hour%12==0 && (flag_event & CHARTEVENT_NEWBAR_H12)!=0) EventCustom(CHARTEVENT_NEWBAR_H12,price_current);
if(time.hour!=0) {prev_time=time; return(rates_total);}
//--- new day
if((flag_event & CHARTEVENT_NEWBAR_D1)!=0) EventCustom(CHARTEVENT_NEWBAR_D1,price_current);
//--- new week
if(time.day_of_week==1 && (flag_event & CHARTEVENT_NEWBAR_W1)!=0) EventCustom(CHARTEVENT_NEWBAR_W1,price_current);
//--- new month
if(time.day==1 && (flag_event & CHARTEVENT_NEWBAR_MN1)!=0) EventCustom(CHARTEVENT_NEWBAR_MN1,price_current);
prev_time=time;
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
void EventCustom(ENUM_CHART_EVENT_SYMBOL event,double price)
{
if(!testing) EventChartCustom(chart_id,custom_event_id,(long)event,price,_Symbol);
else
{
if(GlobalVariableSet(_Symbol+"_custom_id",custom_event_id)==0) return;
if(GlobalVariableSet(_Symbol+"_event",event)==0) return;
if(GlobalVariableSet(_Symbol+"_price",price)==0) return;
GlobalVariableSet(_Symbol+"_flag",2);
}
return;
}