forked from chiki2bum2/SniperGold_ML
632 lines
No EOL
13 KiB
Text
632 lines
No EOL
13 KiB
Text
//+------------------------------------------------------------------+
|
|
//| XAUUSD MTF MARKET HEALTH EA - MT5 |
|
|
//| M5 Entry + M15 + H1 + H4 Confirmation |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
|
|
#include <Trade/Trade.mqh>
|
|
CTrade trade;
|
|
|
|
//==================== INPUTS ========================================//
|
|
input ENUM_TIMEFRAMES EntryTF = PERIOD_M5;
|
|
|
|
input int FastEMA = 9;
|
|
input int SlowEMA = 15;
|
|
input int TrendEMA = 100;
|
|
input int RSIPeriod = 14;
|
|
input int ATRPeriod = 14;
|
|
|
|
input double MinHealthScore = 70.0;
|
|
|
|
input double RiskPercent = 1.0;
|
|
input double MaxDailyLossPercent = 3.0;
|
|
input int MaxTradesPerDay = 5;
|
|
|
|
input double SL_ATR_Multiplier = 1.5;
|
|
input double TP_ATR_Multiplier = 2.5;
|
|
|
|
input bool UseBreakEven = true;
|
|
input double BreakEvenATR = 1.0;
|
|
|
|
input bool UseTrailing = true;
|
|
input double TrailingATR = 1.0;
|
|
|
|
input int MaxSpreadPoints = 100;
|
|
|
|
input ulong MagicNumber = 9051501;
|
|
|
|
//==================== HANDLES ======================================//
|
|
int fastM5, slowM5, trendM5, rsiM5, atrM5;
|
|
|
|
int trendM15;
|
|
int trendH1;
|
|
int trendH4;
|
|
|
|
datetime lastBar = 0;
|
|
|
|
//==================== INIT ==========================================//
|
|
int OnInit()
|
|
{
|
|
fastM5 = iMA(_Symbol, EntryTF, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
|
|
slowM5 = iMA(_Symbol, EntryTF, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
|
|
trendM5 = iMA(_Symbol, EntryTF, TrendEMA, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
rsiM5 = iRSI(_Symbol, EntryTF, RSIPeriod, PRICE_CLOSE);
|
|
atrM5 = iATR(_Symbol, EntryTF, ATRPeriod);
|
|
|
|
trendM15 = iMA(_Symbol, PERIOD_M15, TrendEMA, 0,
|
|
MODE_EMA, PRICE_CLOSE);
|
|
|
|
trendH1 = iMA(_Symbol, PERIOD_H1, TrendEMA, 0,
|
|
MODE_EMA, PRICE_CLOSE);
|
|
|
|
trendH4 = iMA(_Symbol, PERIOD_H4, TrendEMA, 0,
|
|
MODE_EMA, PRICE_CLOSE);
|
|
|
|
if(fastM5 == INVALID_HANDLE ||
|
|
slowM5 == INVALID_HANDLE ||
|
|
trendM5 == INVALID_HANDLE ||
|
|
rsiM5 == INVALID_HANDLE ||
|
|
atrM5 == INVALID_HANDLE ||
|
|
trendM15 == INVALID_HANDLE ||
|
|
trendH1 == INVALID_HANDLE ||
|
|
trendH4 == INVALID_HANDLE)
|
|
{
|
|
Print("Indicator initialization failed.");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
trade.SetExpertMagicNumber(MagicNumber);
|
|
|
|
Print("MTF Market Health EA Started.");
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//==================== DEINIT ========================================//
|
|
void OnDeinit(const int reason)
|
|
{
|
|
IndicatorRelease(fastM5);
|
|
IndicatorRelease(slowM5);
|
|
IndicatorRelease(trendM5);
|
|
IndicatorRelease(rsiM5);
|
|
IndicatorRelease(atrM5);
|
|
|
|
IndicatorRelease(trendM15);
|
|
IndicatorRelease(trendH1);
|
|
IndicatorRelease(trendH4);
|
|
}
|
|
|
|
//==================== NEW BAR =======================================//
|
|
bool IsNewBar()
|
|
{
|
|
datetime t = iTime(_Symbol, EntryTF, 0);
|
|
|
|
if(t != lastBar)
|
|
{
|
|
lastBar = t;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//==================== GET VALUE =====================================//
|
|
double GetMAValue(int handle, int shift)
|
|
{
|
|
double buffer[];
|
|
|
|
if(CopyBuffer(handle, 0, shift, 1, buffer) != 1)
|
|
return 0;
|
|
|
|
return buffer[0];
|
|
}
|
|
|
|
//==================== RSI ===========================================//
|
|
double GetRSI()
|
|
{
|
|
double buffer[];
|
|
|
|
if(CopyBuffer(rsiM5, 0, 1, 1, buffer) != 1)
|
|
return 50;
|
|
|
|
return buffer[0];
|
|
}
|
|
|
|
//==================== ATR ===========================================//
|
|
double GetATR()
|
|
{
|
|
double buffer[];
|
|
|
|
if(CopyBuffer(atrM5, 0, 1, 1, buffer) != 1)
|
|
return 0;
|
|
|
|
return buffer[0];
|
|
}
|
|
|
|
//==================== TREND =========================================//
|
|
int GetTrend(ENUM_TIMEFRAMES tf, int handle)
|
|
{
|
|
double ema = GetMAValue(handle, 1);
|
|
double price = iClose(_Symbol, tf, 1);
|
|
|
|
if(ema == 0 || price == 0)
|
|
return 0;
|
|
|
|
if(price > ema)
|
|
return 1;
|
|
|
|
if(price < ema)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//==================== VOLUME ========================================//
|
|
bool VolumeBullish()
|
|
{
|
|
long currentVol = iVolume(_Symbol, EntryTF, 1);
|
|
long previousVol = iVolume(_Symbol, EntryTF, 2);
|
|
|
|
return currentVol >= previousVol;
|
|
}
|
|
|
|
//==================== SPREAD ========================================//
|
|
bool SpreadOK()
|
|
{
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
double spread = (ask - bid) / _Point;
|
|
|
|
return spread <= MaxSpreadPoints;
|
|
}
|
|
|
|
//==================== DAILY LOSS ====================================//
|
|
double TodayProfit()
|
|
{
|
|
datetime start =
|
|
StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
|
|
|
|
if(!HistorySelect(start, TimeCurrent()))
|
|
return 0;
|
|
|
|
double profit = 0;
|
|
|
|
int total = HistoryDealsTotal();
|
|
|
|
for(int i=0; i<total; i++)
|
|
{
|
|
ulong ticket = HistoryDealGetTicket(i);
|
|
|
|
if(ticket == 0)
|
|
continue;
|
|
|
|
long magic =
|
|
HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
|
|
|
if(magic != (long)MagicNumber)
|
|
continue;
|
|
|
|
profit += HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
|
profit += HistoryDealGetDouble(ticket, DEAL_SWAP);
|
|
profit += HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
|
}
|
|
|
|
return profit;
|
|
}
|
|
|
|
//==================== TRADES TODAY ==================================//
|
|
int TradesToday()
|
|
{
|
|
datetime start =
|
|
StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
|
|
|
|
if(!HistorySelect(start, TimeCurrent()))
|
|
return 0;
|
|
|
|
int count = 0;
|
|
|
|
int total = HistoryDealsTotal();
|
|
|
|
for(int i=0; i<total; i++)
|
|
{
|
|
ulong ticket = HistoryDealGetTicket(i);
|
|
|
|
if(ticket == 0)
|
|
continue;
|
|
|
|
long magic =
|
|
HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
|
|
|
long entry =
|
|
HistoryDealGetInteger(ticket, DEAL_ENTRY);
|
|
|
|
if(magic == (long)MagicNumber &&
|
|
entry == DEAL_ENTRY_IN)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
//==================== HEALTH SCORE ==================================//
|
|
double GetHealthScore(int direction)
|
|
{
|
|
double score = 0;
|
|
|
|
// M15 trend = 20
|
|
int m15 = GetTrend(PERIOD_M15, trendM15);
|
|
|
|
if(m15 == direction)
|
|
score += 20;
|
|
|
|
// H1 trend = 25
|
|
int h1 = GetTrend(PERIOD_H1, trendH1);
|
|
|
|
if(h1 == direction)
|
|
score += 25;
|
|
|
|
// H4 trend = 25
|
|
int h4 = GetTrend(PERIOD_H4, trendH4);
|
|
|
|
if(h4 == direction)
|
|
score += 25;
|
|
|
|
// M5 trend = 10
|
|
int m5 = GetTrend(EntryTF, trendM5);
|
|
|
|
if(m5 == direction)
|
|
score += 10;
|
|
|
|
// RSI = 10
|
|
double rsi = GetRSI();
|
|
|
|
if(direction == 1 && rsi >= 55)
|
|
score += 10;
|
|
|
|
if(direction == -1 && rsi <= 45)
|
|
score += 10;
|
|
|
|
// Volume = 10
|
|
if(VolumeBullish())
|
|
score += 10;
|
|
|
|
return score;
|
|
}
|
|
|
|
//==================== LOT SIZE ======================================//
|
|
double CalculateLot(double slDistance)
|
|
{
|
|
double balance =
|
|
AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
double riskMoney =
|
|
balance * RiskPercent / 100.0;
|
|
|
|
double tickValue =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
|
|
double tickSize =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
if(tickValue <= 0 || tickSize <= 0)
|
|
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
|
|
double lossPerLot =
|
|
(slDistance / tickSize) * tickValue;
|
|
|
|
if(lossPerLot <= 0)
|
|
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
|
|
double lot = riskMoney / lossPerLot;
|
|
|
|
double minLot =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
|
|
double maxLot =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
|
|
double step =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
lot = MathMax(lot, minLot);
|
|
lot = MathMin(lot, maxLot);
|
|
|
|
lot = MathFloor(lot / step) * step;
|
|
|
|
return NormalizeDouble(lot, 2);
|
|
}
|
|
|
|
//==================== BUY SIGNAL ====================================//
|
|
bool BuySignal()
|
|
{
|
|
double fast1 = GetMAValue(fastM5, 1);
|
|
double fast2 = GetMAValue(fastM5, 2);
|
|
|
|
double slow1 = GetMAValue(slowM5, 1);
|
|
double slow2 = GetMAValue(slowM5, 2);
|
|
|
|
if(fast1 == 0 || slow1 == 0)
|
|
return false;
|
|
|
|
bool crossover =
|
|
fast2 <= slow2 &&
|
|
fast1 > slow1;
|
|
|
|
return crossover;
|
|
}
|
|
|
|
//==================== SELL SIGNAL ===================================//
|
|
bool SellSignal()
|
|
{
|
|
double fast1 = GetMAValue(fastM5, 1);
|
|
double fast2 = GetMAValue(fastM5, 2);
|
|
|
|
double slow1 = GetMAValue(slowM5, 1);
|
|
double slow2 = GetMAValue(slowM5, 2);
|
|
|
|
if(fast1 == 0 || slow1 == 0)
|
|
return false;
|
|
|
|
bool crossover =
|
|
fast2 >= slow2 &&
|
|
fast1 < slow1;
|
|
|
|
return crossover;
|
|
}
|
|
|
|
//==================== BUY ===========================================//
|
|
void OpenBuy()
|
|
{
|
|
double atr = GetATR();
|
|
|
|
if(atr <= 0)
|
|
return;
|
|
|
|
double ask =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
double slDistance =
|
|
atr * SL_ATR_Multiplier;
|
|
|
|
double tpDistance =
|
|
atr * TP_ATR_Multiplier;
|
|
|
|
double lot =
|
|
CalculateLot(slDistance);
|
|
|
|
double sl =
|
|
ask - slDistance;
|
|
|
|
double tp =
|
|
ask + tpDistance;
|
|
|
|
trade.Buy(
|
|
lot,
|
|
_Symbol,
|
|
ask,
|
|
NormalizeDouble(sl, _Digits),
|
|
NormalizeDouble(tp, _Digits),
|
|
"MTF HEALTH BUY"
|
|
);
|
|
}
|
|
|
|
//==================== SELL ==========================================//
|
|
void OpenSell()
|
|
{
|
|
double atr = GetATR();
|
|
|
|
if(atr <= 0)
|
|
return;
|
|
|
|
double bid =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
double slDistance =
|
|
atr * SL_ATR_Multiplier;
|
|
|
|
double tpDistance =
|
|
atr * TP_ATR_Multiplier;
|
|
|
|
double lot =
|
|
CalculateLot(slDistance);
|
|
|
|
double sl =
|
|
bid + slDistance;
|
|
|
|
double tp =
|
|
bid - tpDistance;
|
|
|
|
trade.Sell(
|
|
lot,
|
|
_Symbol,
|
|
bid,
|
|
NormalizeDouble(sl, _Digits),
|
|
NormalizeDouble(tp, _Digits),
|
|
"MTF HEALTH SELL"
|
|
);
|
|
}
|
|
|
|
//==================== POSITION MANAGEMENT ===========================//
|
|
void ManagePosition()
|
|
{
|
|
if(!PositionSelect(_Symbol))
|
|
return;
|
|
|
|
double atr = GetATR();
|
|
|
|
if(atr <= 0)
|
|
return;
|
|
|
|
long type =
|
|
PositionGetInteger(POSITION_TYPE);
|
|
|
|
double open =
|
|
PositionGetDouble(POSITION_PRICE_OPEN);
|
|
|
|
double sl =
|
|
PositionGetDouble(POSITION_SL);
|
|
|
|
double tp =
|
|
PositionGetDouble(POSITION_TP);
|
|
|
|
double bid =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
double ask =
|
|
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
// BUY
|
|
if(type == POSITION_TYPE_BUY)
|
|
{
|
|
double profit =
|
|
bid - open;
|
|
|
|
// Break-even
|
|
if(UseBreakEven &&
|
|
profit >= atr * BreakEvenATR)
|
|
{
|
|
double newSL = open;
|
|
|
|
if(sl < newSL)
|
|
{
|
|
trade.PositionModify(
|
|
_Symbol,
|
|
NormalizeDouble(newSL, _Digits),
|
|
tp
|
|
);
|
|
}
|
|
}
|
|
|
|
// Trailing
|
|
if(UseTrailing &&
|
|
profit >= atr * TrailingATR)
|
|
{
|
|
double newSL =
|
|
bid - atr * TrailingATR;
|
|
|
|
if(newSL > sl)
|
|
{
|
|
trade.PositionModify(
|
|
_Symbol,
|
|
NormalizeDouble(newSL, _Digits),
|
|
tp
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// SELL
|
|
if(type == POSITION_TYPE_SELL)
|
|
{
|
|
double profit =
|
|
open - ask;
|
|
|
|
// Break-even
|
|
if(UseBreakEven &&
|
|
profit >= atr * BreakEvenATR)
|
|
{
|
|
double newSL = open;
|
|
|
|
if(sl == 0 || sl > newSL)
|
|
{
|
|
trade.PositionModify(
|
|
_Symbol,
|
|
NormalizeDouble(newSL, _Digits),
|
|
tp
|
|
);
|
|
}
|
|
}
|
|
|
|
// Trailing
|
|
if(UseTrailing &&
|
|
profit >= atr * TrailingATR)
|
|
{
|
|
double newSL =
|
|
ask + atr * TrailingATR;
|
|
|
|
if(sl == 0 || newSL < sl)
|
|
{
|
|
trade.PositionModify(
|
|
_Symbol,
|
|
NormalizeDouble(newSL, _Digits),
|
|
tp
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==================== MAIN ==========================================//
|
|
void OnTick()
|
|
{
|
|
// Position management runs continuously
|
|
ManagePosition();
|
|
|
|
// New candle only for entries
|
|
if(!IsNewBar())
|
|
return;
|
|
|
|
// Spread protection
|
|
if(!SpreadOK())
|
|
{
|
|
Print("Spread too high - trade skipped.");
|
|
return;
|
|
}
|
|
|
|
// Daily trade limit
|
|
if(TradesToday() >= MaxTradesPerDay)
|
|
{
|
|
Print("Daily trade limit reached.");
|
|
return;
|
|
}
|
|
|
|
// Daily loss protection
|
|
double balance =
|
|
AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
double maxLoss =
|
|
balance * MaxDailyLossPercent / 100.0;
|
|
|
|
if(TodayProfit() <= -maxLoss)
|
|
{
|
|
Print("Daily loss limit reached.");
|
|
return;
|
|
}
|
|
|
|
// One position at a time
|
|
if(PositionSelect(_Symbol))
|
|
return;
|
|
|
|
// BUY
|
|
if(BuySignal())
|
|
{
|
|
double score = GetHealthScore(1);
|
|
|
|
Print("BUY Health Score = ", score);
|
|
|
|
if(score >= MinHealthScore)
|
|
{
|
|
OpenBuy();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// SELL
|
|
if(SellSignal())
|
|
{
|
|
double score = GetHealthScore(-1);
|
|
|
|
Print("SELL Health Score = ", score);
|
|
|
|
if(score >= MinHealthScore)
|
|
{
|
|
OpenSell();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+ |