299 lines
20 KiB
Plaintext
299 lines
20 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| EA_Trend_EMA50_EMA200_M5_M1.mq5 |
|
|
//| Phiên bản: 1.0 |
|
|
//| Mục đích: Expert Advisor thực hiện các quy tắc của bạn: |
|
|
//| - Bộ lọc xu hướng: EMA50 & EMA200 trên M5 (chỉ giao dịch theo hướng)|
|
|
//| - Vào lệnh trên M1 sử dụng: mô hình 2 đáy/2 đỉnh, nến nhấn chìm, kiểm tra vùng |
|
|
//| - Rủi ro mỗi lệnh = 1% equity, tối đa 2 lệnh mở cho cùng symbol |
|
|
//| - Vẽ EMA50 & EMA200 (M5) trên biểu đồ |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Bell Hyperpush"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
#include <Trade/Trade.mqh>
|
|
CTrade trade;
|
|
|
|
// ----- INPUTS -----------------------------------------------------
|
|
input ENUM_TIMEFRAMES TrendTF = PERIOD_M5; // khung thời gian tính EMA50 & EMA200
|
|
input ENUM_TIMEFRAMES EntryTF = PERIOD_M1; // khung thời gian tìm điểm vào lệnh
|
|
input int EMA_Fast = 50; // EMA nhanh
|
|
input int EMA_Slow = 200; // EMA chậm
|
|
input double RiskPercent = 1.0; // rủi ro % trên equity mỗi lệnh
|
|
input double TP_Multiplier = 2.0; // TP = R * TP_Multiplier (0 -> không TP)
|
|
input int MaxOpenPositions = 2; // số lệnh tối đa cùng lúc
|
|
input double DoubleTolerancePoints = 30; // dung sai để nhận 2 đáy/2 đỉnh
|
|
input int MinBarsBetweenDouble = 4; // số nến tối thiểu giữa 2 đáy/2 đỉnh
|
|
input int SL_Pips_Default = 35; // SL mặc định nếu không tính từ pattern
|
|
input int Slippage = 10; // độ trượt giá tối đa
|
|
input bool VisualizeEMAs = true; // vẽ EMA trên biểu đồ
|
|
|
|
// ----- BIẾN TOÀN CỤC -----------------------------------------------
|
|
int handleEMA50 = INVALID_HANDLE;
|
|
int handleEMA200 = INVALID_HANDLE;
|
|
|
|
// ----- HÀM TIỆN ÍCH -----------------------------------------------
|
|
// Đếm số lệnh mở cho symbol hiện tại
|
|
int CountOpenPositionsSymbol() {
|
|
int cnt = 0;
|
|
|
|
for(int i=0; i<PositionsTotal(); i++) {
|
|
if(PositionGetSymbol(i) == _Symbol) cnt++;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
// Kiểm tra nến nhấn chìm tăng trên timeframe tf
|
|
bool IsBullishEngulfing(ENUM_TIMEFRAMES tf) {
|
|
double open1 = iOpen(_Symbol, tf, 1);
|
|
double close1 = iClose(_Symbol, tf, 1);
|
|
double open2 = iOpen(_Symbol, tf, 2);
|
|
double close2 = iClose(_Symbol, tf, 2);
|
|
|
|
if(close2 < open2 && close1 > open1) {
|
|
if(close1 > open2 && open1 < close2) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Kiểm tra nến nhấn chìm giảm trên timeframe tf
|
|
bool IsBearishEngulfing(ENUM_TIMEFRAMES tf) {
|
|
double open1 = iOpen(_Symbol, tf, 1);
|
|
double close1 = iClose(_Symbol, tf, 1);
|
|
double open2 = iOpen(_Symbol, tf, 2);
|
|
double close2 = iClose(_Symbol, tf, 2);
|
|
|
|
if(close2 > open2 && close1 < open1) {
|
|
if(close1 < open2 && open1 > close2) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Phát hiện mô hình 2 đáy trên timeframe tf
|
|
bool DetectDoubleBottom(ENUM_TIMEFRAMES tf,
|
|
int lookbackBars,
|
|
double tolerancePoints,
|
|
double &levelPrice
|
|
) {
|
|
levelPrice = 0.0;
|
|
|
|
for(int i=2; i<lookbackBars-2; i++) {
|
|
double l1 = iLow(_Symbol, tf, i);
|
|
bool isLow1 = (l1 < iLow(_Symbol, tf, i-1) && l1 < iLow(_Symbol, tf, i+1));
|
|
|
|
if(!isLow1) continue;
|
|
|
|
for(int j=i+MinBarsBetweenDouble; j<lookbackBars-1; j++) {
|
|
double l2 = iLow(_Symbol, tf, j);
|
|
bool isLow2 = (l2 < iLow(_Symbol, tf, j-1) && l2 < iLow(_Symbol, tf, j+1));
|
|
|
|
if(!isLow2) continue;
|
|
|
|
double diffPoints = MathAbs(l1 - l2)/_Point;
|
|
|
|
if(diffPoints <= tolerancePoints) {
|
|
levelPrice = MathMin(l1, l2);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Phát hiện mô hình 2 đỉnh trên timeframe tf
|
|
bool DetectDoubleTop(ENUM_TIMEFRAMES tf,
|
|
int lookbackBars,
|
|
double tolerancePoints,
|
|
double &levelPrice
|
|
) {
|
|
levelPrice = 0.0;
|
|
|
|
for(int i=2; i<lookbackBars-2; i++) {
|
|
double h1 = iHigh(_Symbol, tf, i);
|
|
bool isHigh1 = (h1 > iHigh(_Symbol, tf, i-1) && h1 > iHigh(_Symbol, tf, i+1));
|
|
|
|
if(!isHigh1) continue;
|
|
|
|
for(int j=i+MinBarsBetweenDouble; j<lookbackBars-1; j++) {
|
|
double h2 = iHigh(_Symbol, tf, j);
|
|
bool isHigh2 = (h2 > iHigh(_Symbol, tf, j-1) && h2 > iHigh(_Symbol, tf, j+1));
|
|
|
|
if(!isHigh2) continue;
|
|
|
|
double diffPoints = MathAbs(h1 - h2)/_Point;
|
|
|
|
if(diffPoints <= tolerancePoints) {
|
|
levelPrice = MathMax(h1, h2);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Tính khối lượng lệnh dựa trên % rủi ro
|
|
double CalculateLotByRisk(double riskPercent, double stopLossPoints) {
|
|
if(stopLossPoints <= 0) return(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));
|
|
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
double riskMoney = equity * (riskPercent/100.0);
|
|
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
if(tickValue <= 0 || tickSize <= 0)
|
|
{
|
|
double fallback = riskMoney / (stopLossPoints * _Point);
|
|
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
double lot = MathMax(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN), MathFloor(fallback/step)*step);
|
|
|
|
return lot;
|
|
}
|
|
|
|
double valuePerPointFor1Lot = tickValue / tickSize;
|
|
double lots = riskMoney / (stopLossPoints * valuePerPointFor1Lot);
|
|
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
if(step <= 0) step = 0.01;
|
|
|
|
lots = MathFloor(lots/step)*step;
|
|
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
|
|
if(lots < minLot) lots = minLot;
|
|
|
|
return NormalizeDouble(lots,2);
|
|
}
|
|
|
|
// ----- QUYẾT ĐỊNH VÀO LỆNH --------------------------------------
|
|
// Kiểm tra bộ lọc EMA: 1 = Buy allowed, -1 = Sell allowed, 0 = không trade
|
|
int EMAFilter()
|
|
{
|
|
double ema50 = iMA(_Symbol, TrendTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE, 0);
|
|
double ema200 = iMA(_Symbol, TrendTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE, 0);
|
|
|
|
if(ema50 == 0 || ema200 == 0) return 0;
|
|
|
|
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double diff = MathAbs(ema50 - ema200);
|
|
double thresh = 0.001 * price; // tránh vùng nhiễu
|
|
|
|
if(diff < thresh) return 0;
|
|
if(ema50 > ema200) return 1;
|
|
if(ema50 < ema200) return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Thực hiện lệnh thị trường
|
|
bool PlaceMarketOrder(bool isBuy, double lots, double sl, double tp)
|
|
{
|
|
trade.SetExpertMagicNumber(20251008);
|
|
trade.SetDeviationInPoints(Slippage);
|
|
bool ok = false;
|
|
|
|
if(isBuy)
|
|
ok = trade.Buy(lots, NULL, 0, sl, tp, NULL);
|
|
else
|
|
ok = trade.Sell(lots, NULL, 0, sl, tp, NULL);
|
|
|
|
if(!ok) {
|
|
Print("Đặt lệnh thất bại: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----- VÒNG LẶP CHÍNH --------------------------------------------
|
|
void OnTick() {
|
|
static datetime lastEntryTime = 0;
|
|
datetime t = iTime(_Symbol, EntryTF, 0);
|
|
|
|
if(t == lastEntryTime) return;
|
|
lastEntryTime = t;
|
|
|
|
if(CountOpenPositionsSymbol() >= MaxOpenPositions) return;
|
|
|
|
int filter = EMAFilter();
|
|
|
|
if(filter == 0) return;
|
|
|
|
double level=0.0;
|
|
bool buySignal = false; bool sellSignal = false;
|
|
|
|
if(filter == 1 && DetectDoubleBottom(EntryTF, 40, DoubleTolerancePoints, level)) {
|
|
if(IsBullishEngulfing(EntryTF) || iClose(_Symbol, EntryTF, 0) > level)
|
|
buySignal = true;
|
|
}
|
|
|
|
if(filter == -1 && DetectDoubleTop(EntryTF, 40, DoubleTolerancePoints, level))
|
|
{
|
|
if(IsBearishEngulfing(EntryTF) || iClose(_Symbol, EntryTF, 0) < level)
|
|
sellSignal = true;
|
|
}
|
|
|
|
double ema50_trend = iMA(_Symbol, TrendTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE, 0);
|
|
|
|
if(filter == 1 && !buySignal) {
|
|
double lowTouch = iLow(_Symbol, EntryTF, 1);
|
|
if(lowTouch <= ema50_trend && IsBullishEngulfing(EntryTF)) buySignal = true;
|
|
}
|
|
|
|
if(filter == -1 && !sellSignal) {
|
|
double highTouch = iHigh(_Symbol, EntryTF, 1);
|
|
if(highTouch >= ema50_trend && IsBearishEngulfing(EntryTF)) sellSignal = true;
|
|
}
|
|
|
|
if(!buySignal && !sellSignal) return;
|
|
|
|
double entryPrice = (buySignal? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID));
|
|
double slPrice = 0.0; double tpPrice = 0.0;
|
|
double stopLossPoints = SL_Pips_Default;
|
|
|
|
if(level > 0) {
|
|
if(buySignal) {
|
|
slPrice = level - 5*_Point; stopLossPoints = MathAbs(entryPrice - slPrice)/_Point;
|
|
}
|
|
|
|
if(sellSignal){
|
|
slPrice = level + 5*_Point; stopLossPoints = MathAbs(entryPrice - slPrice)/_Point; }
|
|
} else {
|
|
if(buySignal) slPrice = entryPrice - SL_Pips_Default*_Point;
|
|
if(sellSignal) slPrice = entryPrice + SL_Pips_Default*_Point;
|
|
stopLossPoints = SL_Pips_Default;
|
|
}
|
|
|
|
double lots = CalculateLotByRisk(RiskPercent, stopLossPoints);
|
|
|
|
if(TP_Multiplier > 0)
|
|
{
|
|
double riskPriceDiff = MathAbs(entryPrice - slPrice);
|
|
if(buySignal) tpPrice = entryPrice + TP_Multiplier * riskPriceDiff;
|
|
else tpPrice = entryPrice - TP_Multiplier * riskPriceDiff;
|
|
}
|
|
|
|
if(CountOpenPositionsSymbol() >= MaxOpenPositions) return;
|
|
|
|
bool placed = false;
|
|
if(buySignal) placed = PlaceMarketOrder(true, lots, slPrice, tpPrice);
|
|
else if(sellSignal) placed = PlaceMarketOrder(false, lots, slPrice, tpPrice);
|
|
|
|
if(placed) PrintFormat("Đặt lệnh %s lots=%.2f SL=%.5f TP=%.5f", (buySignal?"BUY":"SELL"), lots, slPrice, tpPrice);
|
|
}
|
|
|
|
// ----- KHỞI TẠO & KẾT THÚC -------------------------------------
|
|
int OnInit() {
|
|
handleEMA50 = iMA(_Symbol, TrendTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE);
|
|
handleEMA200 = iMA(_Symbol, TrendTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
if(VisualizeEMAs) {
|
|
if(handleEMA50 != INVALID_HANDLE) ChartIndicatorAdd(0, 0, handleEMA50);
|
|
if(handleEMA200 != INVALID_HANDLE) ChartIndicatorAdd(0, 0, handleEMA200);
|
|
}
|
|
|
|
return(INIT_SUCCEEDED);
|
|
} |