Files
Bell-PriceActionWithEma-EA/Experts/BellPriceActionWithEma50EA.mq5
T
2025-12-20 13:11:49 +07:00

223 lines
14 KiB
Plaintext

//+------------------------------------------------------------------+
//| EURUSD Trend Pullback EA (Clean Architecture) |
//| SRP - Meaningful Names - No Logic In OnTick |
//| Author: Bell CW |
//+------------------------------------------------------------------+
#property strict
#include <Trade/Trade.mqh>
//==================================================================
// INPUTS
//==================================================================
input ENUM_MA_METHOD TrendMaType = MODE_EMA;
input ENUM_MA_METHOD EntryMaType = MODE_EMA;
input int TrendFastMAPeriod = 100;
input int TrendSlowMAPeriod = 200;
input int EntryMAPeriod = 50;
input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H4;
input ENUM_TIMEFRAMES EntryTimeframe = PERIOD_M15;
input double RiskPercent = 1.0;
input double RewardRisk = 1.0;
input int TradeStartHour = 14; // VN time
input int TradeEndHour = 22;
input int SlippagePoints = 5;
input int MagicNumber = 26092025;
//==================================================================
// GLOBALS
//==================================================================
CTrade trade;
int hTrendFastMA;
int hTrendSlowMA;
int hEntryMA;
//==================================================================
// INIT
//==================================================================
int OnInit()
{
InitIndicators();
return INIT_SUCCEEDED;
}
void InitIndicators()
{
hTrendFastMA = iMA(_Symbol, TrendTimeframe, TrendFastMAPeriod, 0,
TrendMaType, PRICE_CLOSE);
hTrendSlowMA = iMA(_Symbol, TrendTimeframe, TrendSlowMAPeriod, 0,
TrendMaType, PRICE_CLOSE);
hEntryMA = iMA(_Symbol, EntryTimeframe, EntryMAPeriod, 0,
EntryMaType, PRICE_CLOSE);
}
bool IsNewEntryBarClosed()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, EntryTimeframe, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true; // nến trước vừa đóng
}
return false;
}
//==================================================================
// ON TICK (NO LOGIC HERE)
//==================================================================
void OnTick()
{
if(!IsNewEntryBarClosed())
return;
RunTradingFlow();
}
//==================================================================
// MAIN FLOW
//==================================================================
void RunTradingFlow()
{
if(!IsTradeSessionAllowed()) return;
if(HasOpenPosition()) return;
int trendDirection = DetermineTrendDirection();
if(trendDirection == 0) return;
if(!IsPricePullbackToEntryMA(trendDirection)) return;
if(!IsEngulfingPattern(trendDirection)) return;
ExecuteMarketTrade(trendDirection);
}
//==================================================================
// SESSION
//==================================================================
bool IsTradeSessionAllowed()
{
datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);
return (dt.hour >= TradeStartHour && dt.hour <= TradeEndHour);
}
//==================================================================
// POSITION
//==================================================================
bool HasOpenPosition()
{
return PositionSelect(_Symbol);
}
//==================================================================
// TREND
//==================================================================
int DetermineTrendDirection()
{
double fastMA = GetMAValue(hTrendFastMA, TrendTimeframe, 1);
double slowMA = GetMAValue(hTrendSlowMA, TrendTimeframe, 1);
if(fastMA > slowMA) return 1;
if(fastMA < slowMA) return -1;
return 0;
}
//==================================================================
// ENTRY CONDITIONS
//==================================================================
bool IsPricePullbackToEntryMA(int direction)
{
double price = iClose(_Symbol, EntryTimeframe, 1);
double ma = GetMAValue(hEntryMA, EntryTimeframe, 1);
if(direction == 1 && price <= ma) return true;
if(direction == -1 && price >= ma) return true;
return false;
}
bool IsEngulfingPattern(int direction)
{
double open1 = iOpen (_Symbol, EntryTimeframe, 1);
double close1 = iClose(_Symbol, EntryTimeframe, 1);
double open2 = iOpen (_Symbol, EntryTimeframe, 2);
double close2 = iClose(_Symbol, EntryTimeframe, 2);
if(direction == 1)
return (close1 > open1 && open1 < close2 && close1 > open2);
if(direction == -1)
return (close1 < open1 && open1 > close2 && close1 < open2);
return false;
}
//==================================================================
// TRADE EXECUTION
//==================================================================
void ExecuteMarketTrade(int direction)
{
double entryPrice = (direction == 1)
? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
double stopLoss = (direction == 1)
? iLow (_Symbol, EntryTimeframe, 1)
: iHigh(_Symbol, EntryTimeframe, 1);
double takeProfit = (direction == 1)
? entryPrice + (entryPrice - stopLoss) * RewardRisk
: entryPrice - (stopLoss - entryPrice) * RewardRisk;
double slPoints = MathAbs(entryPrice - stopLoss) / _Point;
double lotSize = CalculateLotSize(slPoints);
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(SlippagePoints);
if(direction == 1)
trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit);
else
trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit);
}
//==================================================================
// UTILITIES
//==================================================================
double GetMAValue(int handle, ENUM_TIMEFRAMES tf, int shift)
{
double buffer[];
if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0)
return 0.0;
return buffer[0];
}
double CalculateLotSize(double slPoints)
{
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);
double lot = riskMoney / (slPoints * tickValue / tickSize);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
return MathMax(minLot, MathMin(lot, maxLot));
}