Files
Bell-PriceActionWithEma-EA/Indicators/RsiMomentumIndicator/Lib/SignalScan.mqh
T
2026-05-14 00:54:40 +07:00

163 lines
8.2 KiB
Plaintext

//+------------------------------------------------------------------+
//| SignalScan.mqh |
//| Vòng for phát hiện giao cắt RSI vs WMA45, áp 5 filter: |
//| 1. crossUp / crossDown |
//| 2. trend EMA200 (N nến liên tiếp cùng phía) |
//| 3. EMA9 vs WMA45 (persist N bars) |
//| 4. slope EMA9 cùng chiều tín hiệu |
//| 5. RSI không ở extreme |
//| Sau đó vẽ mũi tên BUY/SELL lên chart. |
//+------------------------------------------------------------------+
#ifndef RSIMOM_SIGNALSCAN_MQH
#define RSIMOM_SIGNALSCAN_MQH
//+------------------------------------------------------------------+
//| Quét signal từ bar `barsToScan` về bar 1, ghi mảng nội bộ |
//| buf_Signal / buf_Trend / buf_EMA200 + vẽ OBJ_ARROW. |
//+------------------------------------------------------------------+
void SignalScan_Run(const int barsToScan,
const int rates_total,
const int need,
const int trendN,
const datetime &timeArr[],
const double &highArr[],
const double &lowArr[],
const double &closeArr[],
const double &ema200Arr[])
{
const double arrowOffset = InpArrowOffsetPts * _Point;
// Reset buffer cho bar 0 (đang hình thành) — đặt giá trị mặc định
buf_Signal[0] = 0.0;
buf_Trend [0] = 0.0;
buf_EMA200[0] = (need > 0) ? ema200Arr[0] : 0.0;
// Phát hiện giao cắt RSI vs WMA45, chỉ vẽ khi khớp với trend EMA200
for (int i = barsToScan; i >= 1; i--)
{
buf_Signal[i] = 0.0; // mặc định: không có tín hiệu
buf_Trend [i] = 0.0;
buf_EMA200[i] = (i < need) ? ema200Arr[i] : 0.0;
if (i + 1 >= rates_total) continue;
if (i + trendN >= need) continue; // ngoài phạm vi mảng cục bộ
if (ema200Arr[i] <= 0.0) continue; // EMA200 chưa tính đủ bars
// Lọc theo trend EMA200: cần N nến LIÊN TIẾP gần nhất đều đóng cùng phía
// Quét từ nến tín hiệu i → i+N-1 (tính LUÔN, kể cả khi không có cross)
bool trendUp = true;
bool trendDown = true;
for (int k = 0; k < trendN; k++)
{
const int idx = i + k;
if (ema200Arr[idx] <= 0.0) { trendUp = false; trendDown = false; break; }
if (closeArr[idx] <= ema200Arr[idx]) trendUp = false;
if (closeArr[idx] >= ema200Arr[idx]) trendDown = false;
if (!trendUp && !trendDown) break;
}
buf_Trend[i] = trendUp ? 1.0 : (trendDown ? -1.0 : 0.0);
const bool crossUp = (buf_RSI[i+1] <= buf_WMA45[i+1]) && (buf_RSI[i] > buf_WMA45[i]);
const bool crossDown = (buf_RSI[i+1] >= buf_WMA45[i+1]) && (buf_RSI[i] < buf_WMA45[i]);
if (!crossUp && !crossDown) continue;
// Vị trí EMA9 vs WMA45 tại nến signal — "lực mua/bán mới bắt đầu chiếm ưu thế"
// BUY: EMA9 vẫn dưới WMA45 (lực mua chỉ vừa nhú lên qua RSI cross)
// SELL: EMA9 vẫn trên WMA45 (lực bán chỉ vừa nhú xuống)
const bool ema9BelowWma = (buf_EMA9[i] < buf_WMA45[i]);
const bool ema9AboveWma = (buf_EMA9[i] > buf_WMA45[i]);
// ── Filter PERSIST (chống nhiễu) ─────────────────────────────────────
// Yêu cầu EMA9 ở CÙNG PHÍA WMA45 trong N nến liên tiếp (i, i+1, …, i+N-1)
// - Tránh trường hợp EMA9 cắt lên/xuống WMA45 liên tục (lực không rõ)
// - Mặc định N=5: EMA9 phải đã ổn định 1 phía ít nhất 5 bar
// - N <= 1 → tắt filter (chỉ check tại bar signal như cũ)
const int persistN = MathMax(1, InpEma9PersistBars);
bool ema9PersistBelow = ema9BelowWma;
bool ema9PersistAbove = ema9AboveWma;
if (persistN > 1)
{
// Cần thêm (persistN - 1) bar trước nến signal cùng phía
// Đảm bảo không vượt phạm vi buffer (i + persistN - 1 < rates_total)
if (i + persistN - 1 >= rates_total)
{
ema9PersistBelow = false;
ema9PersistAbove = false;
}
else
{
for (int k = 1; k < persistN; k++)
{
if (buf_EMA9[i+k] >= buf_WMA45[i+k]) ema9PersistBelow = false;
if (buf_EMA9[i+k] <= buf_WMA45[i+k]) ema9PersistAbove = false;
if (!ema9PersistBelow && !ema9PersistAbove) break;
}
}
}
// Slope EMA9 — phải đang hướng cùng chiều với tín hiệu
const bool ema9SlopeUp = (buf_EMA9[i] > buf_EMA9[i+1]);
const bool ema9SlopeDown = (buf_EMA9[i] < buf_EMA9[i+1]);
// Lọc RSI extreme — không mua đỉnh, không bán đáy
// RSI ≥ overbought → bỏ tín hiệu BUY
// RSI ≤ oversold → bỏ tín hiệu SELL
const bool rsiOkBuy = (buf_RSI[i] < InpRSIOverbought);
const bool rsiOkSell = (buf_RSI[i] > InpRSIOversold);
// Tín hiệu hợp lệ khi đủ 6 điều kiện (thêm filter PERSIST)
const bool validBuy = crossUp && trendUp && ema9PersistBelow && ema9SlopeUp && rsiOkBuy;
const bool validSell = crossDown && trendDown && ema9PersistAbove && ema9SlopeDown && rsiOkSell;
if (!validBuy && !validSell) continue;
// Dùng timestamp làm tên duy nhất để tránh tạo trùng object
const string arrowName = OBJ_PREFIX + "CR_" + IntegerToString((int)timeArr[i]);
if (ObjectFind(0, arrowName) >= 0) continue;
if (validBuy)
{
buf_Signal[i] = 1.0;
// Mũi tên lên (↑) đặt dưới đáy nến — tín hiệu BUY trong uptrend
// ANCHOR_TOP: điểm anchor là đỉnh icon → arrow nằm xuôi xuống dưới price
// → tip ↑ ở phía trên, sát đáy nến (cách lowArr[i] một khoảng arrowOffset)
const double price = lowArr[i] - arrowOffset;
ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeArr[i], price);
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 233);
ObjectSetInteger(0, arrowName, OBJPROP_ANCHOR, ANCHOR_TOP);
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpArrowUpColor);
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, InpArrowSize);
ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, arrowName, OBJPROP_HIDDEN, true);
ObjectSetString (0, arrowName, OBJPROP_TOOLTIP,
StringFormat("BUY signal\n%s\nRSI=%.2f EMA9=%.2f (slope+) WMA45=%.2f\nEMA200=%.5f (Uptrend)\nEMA9 below WMA45 ≥ %d bars",
TimeToString(timeArr[i], TIME_DATE|TIME_MINUTES),
buf_RSI[i], buf_EMA9[i], buf_WMA45[i], ema200Arr[i], persistN));
}
else // validSell
{
buf_Signal[i] = -1.0;
// Mũi tên xuống (↓) đặt trên đỉnh nến — tín hiệu SELL trong downtrend
// ANCHOR_BOTTOM: điểm anchor là đáy icon → arrow nằm ngược lên trên price
// → tip ↓ ở phía dưới, sát đỉnh nến (cách highArr[i] một khoảng arrowOffset)
const double price = highArr[i] + arrowOffset;
ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeArr[i], price);
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 234);
ObjectSetInteger(0, arrowName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpArrowDownColor);
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, InpArrowSize);
ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, arrowName, OBJPROP_HIDDEN, true);
ObjectSetString (0, arrowName, OBJPROP_TOOLTIP,
StringFormat("SELL signal\n%s\nRSI=%.2f EMA9=%.2f (slope-) WMA45=%.2f\nEMA200=%.5f (Downtrend)\nEMA9 above WMA45 ≥ %d bars",
TimeToString(timeArr[i], TIME_DATE|TIME_MINUTES),
buf_RSI[i], buf_EMA9[i], buf_WMA45[i], ema200Arr[i], persistN));
}
}
}
#endif // RSIMOM_SIGNALSCAN_MQH