223 lines
14 KiB
Plaintext
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));
|
|
}
|