Add files via upload

This commit is contained in:
kingstonebridge2032
2026-05-20 22:32:26 +03:00
committed by GitHub
commit 32f17bc949
4 changed files with 1270 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
FROM python:3.11-slim-bookworm
USER root
ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:1
ENV WINEPREFIX=/root/.wine
ENV WINEARCH=win64
ENV WINEDEBUG=-all
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y --no-install-recommends \
wine wine64 wine32:i386 winbind xvfb fluxbox x11vnc novnc websockify \
wget curl procps cabextract unzip dos2unix xdotool \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir mt5linux rpyc
RUN wget -q https://download.mql5.com/cdn/web/metaquotes.software.corp/mt5/mt5setup.exe \
-O /root/mt5setup.exe
# Copy EA files
COPY ["Volatility_Breakout_Pro_News_Filter.mq5", "/root/Volatility_Breakout_Pro_News_Filter.mq5"]
COPY ["InstitutionalTickHybridNewsFilter.mq5", "/root/InstitutionalTickHybridNewsFilter.mq5"]
COPY ["Tick_Reversion_Pro_NEWS_FILTER.mq5", "/root/Tick_Reversion_Pro_NEWS_FILTER.mq5"]
RUN cat > /entrypoint.sh <<EOF
#!/bin/bash
set -e
rm -rf /tmp/.X*
Xvfb :1 -screen 0 1280x1024x24 -ac &
sleep 2
fluxbox &
x11vnc -display :1 -forever -shared -nopw -rfbport 5900 &
websockify --web=/usr/share/novnc 8080 0.0.0.0:5900 &
wineboot --init
sleep 5
MT5_EXE="/root/.wine/drive_c/Program Files/MetaTrader 5/terminal64.exe"
if [ ! -f "\$MT5_EXE" ]; then
wine /root/mt5setup.exe /auto
sleep 90
fi
wine "\$MT5_EXE" &
sleep 30
DATA_DIR=\$(find /root/.wine -type d -path "*MetaQuotes/Terminal/*/MQL5" | head -n 1)
if [ -z "\$DATA_DIR" ]; then
DATA_DIR="/root/.wine/drive_c/Program Files/MetaTrader 5/MQL5"
fi
mkdir -p "\$DATA_DIR/Experts"
cp "/root/Volatility_Breakout_Pro_News_Filter.mq5" "\$DATA_DIR/Experts/"
cp "/root/InstitutionalTickHybridNewsFilter.mq5" "\$DATA_DIR/Experts/"
cp "/root/Tick_Reversion_Pro_NEWS_FILTER.mq5" "\$DATA_DIR/Experts/"
echo "✅ Copied EAs to \$DATA_DIR/Experts/"
ls -la "\$DATA_DIR/Experts/"
python3 -m mt5linux --host 0.0.0.0 --port 8001 &
tail -f /dev/null
EOF
RUN chmod +x /entrypoint.sh && dos2unix /entrypoint.sh
EXPOSE 8080 8001
CMD ["/bin/bash", "/entrypoint.sh"]
+503
View File
@@ -0,0 +1,503 @@
//+------------------------------------------------------------------+
//| InstitutionalTickHybridEA.mq5 |
//| FIXED VERSION + HIGH IMPACT NEWS FILTER |
//+------------------------------------------------------------------+
#property strict
#property version "1.20"
#include <Trade/Trade.mqh>
CTrade trade;
//====================================================
// INPUTS
//====================================================
input ulong MagicNumber = 880001;
input double ForexLot = 0.01;
input double GoldLot = 0.01;
input int EMAFastPeriod = 20;
input int EMATrendPeriod = 200;
input int ATRPeriod = 14;
input double ATRMultiplierSL = 2.0;
input double ATRTrailingMultiplier = 1.5;
input int TickBufferSize = 30;
input double ZScoreThreshold = 1.3;
input int CooldownSeconds = 10;
input int MaxPositionsPerSymbol = 3;
input double MaxSpreadPoints = 40;
input double MinATRPointsForex = 15;
input double MinATRPointsGold = 150;
input double ProfitTargetPerLot = 20.0;
input bool UseBreakeven = true;
input double BreakevenOffsetPoints = 10;
input bool EnableDebug = true;
//====================================================
// NEWS FILTER
//====================================================
input bool UseNewsFilter = true;
input int NewsPauseBeforeMin = 60;
input int NewsPauseAfterMin = 60;
//====================================================
// GLOBALS
//====================================================
double TickBuffer[];
datetime LastTradeTime = 0;
int emaFastHandle;
int emaTrendHandle;
int atrHandle;
//====================================================
// INIT
//====================================================
int OnInit()
{
ArrayResize(TickBuffer, TickBufferSize);
emaFastHandle = iMA(_Symbol,_Period,EMAFastPeriod,0,MODE_EMA,PRICE_CLOSE);
emaTrendHandle = iMA(_Symbol,_Period,EMATrendPeriod,0,MODE_EMA,PRICE_CLOSE);
atrHandle = iATR(_Symbol,_Period,ATRPeriod);
if(emaFastHandle==INVALID_HANDLE || emaTrendHandle==INVALID_HANDLE || atrHandle==INVALID_HANDLE)
{
Print("Indicator init failed");
return INIT_FAILED;
}
Print("EA WITH NEWS FILTER INITIALIZED");
return INIT_SUCCEEDED;
}
//====================================================
// DEINIT
//====================================================
void OnDeinit(const int reason)
{
IndicatorRelease(emaFastHandle);
IndicatorRelease(emaTrendHandle);
IndicatorRelease(atrHandle);
}
//====================================================
// ON TICK
//====================================================
void OnTick()
{
UpdateTickBuffer();
//================================================
// HIGH IMPACT NEWS FILTER
//================================================
if(UseNewsFilter && IsHighImpactNewsTime())
{
CloseAllPositions();
Debug("Trading paused due to HIGH IMPACT NEWS");
return;
}
ManagePositions();
if(!CanTrade())
return;
if(CountPositionsBySymbol() >= MaxPositionsPerSymbol)
return;
double z = CalculateZScore();
double emaFast = GetEMA(emaFastHandle);
double emaTrend = GetEMA(emaTrendHandle);
double atr = GetATR();
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
bool bullish = bid > emaTrend;
bool bearish = bid < emaTrend;
bool reversalBuy = (z < -1.0);
bool reversalSell = (z > 1.0);
if(bullish && reversalBuy)
OpenBuy(atr);
if(bearish && reversalSell)
OpenSell(atr);
}
//====================================================
// NEWS FILTER FUNCTION
//====================================================
//====================================================
// NEWS FILTER FUNCTION
//====================================================
bool IsHighImpactNewsTime()
{
MqlCalendarValue values[];
datetime now = TimeCurrent();
datetime from = now - 60;
datetime to = now + (NewsPauseBeforeMin * 60);
int count = CalendarValueHistory(values, from, to);
if(count <= 0)
return false;
string symbolCurrency1 = StringSubstr(_Symbol,0,3);
string symbolCurrency2 = StringSubstr(_Symbol,3,3);
for(int i=0; i<count; i++)
{
MqlCalendarEvent event;
if(!CalendarEventById(values[i].event_id,event))
continue;
// HIGH IMPACT ONLY
if(event.importance != CALENDAR_IMPORTANCE_HIGH)
continue;
//================================================
// FIXED CURRENCY ACCESS
//================================================
MqlCalendarCountry country;
if(!CalendarCountryById(event.country_id,country))
continue;
string currency = country.currency;
bool relevant = false;
// GOLD -> USD NEWS ONLY
if(StringFind(_Symbol,"XAU") >= 0 ||
StringFind(_Symbol,"GOLD") >= 0)
{
if(currency == "USD")
relevant = true;
}
else
{
if(currency == symbolCurrency1 ||
currency == symbolCurrency2)
relevant = true;
}
if(!relevant)
continue;
datetime eventTime = values[i].time;
datetime blockStart =
eventTime - (NewsPauseBeforeMin * 60);
datetime blockEnd =
eventTime + (NewsPauseAfterMin * 60);
if(now >= blockStart && now <= blockEnd)
{
Debug("High impact news detected");
return true;
}
}
return false;
}
//====================================================
// CLOSE ALL POSITIONS
//====================================================
void CloseAllPositions()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(ticket==0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
trade.PositionClose(ticket);
}
}
//====================================================
// UPDATE TICKS
//====================================================
void UpdateTickBuffer()
{
for(int i=TickBufferSize-1;i>0;i--)
TickBuffer[i]=TickBuffer[i-1];
TickBuffer[0]=SymbolInfoDouble(_Symbol,SYMBOL_BID);
}
//====================================================
// Z SCORE
//====================================================
double CalculateZScore()
{
double sum=0;
for(int i=0;i<TickBufferSize;i++)
sum+=TickBuffer[i];
double mean=sum/TickBufferSize;
double var=0;
for(int i=0;i<TickBufferSize;i++)
var += MathPow(TickBuffer[i]-mean,2);
var/=TickBufferSize;
double std=MathSqrt(var);
if(std==0)
return 0;
return (TickBuffer[0]-mean)/std;
}
//====================================================
// INDICATORS
//====================================================
double GetEMA(int handle)
{
double buf[];
if(CopyBuffer(handle,0,0,1,buf)<=0)
return 0;
return buf[0];
}
double GetATR()
{
double buf[];
if(CopyBuffer(atrHandle,0,0,1,buf)<=0)
return 0;
return buf[0];
}
//====================================================
// CAN TRADE
//====================================================
bool CanTrade()
{
double spread =
(SymbolInfoDouble(_Symbol,SYMBOL_ASK)
-SymbolInfoDouble(_Symbol,SYMBOL_BID))/_Point;
if(spread > MaxSpreadPoints)
{
Debug("Spread too high");
return false;
}
double atrPoints = GetATR()/_Point;
string sym=_Symbol;
StringToUpper(sym);
double minATR = MinATRPointsForex;
if(StringFind(sym,"XAU")>=0 || StringFind(sym,"GOLD")>=0)
minATR = MinATRPointsGold;
if(atrPoints < minATR)
{
Debug("ATR too low: " + DoubleToString(atrPoints,1));
return false;
}
if(TimeCurrent()-LastTradeTime < CooldownSeconds)
return false;
return true;
}
//====================================================
// OPEN BUY
//====================================================
void OpenBuy(double atr)
{
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double sl=ask-(atr*ATRMultiplierSL);
AdjustStopsForBroker(sl,true);
double lot=GetLotSize();
trade.SetExpertMagicNumber(MagicNumber);
if(trade.Buy(lot,_Symbol,ask,sl,0,"BUY WITH NEWS FILTER"))
LastTradeTime=TimeCurrent();
}
//====================================================
// OPEN SELL
//====================================================
void OpenSell(double atr)
{
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
double sl=bid+(atr*ATRMultiplierSL);
AdjustStopsForBroker(sl,false);
double lot=GetLotSize();
trade.SetExpertMagicNumber(MagicNumber);
if(trade.Sell(lot,_Symbol,bid,sl,0,"SELL WITH NEWS FILTER"))
LastTradeTime=TimeCurrent();
}
//====================================================
// LOT SIZE
//====================================================
double GetLotSize()
{
string s=_Symbol;
StringToUpper(s);
if(StringFind(s,"XAU")>=0 || StringFind(s,"GOLD")>=0)
return GoldLot;
return ForexLot;
}
//====================================================
// POSITION COUNT
//====================================================
int CountPositionsBySymbol()
{
int c=0;
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong t=PositionGetTicket(i);
if(t==0)
continue;
if(!PositionSelectByTicket(t))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
c++;
}
return c;
}
//====================================================
// POSITION MANAGEMENT
//====================================================
void ManagePositions()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong t=PositionGetTicket(i);
if(t==0)
continue;
if(!PositionSelectByTicket(t))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
double vol=PositionGetDouble(POSITION_VOLUME);
double sl=PositionGetDouble(POSITION_SL);
double profit=PositionGetDouble(POSITION_PROFIT);
double atr=GetATR();
double target=vol*ProfitTargetPerLot;
if(profit>=target)
{
trade.PositionClose(t);
continue;
}
ENUM_POSITION_TYPE type=
(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type==POSITION_TYPE_BUY)
{
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
double newSL=bid-(atr*ATRTrailingMultiplier);
if(newSL>sl)
trade.PositionModify(t,newSL,0);
}
if(type==POSITION_TYPE_SELL)
{
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double newSL=ask+(atr*ATRTrailingMultiplier);
if(sl==0 || newSL<sl)
trade.PositionModify(t,newSL,0);
}
}
}
//====================================================
// BROKER SAFETY
//====================================================
void AdjustStopsForBroker(double &sl,bool buy)
{
double stop=
(double)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point;
double freeze=
(double)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL)*_Point;
double min=MathMax(stop,freeze);
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
if(buy && (ask-sl)<min)
sl=ask-(min+10*_Point);
if(!buy && (sl-bid)<min)
sl=bid+(min+10*_Point);
}
//====================================================
// DEBUG
//====================================================
void Debug(string txt)
{
if(EnableDebug)
Print("[",_Symbol,"] ",txt);
}
//+------------------------------------------------------------------+
+384
View File
@@ -0,0 +1,384 @@
//+------------------------------------------------------------------+
//| Tick_Reversion_Pro.mq5 |
//| WITH HIGH IMPACT NEWS FILTER |
//+------------------------------------------------------------------+
#property strict
#include <Trade/Trade.mqh>
input double ForexLotSize=0.10;
input double OtherLotSize=0.01;
input int LookbackTicks=50;
input double DeviationThreshold=1.5;
input int ATRPeriod=14;
input double ATRMultiplier=2.5;
input double RiskReward=4.0;
input int MaxPositionsPerSymbol=2;
input bool UseEMA200=true;
input double ProfitTargetPerLot=100.0;
input ulong MagicNumber=777777;
//====================================================
// NEWS FILTER
//====================================================
input bool UseNewsFilter=true;
input int NewsPauseBeforeMin=60;
input int NewsPauseAfterMin=60;
double TickBuffer[];
int atrHandle;
int emaHandle;
CTrade trade;
//====================================================
// LOT SIZE
//====================================================
double GetLotSize()
{
string s=_Symbol;
if(StringFind(s,"XAU")>=0 || StringFind(s,"XAG")>=0 ||
StringFind(s,"BTC")>=0 || StringFind(s,"ETH")>=0 ||
StringFind(s,"US30")>=0 || StringFind(s,"NAS")>=0 ||
StringFind(s,"GER")>=0)
return OtherLotSize;
return ForexLotSize;
}
//====================================================
// POSITION COUNT
//====================================================
int CountPositionsForSymbol()
{
int count=0;
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
count++;
}
return count;
}
//====================================================
// TREND FILTER
//====================================================
bool TrendFilter(ENUM_ORDER_TYPE type)
{
if(!UseEMA200)
return true;
double ema[1];
if(CopyBuffer(emaHandle,0,1,1,ema)<1)
return false;
double close=iClose(_Symbol,_Period,1);
if(type==ORDER_TYPE_BUY)
return close>ema[0];
return close<ema[0];
}
//====================================================
// DYNAMIC STOP
//====================================================
double DynamicStopDistance()
{
double atr[1];
if(CopyBuffer(atrHandle,0,0,1,atr)<1)
return 100*_Point;
double stopLevel=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point;
double freezeLevel=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL)*_Point;
double spread=SymbolInfoDouble(_Symbol,SYMBOL_ASK)-SymbolInfoDouble(_Symbol,SYMBOL_BID);
double brokerMin=MathMax(MathMax(stopLevel,freezeLevel),spread*10.0);
return MathMax(atr[0]*ATRMultiplier,brokerMin*3.0);
}
//====================================================
// MANAGE PROFIT TARGET
//====================================================
void ManageProfitTarget()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
double profit=PositionGetDouble(POSITION_PROFIT);
double volume=PositionGetDouble(POSITION_VOLUME);
if(profit>=volume*ProfitTargetPerLot)
trade.PositionClose(ticket);
}
}
//====================================================
// MANAGE TRAILING
//====================================================
void ManageTrailing()
{
double dist=DynamicStopDistance();
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
ENUM_POSITION_TYPE pos=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double sl=PositionGetDouble(POSITION_SL);
double tp=PositionGetDouble(POSITION_TP);
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
if(pos==POSITION_TYPE_BUY)
{
double newSL=NormalizeDouble(bid-dist,_Digits);
if(newSL>sl)
trade.PositionModify(ticket,newSL,tp);
}
else
{
double newSL=NormalizeDouble(ask+dist,_Digits);
if(sl==0 || newSL<sl)
trade.PositionModify(ticket,newSL,tp);
}
}
}
//====================================================
// CLOSE ALL POSITIONS
//====================================================
void CloseAllPositions()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(ticket==0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
trade.PositionClose(ticket);
}
}
//====================================================
// NEWS FILTER
//====================================================
bool IsHighImpactNewsTime()
{
MqlCalendarValue values[];
datetime now = TimeCurrent();
datetime from = now - 60;
datetime to = now + (NewsPauseBeforeMin * 60);
int count=CalendarValueHistory(values,from,to);
if(count<=0)
return false;
string symbolCurrency1=StringSubstr(_Symbol,0,3);
string symbolCurrency2=StringSubstr(_Symbol,3,3);
for(int i=0;i<count;i++)
{
MqlCalendarEvent event;
if(!CalendarEventById(values[i].event_id,event))
continue;
if(event.importance!=CALENDAR_IMPORTANCE_HIGH)
continue;
MqlCalendarCountry country;
if(!CalendarCountryById(event.country_id,country))
continue;
string currency=country.currency;
bool relevant=false;
// GOLD/SILVER/CRYPTO/INDICES -> USD NEWS
if(StringFind(_Symbol,"XAU")>=0 ||
StringFind(_Symbol,"XAG")>=0 ||
StringFind(_Symbol,"BTC")>=0 ||
StringFind(_Symbol,"ETH")>=0 ||
StringFind(_Symbol,"US30")>=0 ||
StringFind(_Symbol,"NAS")>=0 ||
StringFind(_Symbol,"GER")>=0)
{
if(currency=="USD")
relevant=true;
}
else
{
if(currency==symbolCurrency1 ||
currency==symbolCurrency2)
relevant=true;
}
if(!relevant)
continue;
datetime eventTime=values[i].time;
datetime blockStart=
eventTime-(NewsPauseBeforeMin*60);
datetime blockEnd=
eventTime+(NewsPauseAfterMin*60);
if(now>=blockStart && now<=blockEnd)
return true;
}
return false;
}
//====================================================
// EXECUTE DEAL
//====================================================
void ExecuteDeal(ENUM_ORDER_TYPE type)
{
double dist=DynamicStopDistance();
double price=(type==ORDER_TYPE_BUY)
? SymbolInfoDouble(_Symbol,SYMBOL_ASK)
: SymbolInfoDouble(_Symbol,SYMBOL_BID);
double sl=(type==ORDER_TYPE_BUY) ? price-dist : price+dist;
double tp=(type==ORDER_TYPE_BUY) ? price+dist*RiskReward : price-dist*RiskReward;
trade.PositionOpen(
_Symbol,
type,
GetLotSize(),
price,
NormalizeDouble(sl,_Digits),
NormalizeDouble(tp,_Digits),
"TickReversionProNews"
);
}
//====================================================
// INIT
//====================================================
int OnInit()
{
ArrayResize(TickBuffer,LookbackTicks);
ArrayInitialize(TickBuffer,0);
atrHandle=iATR(_Symbol,_Period,ATRPeriod);
emaHandle=iMA(_Symbol,_Period,200,0,MODE_EMA,PRICE_CLOSE);
trade.SetExpertMagicNumber(MagicNumber);
return(INIT_SUCCEEDED);
}
//====================================================
// ON TICK
//====================================================
void OnTick()
{
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
for(int i=LookbackTicks-1;i>0;i--)
TickBuffer[i]=TickBuffer[i-1];
TickBuffer[0]=bid;
//================================================
// NEWS FILTER
//================================================
if(UseNewsFilter && IsHighImpactNewsTime())
{
CloseAllPositions();
return;
}
ManageProfitTarget();
ManageTrailing();
if(TickBuffer[LookbackTicks-1]==0)
return;
if(CountPositionsForSymbol()>=MaxPositionsPerSymbol)
return;
double sum=0;
for(int i=0;i<LookbackTicks;i++)
sum+=TickBuffer[i];
double avg=sum/LookbackTicks;
double var=0;
for(int i=0;i<LookbackTicks;i++)
var+=MathPow(TickBuffer[i]-avg,2);
double sd=MathSqrt(var/LookbackTicks);
if(sd<=0)
return;
double z=(bid-avg)/sd;
//================================================
// ORIGINAL STRATEGY LOGIC UNCHANGED
//================================================
if(z<=-DeviationThreshold && TrendFilter(ORDER_TYPE_BUY))
ExecuteDeal(ORDER_TYPE_BUY);
if(z>=DeviationThreshold && TrendFilter(ORDER_TYPE_SELL))
ExecuteDeal(ORDER_TYPE_SELL);
}
//+------------------------------------------------------------------+
+300
View File
@@ -0,0 +1,300 @@
//+------------------------------------------------------------------+
//| Volatility_Breakout_Pro.mq5 |
//| WITH HIGH IMPACT NEWS FILTER |
//+------------------------------------------------------------------+
#property strict
#include <Trade/Trade.mqh>
input double ForexLotSize=0.01;
input double OtherLotSize=0.01;
input int ATRPeriod=14;
input int EMAFast=50;
input int EMASlow=200;
input double ATRMultiplierSL=2.5;
input double RiskReward=3.0;
input double ProfitTargetPerLot=20.0;
input int MaxPositionsPerSymbol=10;
input ulong MagicNumber=888888;
//====================================================
// NEWS FILTER
//====================================================
input bool UseNewsFilter=true;
input int NewsPauseBeforeMin=60;
input int NewsPauseAfterMin=60;
CTrade trade;
int atrHandle, fastHandle, slowHandle;
//====================================================
// LOT SIZE
//====================================================
double GetLotSize()
{
string s=_Symbol;
if(StringFind(s,"XAU")>=0 || StringFind(s,"XAG")>=0 ||
StringFind(s,"BTC")>=0 || StringFind(s,"ETH")>=0 ||
StringFind(s,"US30")>=0 || StringFind(s,"NAS")>=0)
return OtherLotSize;
return ForexLotSize;
}
//====================================================
// POSITION COUNT
//====================================================
int CountPositionsForSymbol()
{
int count=0;
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
count++;
}
return count;
}
//====================================================
// DYNAMIC STOP
//====================================================
double DynamicStop()
{
double atr[1];
if(CopyBuffer(atrHandle,0,0,1,atr)<1)
return 100*_Point;
double stopLevel=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point;
double freezeLevel=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL)*_Point;
double spread=SymbolInfoDouble(_Symbol,SYMBOL_ASK)-SymbolInfoDouble(_Symbol,SYMBOL_BID);
return MathMax(atr[0]*ATRMultiplierSL,
MathMax(stopLevel,freezeLevel)+spread*10.0);
}
//====================================================
// MANAGE POSITIONS
//====================================================
void ManagePositions()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
double profit=PositionGetDouble(POSITION_PROFIT);
double volume=PositionGetDouble(POSITION_VOLUME);
if(profit>=volume*ProfitTargetPerLot)
{
trade.PositionClose(ticket);
continue;
}
double tp=PositionGetDouble(POSITION_TP);
double sl=PositionGetDouble(POSITION_SL);
double dist=DynamicStop();
ENUM_POSITION_TYPE pos=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(pos==POSITION_TYPE_BUY)
{
double newSL=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID)-dist,_Digits);
if(newSL>sl)
trade.PositionModify(ticket,newSL,tp);
}
else
{
double newSL=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK)+dist,_Digits);
if(sl==0 || newSL<sl)
trade.PositionModify(ticket,newSL,tp);
}
}
}
//====================================================
// CLOSE ALL POSITIONS
//====================================================
void CloseAllPositions()
{
for(int i=PositionsTotal()-1;i>=0;i--)
{
ulong ticket=PositionGetTicket(i);
if(ticket==0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL)!=_Symbol)
continue;
trade.PositionClose(ticket);
}
}
//====================================================
// NEWS FILTER
//====================================================
bool IsHighImpactNewsTime()
{
MqlCalendarValue values[];
datetime now = TimeCurrent();
datetime from = now - 60;
datetime to = now + (NewsPauseBeforeMin * 60);
int count = CalendarValueHistory(values,from,to);
if(count<=0)
return false;
string symbolCurrency1=StringSubstr(_Symbol,0,3);
string symbolCurrency2=StringSubstr(_Symbol,3,3);
for(int i=0;i<count;i++)
{
MqlCalendarEvent event;
if(!CalendarEventById(values[i].event_id,event))
continue;
if(event.importance!=CALENDAR_IMPORTANCE_HIGH)
continue;
// FIXED MT5 COMPATIBILITY
MqlCalendarCountry country;
if(!CalendarCountryById(event.country_id,country))
continue;
string currency=country.currency;
bool relevant=false;
// GOLD/SILVER/INDICES/CRYPTO -> USD NEWS
if(StringFind(_Symbol,"XAU")>=0 ||
StringFind(_Symbol,"XAG")>=0 ||
StringFind(_Symbol,"BTC")>=0 ||
StringFind(_Symbol,"ETH")>=0 ||
StringFind(_Symbol,"US30")>=0 ||
StringFind(_Symbol,"NAS")>=0)
{
if(currency=="USD")
relevant=true;
}
else
{
if(currency==symbolCurrency1 ||
currency==symbolCurrency2)
relevant=true;
}
if(!relevant)
continue;
datetime eventTime=values[i].time;
datetime blockStart=
eventTime-(NewsPauseBeforeMin*60);
datetime blockEnd=
eventTime+(NewsPauseAfterMin*60);
if(now>=blockStart && now<=blockEnd)
return true;
}
return false;
}
//====================================================
// INIT
//====================================================
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
atrHandle=iATR(_Symbol,_Period,ATRPeriod);
fastHandle=iMA(_Symbol,_Period,EMAFast,0,MODE_EMA,PRICE_CLOSE);
slowHandle=iMA(_Symbol,_Period,EMASlow,0,MODE_EMA,PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
//====================================================
// ON TICK
//====================================================
void OnTick()
{
//================================================
// NEWS FILTER
//================================================
if(UseNewsFilter && IsHighImpactNewsTime())
{
CloseAllPositions();
return;
}
ManagePositions();
if(CountPositionsForSymbol()>=MaxPositionsPerSymbol)
return;
double fast[2], slow[2];
if(CopyBuffer(fastHandle,0,0,2,fast)<2) return;
if(CopyBuffer(slowHandle,0,0,2,slow)<2) return;
double high=iHigh(_Symbol,_Period,1);
double low=iLow(_Symbol,_Period,1);
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
double slDist=DynamicStop();
bool upTrend=fast[0]>slow[0];
bool downTrend=fast[0]<slow[0];
//================================================
// ORIGINAL STRATEGY LOGIC UNCHANGED
//================================================
if(upTrend && ask>high)
{
trade.Buy(GetLotSize(),
_Symbol,
ask,
ask-slDist,
ask+slDist*RiskReward,
"BreakoutBuyNews");
}
if(downTrend && bid<low)
{
trade.Sell(GetLotSize(),
_Symbol,
bid,
bid+slDist,
bid-slDist*RiskReward,
"BreakoutSellNews");
}
}
//+------------------------------------------------------------------+