EA Script

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 24

//

// EA Studio Expert Advisor


//
// Created with: Expert Advisor Studio
// Website: https://studio.eatradingacademy.com/
//
// Copyright 2024, Forex Software Ltd.
//
// Risk Disclosure
//
// Futures and forex trading contains substantial risk and is not for every
investor.
// An investor could potentially lose all or more than the initial investment.
// Risk capital is money that can be lost without jeopardizing ones’ financial
security or life style.
// Only risk capital should be used for trading and only those with sufficient risk
capital should consider trading.

#property copyright "Forex Software Ltd."


#property version "6.1"
#property strict

static input string _Properties_ = "------"; // --- Expert Properties ---


static input int Magic_Number = 1821985319; // Magic number
static input double Entry_Amount = 0.10; // Entry lots
input int Stop_Loss = 100; // Stop Loss (pips)
input int Take_Profit = 100; // Take Profit (pips)

// "Entry protections" prevents new entry if a protection is activated


static input string Entry_prot__ = "------"; // --- Entry Protections ---
static input int Max_Spread = 0; // Max spread (points)
static input int Max_OpenPos = 0; // Max open positions
static input double Max_OpenLots = 0; // Max open lots

// "Account protections" stops the expert if a protection is activated


static input string Account_prot = "------"; // --- Account Protections ---
static input int MaxDailyLoss = 0; // Maximum daily loss (currency)
static input int Min_Equity = 0; // Minimum equity (currency)
static input int Max_Equity = 0; // Maximum equity (currency)

static input string _NewsFilter_ = "------"; // --- News Filter ---


enum NewsFilterPriority
{
NewsFilter_Disabled, // News filter disabled
NewsFilter_HighOnly, // High news filter
NewsFilter_HighAndMedium // Medium and High news filter
};
static input NewsFilterPriority News_Priority = NewsFilter_Disabled; // News
priority
static input string News_Currencies = "USD,EUR,JPY,GBP,CAD,AUD,CHF,NZD"; // News
currencies
static input int News_BeforeMedium = 2; // Before Medium news (minutes)
static input int News_AfterMedium = 2; // After Medium news (minutes)
static input int News_BeforeHigh = 2; // Before High news (minutes)
static input int News_AfterHigh = 5; // After High news (minutes)
static input int News_ViewCount = 10; // News records to show
static input string _Settings___ = "------"; // --- Settings ---
static input bool Show_inds = false; // Show indicators

static input string __Stats_____ = "------"; // --- Stats ---


static input bool Pos_Stat = true; // Position stats
static input bool Robot_Stats = true; // Robot stats

#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

string robotTagline = "An Expert Advisor from Expert Advisor Studio";

// Session time is set in seconds from 00:00


int sessionSundayOpen = 0; // 00:00
int sessionSundayClose = 86400; // 24:00
int sessionMondayThursdayOpen = 0; // 00:00
int sessionMondayThursdayClose = 86400; // 24:00
int sessionFridayOpen = 0; // 00:00
int sessionFridayClose = 86400; // 24:00
bool sessionIgnoreSunday = false;
bool sessionCloseAtSessionClose = false;
bool sessionCloseAtFridayClose = false;

const double sigma = 0.000001;

int posType = OP_FLAT;


ulong posTicket = 0;
double posLots = 0;
double posStopLoss = 0;
double posTakeProfit = 0;
double posProfit = 0;
double posPriceOpen = 0;
double posPriceCurr = 0;

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;

string accountProtectionMessage = "";


string entryProtectionMessage = "";

struct NewsRecord
{
datetime time;
string priority;
string currency;
string title;
};
NewsRecord newsRecords[];
string newsCurrencies[];
datetime lastNewsUpdate = 0;
string loadNewsError = "";
bool isNewsFeedOk = true;

ENUM_ORDER_TYPE_FILLING orderFillingType = ORDER_FILLING_FOK;

//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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(time > lastNewsUpdate + 6*60*60 || !isNewsFeedOk)


{
lastNewsUpdate = time;
LoadNews();
}
}

const datetime time = Time(0);


if(time > barTime)
{
barTime = time;
OnBar();
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnBar(void)
{
UpdatePosition();

if(posType != OP_FLAT && IsForceSessionClose())


{
ClosePosition();
return;
}

if(IsOutOfSession())
return;

if(posType != OP_FLAT)
{
ManageClose();
UpdatePosition();
}

if(posType != OP_FLAT && isTrailingStop)


{
double trailingStop=GetTrailingStopPrice();
ManageTrailingStop(trailingStop);
UpdatePosition();
}

int entrySignal = GetEntrySignal();

if(posType == OP_FLAT && entrySignal != OP_FLAT)


{
OpenPosition(entrySignal);
UpdatePosition();
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void UpdatePosition(void)
{
posType = OP_FLAT;
posTicket = 0;
posLots = 0;
posProfit = 0;
posStopLoss = 0;
posTakeProfit = 0;
posPriceOpen = 0;
posPriceCurr = 0;

for(int posIndex = PositionsTotal() - 1; posIndex >= 0; posIndex -= 1)


{
const ulong ticket = PositionGetTicket(posIndex);

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);

for(int window = (int) windowsCount - 1; window >= 0; window -= 1)


{
const int indicatorsCount = ChartIndicatorsTotal(0, window);
for(int i = indicatorsCount - 1; i >= 0; i -= 1)
{
const string name = ChartIndicatorName(0, window, i);
ChartIndicatorDelete(0, window, name);
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int GetEntrySignal(void)
{
bool canOpenLong = false;
bool canOpenShort = false;

return canOpenLong && !canOpenShort ? OP_BUY


: canOpenShort && !canOpenLong ? OP_SELL
: OP_FLAT;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ManageClose(void)
{
if( (posType == OP_BUY ) ||
(posType == OP_SELL) )
ClosePosition();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OpenPosition(const int command)
{
entryProtectionMessage = "";
const int spread = (int)((Ask() - Bid()) / _Point);
if(Max_OpenPos > sigma && posStatCount > Max_OpenPos)
entryProtectionMessage += StringFormat("Protection: Max open positions: %d,
current: %d\n",
Max_OpenPos, posStatCount);
if(Max_OpenLots > sigma && posStatLots > Max_OpenLots)
entryProtectionMessage += StringFormat("Protection: Max open lots: %.2f,
current: %.2f\n",
Max_OpenLots, posStatLots);
if(Max_Spread > sigma && spread > Max_Spread)
entryProtectionMessage += StringFormat("Protection: Max spread: %d, current:
%d\n",
Max_Spread, spread);
const int newsIndex = NewsFilterActive();
if(newsIndex > -1)
{
const NewsRecord newsRecord = newsRecords[newsIndex];
const datetime timeShift = (datetime) MathRound((TimeLocal() - TimeGMT()) /
3600.0) * 3600;
const string priority = newsRecord.priority == "high" ? "[high]" :
"[med]";
entryProtectionMessage += StringFormat("News filter: %s %s %s %s\n",
priority,
TimeToString(newsRecord.time +
timeShift,
TIME_DATE | TIME_MINUTES),
newsRecord.currency,
newsRecord.title);
}

if(entryProtectionMessage != "")
{
entryProtectionMessage = TimeToString(TimeCurrent()) + " " +
"Entry order was canceled:\n" +
entryProtectionMessage;
return;
}

const double stopLoss = GetStopLossPrice(command);


const double takeProfit = GetTakeProfitPrice(command);
ManageOrderSend(command, Entry_Amount, stopLoss, takeProfit, 0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ClosePosition(void)
{
const int command = posType == OP_BUY ? OP_SELL : OP_BUY;
ManageOrderSend(command, posLots, 0, 0, posTicket);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ManageOrderSend(int command, double lots, 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_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);

bool isOrderCheck = CheckOrder(request);


bool isOrderSend = false;

if(isOrderCheck)
{
ResetLastError();
isOrderSend = OrderSend(request, result);
}

if(isOrderCheck && isOrderSend && result.retcode == TRADE_RETCODE_DONE)


return;
}

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);

bool isOrderCheck = CheckOrder(request);


bool isOrderSend = false;

if(isOrderCheck)
{
ResetLastError();
isOrderSend = OrderSend(request, result);
}

if(isOrderCheck && isOrderSend && result.retcode == TRADE_RETCODE_DONE)


return;
}
Sleep(TRADE_RETRY_WAIT);
Print("Order Send retry no: " + IntegerToString(attempt + 2));
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CheckOrder(MqlTradeRequest &request)
{
MqlTradeCheckResult check;
ZeroMemory(check);
ResetLastError();

if(OrderCheck(request, check)) return true;

Print("Error with OrderCheck: " + check.comment);

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;

const double delta = MathMax(pip * Stop_Loss, _Point * stopLevel);


const double stopLoss = command == OP_BUY ? Bid() - delta : Ask() + delta;

return NormalizeDouble(stopLoss, _Digits);


}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetTakeProfitPrice(const int command)
{
if(Take_Profit == 0) return 0;

const double delta = MathMax(pip * Take_Profit, _Point * stopLevel);


const double takeProfit = command == OP_BUY ? Bid() + delta : Ask() - delta;

return NormalizeDouble(takeProfit, _Digits);


}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetTrailingStopPrice(void)
{
const double bid = Bid();
const double ask = Ask();
const double spread = ask - bid;
const double stopLevelPoints = _Point * stopLevel;
const double stopLossPoints = pip * Stop_Loss;

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;
}

if(MathAbs(trailingStop - posStopLoss) > _Point)


{
posStopLoss = NormalizeDouble(trailingStop, _Digits);
ModifyPosition(posStopLoss, posTakeProfit, posTicket);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Bid(void)
{
return SymbolInfoDouble(_Symbol, SYMBOL_BID);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Ask(void)
{
return SymbolInfoDouble(_Symbol, SYMBOL_ASK);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
datetime Time(int bar)
{
datetime buffer[];
ArrayResize(buffer, 1);
return CopyTime(_Symbol, _Period, bar, 1, buffer) == 1 ? buffer[0] : 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Open(int bar)
{
double buffer[];
ArrayResize(buffer, 1);
return CopyOpen(_Symbol, _Period, bar, 1, buffer) == 1 ? buffer[0] : 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double High(int bar)
{
double buffer[];
ArrayResize(buffer, 1);
return CopyHigh(_Symbol, _Period, bar, 1, buffer) == 1 ? buffer[0] : 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Low(int bar)
{
double buffer[];
ArrayResize(buffer, 1);
return CopyLow(_Symbol, _Period, bar, 1, buffer) == 1 ? buffer[0] : 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double Close(int bar)
{
double buffer[];
ArrayResize(buffer, 1);
return CopyClose(_Symbol, _Period, bar, 1, buffer) == 1 ? buffer[0] : 0;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetPipValue(void)
{
return _Digits == 4 || _Digits == 5 ? 0.0001
: _Digits == 2 || _Digits == 3 ? 0.01
: _Digits == 1 ? 0.1 : 1;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsTradeAllowed(void)
{
return (bool) MQL5InfoInteger(MQL5_TRADE_ALLOWED);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void RefreshRates(void)
{
// Dummy function to make it compatible with MQL4
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int DayOfWeek(void)
{
MqlDateTime mqlTime;
TimeToStruct(Time(0), mqlTime);
return mqlTime.day_of_week;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsTradeContextFree(void)
{
if(IsTradeAllowed()) return true;

uint startWait = GetTickCount();


Print("Trade context is busy! Waiting...");

while(true)
{
if(IsStopped())
return false;

uint diff = GetTickCount() - startWait;


if(diff > 30 * 1000)
{
Print("The waiting limit exceeded!");
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);

return dayOfWeek == 0 && sessionIgnoreSunday ? true


: dayOfWeek == 0 ? periodStart < sessionSundayOpen ||
periodFix > sessionSundayClose
: dayOfWeek < 5 ? periodStart < sessionMondayThursdayOpen ||
periodFix > sessionMondayThursdayClose
: periodStart < sessionFridayOpen ||
friBarFix > sessionFridayClose;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsForceSessionClose(void)
{
if(!sessionCloseAtFridayClose && !sessionCloseAtSessionClose)
return false;

const int dayOfWeek = DayOfWeek();


const int periodEnd = int(Time(0) % 86400) + PeriodSeconds(_Period);

return dayOfWeek == 0 && sessionCloseAtSessionClose ? periodEnd >


sessionSundayClose
: dayOfWeek < 5 && sessionCloseAtSessionClose ? periodEnd >
sessionMondayThursdayClose
: dayOfWeek == 5 ? periodEnd > sessionFridayClose : false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CheckAccountProtection(void)
{
const double accountEquity = AccountInfoDouble(ACCOUNT_EQUITY);

if(Min_Equity > sigma && accountEquity < Min_Equity)


{
const string equityTxt = DoubleToString(accountEquity, 2);
const string message = "Minimum equity protection activated. Equity: " +
equityTxt;
ActivateProtection(message);
return;
}

if(Max_Equity > sigma && accountEquity >= Max_Equity)


{
const string equityTxt = DoubleToString(accountEquity, 2);
const string message = "Maximum equity protection activated. Equity: " +
equityTxt;
ActivateProtection(message);
return;
}

if(MaxDailyLoss > sigma)


{
const double dailyProfit = GetLastDaysProfit(1, -1);
if(dailyProfit < 0 && MathAbs(dailyProfit) >= MaxDailyLoss)
{
const string dailyProfitTxt = DoubleToString(MathAbs(dailyProfit), 2);
const string message = "Maximum daily loss protection activate! Daily
loss: " + dailyProfitTxt;
ActivateProtection(message);
return;
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetLastDaysProfit(int days, int magic)
{
double lastProfit = 0;

const datetime t0 = TimeCurrent();


const datetime t1 = t0 - 60 * 60 * 24 * days;
if(HistorySelect(t1, t0))
{
const int deals = HistoryDealsTotal();
for(int i = 0; i < deals; i += 1)
{
const ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0) continue;
if(magic >= 0 &&
(HistoryDealGetInteger(ticket, DEAL_MAGIC ) != Magic_Number ||
HistoryDealGetString (ticket, DEAL_SYMBOL) != _Symbol)) continue;
const long dealType = HistoryDealGetInteger(ticket, DEAL_TYPE);
if(dealType != DEAL_TYPE_BUY && dealType != DEAL_TYPE_SELL) continue;

lastProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT) +


HistoryDealGetDouble(ticket, DEAL_COMMISSION) +
HistoryDealGetDouble(ticket, DEAL_SWAP);
}
}

for(int i = PositionsTotal() - 1; i >= 0; i -= 1)


{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket)) continue;
if(magic >= 0 &&
(PositionGetInteger(POSITION_MAGIC ) != Magic_Number ||
PositionGetString (POSITION_SYMBOL) != _Symbol)) continue;

lastProfit += PositionGetDouble(POSITION_PROFIT) +
PositionGetDouble(POSITION_SWAP);
}

return lastProfit;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ActivateProtection(string message)
{
if(posType == OP_BUY || posType == OP_SELL)
ClosePosition();

DeleteObjects();

accountProtectionMessage = StringFormat("\n%s\nMagic number: %d\n",


robotTagline, Magic_Number);
accountProtectionMessage += message + "\n";
accountProtectionMessage += "Current position closed. ";
accountProtectionMessage += "Expert Advisor #" + IntegerToString(Magic_Number) +
" turned off.";
Comment(accountProtectionMessage);
Print(accountProtectionMessage);

Sleep(20 * 1000);
ExpertRemove();
OnDeinit(0);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SetPosStats(void)
{
posStatCount = 0;
posStatLots = 0;

for(int i = PositionsTotal() - 1; i >= 0; i -= 1)


{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket)) continue;

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;

const datetime timeCurrent = TimeCurrent();


const datetime timeStart = timeCurrent - days*PeriodSeconds(PERIOD_D1);
HistorySelect(timeStart, timeCurrent);
const int deals = HistoryDealsTotal();

for(int i = 0; i < deals; i += 1)


{
const ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0) continue;
const long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
if(dealMagic != Magic_Number && dealMagic != 0) continue;
if(HistoryDealGetString (ticket, DEAL_SYMBOL) != _Symbol) continue;
const long dealType = HistoryDealGetInteger(ticket, DEAL_TYPE);
if(dealType != DEAL_TYPE_BUY && dealType != DEAL_TYPE_SELL) continue;
const long dealEntry = HistoryDealGetInteger(ticket, DEAL_ENTRY);
if(dealEntry != DEAL_ENTRY_OUT) continue;

const double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT ) +


HistoryDealGetDouble(ticket, DEAL_SWAP ) +
HistoryDealGetDouble(ticket, DEAL_COMMISSION);

histDealsProfit += profit;
histDealsCnt += 1;

if(profit > sigma) grossProfit += profit;


if(profit < -sigma) grossLoss -= profit;
}

const double profitFactor = grossLoss > sigma ? grossProfit / grossLoss :


grossProfit;

return StringFormat("Trades: %d, Profit: %.2f, Profit factor: %.2f",


histDealsCnt, histDealsProfit, profitFactor);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void RenderStats(const string text)
{
string lines[];
const int linesCount = StringSplit(text, '\n', lines);

int lineWidth, lineHeight;


TextGetSize(robotTagline, lineWidth, lineHeight);

if(maxRectangles == 0)
RectLabelCreate(0, "Stats_background", 0, 0, 30, lineWidth,
linesCount * lineHeight, GetChartBackColor(0));

const color foreColor = GetChartForeColor(0);


for(int i = 0; i < linesCount; i += 1)
{
if(lines[i] == "")
lines[i] = " ";
string labelName = "label" + IntegerToString(i);
if(i < maxLabels)
LabelUpdate(0, labelName, lines[i]);
else
LabelCreate(0, labelName, 0, 10, 20 + i * lineHeight,
CORNER_LEFT_UPPER, lines[i], "Arial", 10, foreColor);

int lnWidth, lnHeight;


TextGetSize(lines[i], lnWidth, lnHeight);
if(lnWidth > lineWidth)
lineWidth = lnWidth;
}
ObjectSetInteger(0, "Stats_background", OBJPROP_XSIZE,
(int) MathRound(lineWidth * 0.90));
ObjectSetInteger(0, "Stats_background", OBJPROP_YSIZE,
linesCount * lineHeight);
for(int i = linesCount; i < maxLabels; i += 1)
LabelUpdate(0, "label" + IntegerToString(i), " ");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void RectLabelCreate(
const long chartId = 0, // chart's ID
const string name = "RectLabel", // label name
const int sub_window = 0, // subwindow index
const int x = 0, // X coordinate
const int y = 0, // Y coordinate
const int width = 50, // width
const int height = 18, // height
const color back_clr = clrBlack, // background color
const ENUM_BORDER_TYPE border = BORDER_SUNKEN, // border type
const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, // chart corner for
anchoring
const color clr = clrBlack, // flat border color
(Flat)
const ENUM_LINE_STYLE style = STYLE_SOLID, // flat border style
const int line_width = 0, // flat border width
const bool back = false, // in the background
const bool selection = false, // highlight to move
const bool hidden = true, // hidden in the object
list
const long z_order = 0) // priority for mouse
click
{
if(!ObjectCreate(chartId, name, OBJ_RECTANGLE_LABEL, sub_window, 0, 0)) return;
maxRectangles += 1;
ObjectSetInteger(chartId, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(chartId, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(chartId, name, OBJPROP_XSIZE, width);
ObjectSetInteger(chartId, name, OBJPROP_YSIZE, height);
ObjectSetInteger(chartId, name, OBJPROP_BGCOLOR, back_clr);
ObjectSetInteger(chartId, name, OBJPROP_BORDER_TYPE, border);
ObjectSetInteger(chartId, name, OBJPROP_CORNER, corner);
ObjectSetInteger(chartId, name, OBJPROP_COLOR, clr);
ObjectSetInteger(chartId, name, OBJPROP_STYLE, style);
ObjectSetInteger(chartId, name, OBJPROP_WIDTH, line_width);
ObjectSetInteger(chartId, name, OBJPROP_BACK, back);
ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, selection);
ObjectSetInteger(chartId, name, OBJPROP_SELECTED, selection);
ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, hidden);
ObjectSetInteger(chartId, name, OBJPROP_ZORDER, z_order);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void LabelCreate(
const long chartId=0, // chart's ID
const string name="Label", // label name
const int sub_window=0, // subwindow index
const int x=0, // X coordinate
const int y=0, // Y coordinate
const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring
const string text="Label", // text
const string font="Arial", // font
const int font_size=10, // font size
const color clr=clrYellow, // color
const double angle=0.0, // text slope
const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type
const bool back=false, // in the background
const bool selection=false, // highlight to move
const bool hidden=true, // hidden in the object list
const long z_order=0) // priority for mouse click
{
if(!ObjectCreate(chartId, name, OBJ_LABEL, sub_window, 0 , 0)) return;
maxLabels += 1;
ObjectSetInteger(chartId, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(chartId, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(chartId, name, OBJPROP_CORNER, corner);
ObjectSetString( chartId, name, OBJPROP_TEXT, text);
ObjectSetString( chartId, name, OBJPROP_FONT, font);
ObjectSetString( chartId, name, OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(chartId, name, OBJPROP_FONTSIZE, font_size);
ObjectSetDouble( chartId, name, OBJPROP_ANGLE, angle);
ObjectSetInteger(chartId, name, OBJPROP_ANCHOR, anchor);
ObjectSetInteger(chartId, name, OBJPROP_COLOR, clr);
ObjectSetInteger(chartId, name, OBJPROP_BACK, back);
ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, selection);
ObjectSetInteger(chartId, name, OBJPROP_SELECTED, selection);
ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, hidden);
ObjectSetInteger(chartId, name, OBJPROP_ZORDER, z_order);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void LabelUpdate(int chartId, string name, string text)
{
ObjectSetString(chartId, name, OBJPROP_TEXT, text);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
color GetChartForeColor(const long chartId=0)
{
long foreColor = clrWhite;
ChartGetInteger(chartId, CHART_COLOR_FOREGROUND, 0, foreColor);
return (color) foreColor;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
color GetChartBackColor(const long chartId=0)
{
long backColor = clrBlack;
ChartGetInteger(chartId, CHART_COLOR_BACKGROUND, 0, backColor);
return (color) backColor;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void LoadNews(void)
{
loadNewsError = "";
string error = "";
const string newsContent = GetNewsContent(error);
if(error != "")
{
loadNewsError = error;
return;
}

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);

for(int i = 0; i < linesLen; i += 1)


{
string fields[];
const int fieldsLen = StringSplit(lines[i], ';', fields);

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";

char reqBody[], resData[];


string headers;

ResetLastError();

const int resCode = WebRequest("GET", url, "", 10000, reqBody, resData,


headers);
const int resError = GetLastError();

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;

const datetime timeNow = TimeGMT();


const datetime timeShift = (datetime) MathRound((TimeLocal() - timeNow) /
3600.0) * 3600;
const int newsCount = ArraySize(newsRecords);

for(int i = 0, count = 0; i < newsCount && count < News_ViewCount; i += 1)


{
const NewsRecord newsRecord = newsRecords[i];

if(newsRecord.time < timeNow - News_AfterHigh * 60 ||


!NewsIsAcceptedCurrency(newsRecord) ||
!NewsIsAcceptedPriority(newsRecord))
continue;

const string newLine = count > 0 ? "\n" : "";


const string newsTime = TimeToString(newsRecord.time + timeShift, TIME_DATE |
TIME_MINUTES);
const string priority = newsRecord.priority == "high" ? "[high]" : "[med]";
const string text = StringFormat("%s%s %s %s %s", newLine, priority,
newsTime,
newsRecord.currency, newsRecord.title);
StringAdd(newsText, text);
count += 1;
}

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;

const datetime timeUtc = TimeGMT();


const int newsLen = ArraySize(newsRecords);
for(int i = 0; i < newsLen; i += 1)
{
const NewsRecord news = newsRecords[i];
if(!NewsIsAcceptedCurrency(news) || !NewsIsAcceptedPriority(news))
continue;

if(news.priority == "high" &&


news.time - News_BeforeHigh * 60 - 15 <= timeUtc &&
news.time + News_AfterHigh * 60 - 15 >= timeUtc)
return i;

if(news.priority == "medium" &&


news.time - News_BeforeMedium * 60 - 15 <= timeUtc &&
news.time + News_AfterMedium * 60 - 15 >= timeUtc)
return i;
}

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":[]} */

You might also like