EA Script
EA Script
EA Script
#define TRADE_RETRY_COUNT 4
#define TRADE_RETRY_WAIT 100
#define OP_FLAT -1
#define OP_BUY ORDER_TYPE_BUY
#define OP_SELL ORDER_TYPE_SELL
datetime lastStatsUpdate = 0;
datetime barTime;
double pip;
double stopLevel;
bool isTrailingStop=true;
int indHandlers[1][12][2];
int maxRectangles = 0;
int maxLabels = 0;
int posStatCount = 0;
double posStatLots = 0;
struct NewsRecord
{
datetime time;
string priority;
string currency;
string title;
};
NewsRecord newsRecords[];
string newsCurrencies[];
datetime lastNewsUpdate = 0;
string loadNewsError = "";
bool isNewsFeedOk = true;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int OnInit(void)
{
barTime = Time(0);
stopLevel = (int) SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
pip = GetPipValue();
isTrailingStop = isTrailingStop && Stop_Loss > 0;
lastStatsUpdate = 0;
Comment("");
InitIndicators();
UpdatePosition();
ParseNewsCurrenciesText();
lastNewsUpdate = TimeCurrent();
if(!MQLInfoInteger(MQL_TESTER))
LoadNews();
DeleteObjects();
OnTick();
ChartRedraw(0);
return ValidateInit();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(Show_inds)
RemoveIndicators();
DeleteObjects();
if(accountProtectionMessage != "")
Comment(accountProtectionMessage);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void DeleteObjects(void)
{
if(ObjectFind(0, "Stats_background") == 0)
ObjectDelete(0, "Stats_background");
maxLabels = MathMax(maxLabels, 35);
for(int i = 0; i < maxLabels; i += 1)
{
const string objName = "label" + IntegerToString(i);
if(ObjectFind(0, objName) == 0)
ObjectDelete(0, objName);
}
maxRectangles = 0;
maxLabels = 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnTick(void)
{
if(!MQLInfoInteger(MQL_TESTER))
{
CheckAccountProtection();
const datetime time = TimeCurrent();
if(time > lastStatsUpdate + 3)
{
lastStatsUpdate = time;
if(Max_OpenPos || Max_OpenLots)
SetPosStats();
UpdateStats();
}
if(IsOutOfSession())
return;
if(posType != OP_FLAT)
{
ManageClose();
UpdatePosition();
}
if(PositionSelectByTicket(ticket) &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == Magic_Number)
{
posType = (int) PositionGetInteger(POSITION_TYPE);
posTicket = ticket;
posLots = NormalizeDouble(PositionGetDouble(POSITION_VOLUME), 2);
posProfit = NormalizeDouble(PositionGetDouble(POSITION_PROFIT), 2);
posStopLoss = NormalizeDouble(PositionGetDouble(POSITION_SL), _Digits);
posTakeProfit = NormalizeDouble(PositionGetDouble(POSITION_TP), _Digits);
posPriceOpen = NormalizeDouble(PositionGetDouble(POSITION_PRICE_OPEN),
_Digits);
posPriceCurr = NormalizeDouble(PositionGetDouble(POSITION_PRICE_CURRENT),
_Digits);
break;
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void InitIndicators(void)
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void RemoveIndicators(void)
{
long windowsCount = -1;
ChartGetInteger(0, CHART_WINDOWS_TOTAL, 0, windowsCount);
if(entryProtectionMessage != "")
{
entryProtectionMessage = TimeToString(TimeCurrent()) + " " +
"Entry order was canceled:\n" +
entryProtectionMessage;
return;
}
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lots;
request.type = command == OP_BUY ? ORDER_TYPE_BUY :
ORDER_TYPE_SELL;
request.price = command == OP_BUY ? Ask() : Bid();
request.type_filling = orderFillingType;
request.deviation = 10;
request.sl = stopLoss;
request.tp = takeProfit;
request.magic = Magic_Number;
request.position = ticket;
request.comment = IntegerToString(Magic_Number);
if(isOrderCheck)
{
ResetLastError();
isOrderSend = OrderSend(request, result);
}
Sleep(TRADE_RETRY_WAIT);
Print("Order Send retry no: " + IntegerToString(attempt + 2));
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ModifyPosition(double stopLoss, double takeProfit, ulong ticket)
{
for(int attempt = 0; attempt < TRADE_RETRY_COUNT; attempt++)
{
if(IsTradeContextFree())
{
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_SLTP;
request.symbol = _Symbol;
request.sl = stopLoss;
request.tp = takeProfit;
request.magic = Magic_Number;
request.position = ticket;
request.comment = IntegerToString(Magic_Number);
if(isOrderCheck)
{
ResetLastError();
isOrderSend = OrderSend(request, result);
}
if(check.retcode == TRADE_RETCODE_INVALID_FILL)
{
switch (orderFillingType)
{
case ORDER_FILLING_FOK:
Print("Filling mode changed to: ORDER_FILLING_IOC");
orderFillingType = ORDER_FILLING_IOC;
break;
case ORDER_FILLING_IOC:
Print("Filling mode changed to: ORDER_FILLING_RETURN");
orderFillingType = ORDER_FILLING_RETURN;
break;
case ORDER_FILLING_RETURN:
Print("Filling mode changed to: ORDER_FILLING_FOK");
orderFillingType = ORDER_FILLING_FOK;
break;
}
request.type_filling = orderFillingType;
return CheckOrder(request);
}
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetStopLossPrice(const int command)
{
if(Stop_Loss == 0) return 0;
if(posType == OP_BUY)
{
const double newStopLoss = High(1) - stopLossPoints;
if(posStopLoss <= newStopLoss - pip)
return newStopLoss < bid
? newStopLoss >= bid - stopLevelPoints
? bid - stopLevelPoints
: newStopLoss
: bid;
}
if(posType == OP_SELL)
{
const double newStopLoss = Low(1) + spread + stopLossPoints;
if(posStopLoss >= newStopLoss + pip)
return newStopLoss > ask
? newStopLoss <= ask + stopLevelPoints
? ask + stopLevelPoints
: newStopLoss
: ask;
}
return posStopLoss;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ManageTrailingStop(const double trailingStop)
{
if((posType == OP_BUY && MathAbs(trailingStop - Bid()) < _Point) ||
(posType == OP_SELL && MathAbs(trailingStop - Ask()) < _Point))
{
ClosePosition();
return;
}
while(true)
{
if(IsStopped())
return false;
if(IsTradeAllowed())
{
RefreshRates();
return true;
}
Sleep(TRADE_RETRY_WAIT);
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsOutOfSession(void)
{
const int dayOfWeek = DayOfWeek();
const int periodStart = int(Time(0) % 86400);
const int periodLength = PeriodSeconds(_Period);
const int periodFix = periodStart + (sessionCloseAtSessionClose ?
periodLength : 0);
const int friBarFix = periodStart + (sessionCloseAtFridayClose ||
sessionCloseAtSessionClose ?
periodLength : 0);
lastProfit += PositionGetDouble(POSITION_PROFIT) +
PositionGetDouble(POSITION_SWAP);
}
return lastProfit;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ActivateProtection(string message)
{
if(posType == OP_BUY || posType == OP_SELL)
ClosePosition();
DeleteObjects();
Sleep(20 * 1000);
ExpertRemove();
OnDeinit(0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SetPosStats(void)
{
posStatCount = 0;
posStatLots = 0;
posStatCount += 1;
posStatLots += PositionGetDouble(POSITION_VOLUME);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void UpdateStats(void)
{
string comment = StringFormat("\n%s\nMagic number %d\n", robotTagline,
Magic_Number);
if(entryProtectionMessage != "")
comment += "\n" + entryProtectionMessage;
if(Pos_Stat)
comment += GetPositionStats() + "\n";
if(Robot_Stats)
comment += GetRobotStats() + "\n";
if(Max_Spread || Max_OpenPos || Max_OpenLots || MaxDailyLoss || Min_Equity ||
Max_Equity)
comment += GetProtectionInfo();
if(News_Priority != NewsFilter_Disabled)
comment += GetNewsText() + "\n";
RenderStats(comment);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetProtectionInfo(void)
{
string protectionInfo = "\n ..:: Active Protections ::..\n";
if(Max_Spread)
protectionInfo += StringFormat("Max spread: %d, current: %d\n",
Max_Spread, (int)MathRound((Ask() - Bid()) /
_Point));
if(Max_OpenPos)
protectionInfo += StringFormat("Max open positions: %d, current: %d\n",
Max_OpenPos, posStatCount);
if(Max_OpenLots)
protectionInfo += StringFormat("Max open lots: %.2f, current: %.2f\n",
Max_OpenLots, posStatLots);
if(MaxDailyLoss)
protectionInfo += StringFormat("Max daily loss: %.2f, current: %.2f\n",
-MathAbs(MaxDailyLoss), GetLastDaysProfit(1, -
1));
if(Min_Equity)
protectionInfo += StringFormat("Min equity: %.2f, current: %.2f\n",
Min_Equity,
AccountInfoDouble(ACCOUNT_EQUITY));
if(Max_Equity)
protectionInfo += StringFormat("Max equity: %.2f, current: %.2f\n",
Max_Equity,
AccountInfoDouble(ACCOUNT_EQUITY));
return protectionInfo;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetPositionStats(void)
{
string positionStats = "\n ..:: Position Stats ::..\n";
if(posType == OP_FLAT)
return positionStats + "Position: no open position";
return positionStats +
StringFormat("Position: %s, Lots: %.2f, Profit %.2f\n",
(posType == OP_BUY) ? "Long" : "Short",
posLots, posProfit) +
StringFormat("Open price: %s, Current price: %s\n",
DoubleToString(posPriceOpen, _Digits),
DoubleToString(posPriceCurr, _Digits)) +
StringFormat("Stop Loss: %s, Take Profit: %s",
DoubleToString(posStopLoss, _Digits),
DoubleToString(posTakeProfit, _Digits));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetRobotStats(void)
{
return "\n ..:: Robot Stats ::..\n" +
"Today " + GetRobotStatsDays( 1) + "\n" +
"This Week " + GetRobotStatsDays( 7) + "\n" +
"This Month " + GetRobotStatsDays(30);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetRobotStatsDays(int days)
{
double grossProfit = 0;
double grossLoss = 0;
int histDealsCnt = 0;
double histDealsProfit = 0;
histDealsProfit += profit;
histDealsCnt += 1;
if(maxRectangles == 0)
RectLabelCreate(0, "Stats_background", 0, 0, 30, lineWidth,
linesCount * lineHeight, GetChartBackColor(0));
if(newsContent == "")
{
loadNewsError = StringFormat("Cannot load news. Last error code: %d",
GetLastError());
return;
}
ParseNewsContent(newsContent, error);
if(error != "")
loadNewsError = error;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ParseNewsContent(string newsContent, string &error)
{
string lines[];
const int linesLen = StringSplit(newsContent, '\n', lines);
if(linesLen == -1)
{
error = "Cannot parse the news feed";
return;
}
ArrayResize(newsRecords, linesLen);
if(fieldsLen != 4)
{
error = "Cannot parse the news feed records";
return;
}
NewsRecord record;
record.time = (datetime) StringToInteger(fields[0]);
record.priority = fields[1];
record.currency = fields[2];
record.title = fields[3];
newsRecords[i] = record;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetNewsContent(string &error)
{
const string url = "https://forexsb.com/updates/news-feed.txt";
ResetLastError();
isNewsFeedOk = false;
if(resError == ERR_FUNCTION_NOT_ALLOWED)
{
error = "News Filter cannot access the news server.\n" +
"Follow these steps to fix it:\n"
" - open the \"Tool\" -> \"Options\" panel\n" +
" - go to the \"Expert Advisors\" tab\n" +
" - enable the \"Allow WebRequest for the listed URL:\" option.\n" +
" - add \"https://forexsb.com\" in a field below.";
return "";
}
if(resError != ERR_SUCCESS)
{
error = StringFormat("News Filter connection error! Error code: %d",
resError);
return "";
}
if(resCode != 200)
{
error = StringFormat("Response code: %d", resCode);
return "";
}
isNewsFeedOk = true;
return CharArrayToString(resData, 0, ArraySize(resData), CP_UTF8);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string GetNewsText(void)
{
string newsText = "\n ..:: Upcoming News ::..\n";
if(loadNewsError != "") return newsText + loadNewsError;
return newsText;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool NewsIsAcceptedCurrency(const NewsRecord &newsRecord)
{
for(int i = 0; i < ArraySize(newsCurrencies); i += 1)
if(newsCurrencies[i] == newsRecord.currency)
return true;
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool NewsIsAcceptedPriority(const NewsRecord &newsRecord)
{
return (News_Priority == NewsFilter_HighAndMedium) ||
(News_Priority == NewsFilter_HighOnly && newsRecord.priority == "high");
}
//+------------------------------------------------------------------+
//| Gets the index of an active news or -1 |
//+------------------------------------------------------------------+
int NewsFilterActive()
{
if(News_Priority == NewsFilter_Disabled)
return -1;
return -1;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ParseNewsCurrenciesText()
{
string parts[], parsed[];
const int partsLen = StringSplit(News_Currencies, ',', parts);
ArrayResize(parsed, partsLen);
int len = 0;
for(int i = 0; i < partsLen; i += 1)
{
string part = parts[i];
StringReplace(part, " ", "");
if(StringLen(part) > 0)
{
parsed[i] = part;
len += 1;
}
}
ArrayResize(newsCurrencies, len);
for(int i = 0; i < len; i += 1)
newsCurrencies[i] = parsed[i];
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
ENUM_INIT_RETCODE ValidateInit(void)
{
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
/*STRATEGY MARKET Premium Data; EURUSD; H1 */
/*STRATEGY CODE {"properties":
{"entryLots":0.1,"tradeDirectionMode":0,"oppositeEntrySignal":0,"stopLoss":100,"tak
eProfit":100,"useStopLoss":true,"useTakeProfit":true,"isTrailingStop":true},"openFi
lters":[],"closeFilters":[]} */