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

199 lines
8.7 KiB
Plaintext

//+------------------------------------------------------------------+
//| RsiMomentumIndicator.mq5 |
//| Chỉ để hiển thị + trade thủ công: cửa sổ phụ RSI/EMA9/WMA45, |
//| mũi tên + panel + cảnh báo entry. Không export buffer cho EA. |
//| |
//| Source code được tách thành các module trong thư mục Lib/: |
//| Inputs.mqh — input parameters |
//| State.mqh — buffers, handles, constants, alert state |
//| Handles.mqh — tạo/giải phóng indicator handle |
//| Panel.mqh — panel thông tin góc trên-phải |
//| Alerts.mqh — Push/Popup/Sound/Email khi có entry |
//| SignalScan.mqh — phát hiện cross + vẽ arrow |
//| Diagnostics.mqh — log chẩn đoán lần đầu |
//+------------------------------------------------------------------+
#property copyright "RsiMomentumIndicator"
#property version "1.20"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_minimum 0
#property indicator_maximum 100
// --- Buffer 0: RSI14 ---
#property indicator_label1 "RSI(14)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumOrchid
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
// --- Buffer 1: EMA9 trên RSI ---
#property indicator_label2 "EMA9 on RSI"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrDarkOrange
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
// --- Buffer 2: WMA45 trên RSI ---
#property indicator_label3 "WMA45 on RSI"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrDodgerBlue
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
//+------------------------------------------------------------------+
//| Modules — thứ tự include theo dependency |
//+------------------------------------------------------------------+
#include "Lib/Inputs.mqh"
#include "Lib/State.mqh"
#include "Lib/Handles.mqh"
#include "Lib/Panel.mqh"
#include "Lib/Alerts.mqh"
#include "Lib/SignalScan.mqh"
#include "Lib/Diagnostics.mqh"
//+------------------------------------------------------------------+
//| Khởi tạo indicator |
//+------------------------------------------------------------------+
int OnInit()
{
// Reset state notification (tránh re-fire alert cũ khi đổi TF / reload)
g_lastAlertBuyBar = 0;
g_lastAlertSellBar = 0;
g_firstCalc = true;
// Đăng ký buffer — đặt AS_SERIES để [0] = nến hiện tại
SetIndexBuffer(0, buf_RSI, INDICATOR_DATA);
SetIndexBuffer(1, buf_EMA9, INDICATOR_DATA);
SetIndexBuffer(2, buf_WMA45, INDICATOR_DATA);
ArraySetAsSeries(buf_RSI, true);
ArraySetAsSeries(buf_EMA9, true);
ArraySetAsSeries(buf_WMA45, true);
ArraySetAsSeries(buf_Signal, true);
ArraySetAsSeries(buf_EMA200, true);
ArraySetAsSeries(buf_Trend, true);
IndicatorSetString (INDICATOR_SHORTNAME, StringFormat("RsiMom(%d)", InpRSIPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
if (!Handles_CreateAll()) return INIT_FAILED;
Panel_CreateAll();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Dọn dẹp khi gỡ indicator |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Handles_ReleaseAll();
// Xóa tất cả object do indicator tạo ra (mũi tên + panel)
ObjectsDeleteAll(0, OBJ_PREFIX);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Tính toán chỉ báo mỗi tick |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
const int minBars = InpWMA45Period + InpRSIPeriod + 5;
if (rates_total < minBars) return 0;
// Mảng nội bộ (alert / diagnostics) — không bind buffer nên tự resize
ArrayResize(buf_Signal, rates_total);
ArrayResize(buf_EMA200, rates_total);
ArrayResize(buf_Trend, rates_total);
ArraySetAsSeries(buf_Signal, true);
ArraySetAsSeries(buf_EMA200, true);
ArraySetAsSeries(buf_Trend, true);
// Đảm bảo các source indicator đã tính xong (quan trọng khi cold-start
// hoặc khi thị trường đóng — không có tick để retry)
const int rsiBars = BarsCalculated(h_RSI);
const int ema9Bars = BarsCalculated(h_EMA9);
const int wmaBars = BarsCalculated(h_WMA45);
const int ema200Bars = BarsCalculated(h_EMA200);
if (rsiBars <= 0 || ema9Bars <= 0 || wmaBars <= 0 || ema200Bars <= 0)
{
static datetime lastWarn = 0;
if (TimeCurrent() - lastWarn > 30)
{
PrintFormat("[RsiMom] Source not ready yet: RSI=%d EMA9=%d WMA45=%d EMA200=%d (rates_total=%d)",
rsiBars, ema9Bars, wmaBars, ema200Bars, rates_total);
lastWarn = TimeCurrent();
}
return 0; // chưa có dữ liệu — return 0 để MT5 retry ngay
}
// Chỉ copy số bar mà TẤT CẢ source (incl. EMA200) đã tính xong
// → tránh CopyBuffer EMA200 fail giữa chừng làm mảng nội bộ stale
const int srcMin = MathMin(MathMin(MathMin(rsiBars, ema9Bars), wmaBars), ema200Bars);
const int copyN = MathMin(srcMin, rates_total);
if (copyN < minBars) return 0;
// Đổ dữ liệu vào indicator buffer (AS_SERIES=true, [0]=nến hiện tại)
if (CopyBuffer(h_RSI, 0, 0, copyN, buf_RSI) <= 0) return 0;
if (CopyBuffer(h_EMA9, 0, 0, copyN, buf_EMA9) <= 0) return 0;
if (CopyBuffer(h_WMA45, 0, 0, copyN, buf_WMA45) <= 0) return 0;
// Số nến cần quét để phát hiện giao cắt mới
int barsToScan = (prev_calculated == 0)
? rates_total - 2
: (rates_total - prev_calculated + 2);
barsToScan = MathMin(barsToScan, rates_total - 2);
// Sao chép time, high, low, close và EMA200 để phát hiện giao cắt + lọc trend
// +InpTrendConfirmBars vì cần check N nến liên tiếp đóng cùng phía EMA200
// CLAMP need ≤ copyN để CopyTime/CopyClose không bao giờ "thiếu" — first call
// có barsToScan = rates_total-2 sẽ cho need lớn hơn rates_total nếu không clamp.
const int trendN = MathMax(1, InpTrendConfirmBars);
const int need = MathMin(barsToScan + 2 + trendN, copyN);
datetime timeArr[];
double highArr[], lowArr[], closeArr[], ema200Arr[];
ArraySetAsSeries(timeArr, true);
ArraySetAsSeries(highArr, true);
ArraySetAsSeries(lowArr, true);
ArraySetAsSeries(closeArr, true);
ArraySetAsSeries(ema200Arr, true);
if (CopyTime (_Symbol, _Period, 0, need, timeArr) < need) return prev_calculated;
if (CopyHigh (_Symbol, _Period, 0, need, highArr) < need) return prev_calculated;
if (CopyLow (_Symbol, _Period, 0, need, lowArr) < need) return prev_calculated;
if (CopyClose (_Symbol, _Period, 0, need, closeArr) < need) return prev_calculated;
if (CopyBuffer(h_EMA200, 0, 0, need, ema200Arr) < need) return prev_calculated;
// ── Phát hiện cross + vẽ arrow ───────────────────────────────────
SignalScan_Run(barsToScan, rates_total, need, trendN,
timeArr, highArr, lowArr, closeArr, ema200Arr);
// ── Bắn alert nếu có signal MỚI trên bar đã đóng ─────────────────
Alerts_CheckAndFire(timeArr, closeArr, ema200Arr, need, rates_total);
// ── Cập nhật panel thông tin (góc trên-phải) ─────────────────────
Panel_Update(closeArr, ema200Arr, trendN, rates_total);
ChartRedraw(0);
// ── Diagnostic: log lần đầu OnCalculate hoàn thành full ──────────
Diagnostics_FirstPass(rates_total, copyN, trendN, need, closeArr);
// Return số bar thật sự đã xử lý — nếu copyN < rates_total thì lần sau MT5
// sẽ scan tiếp phần còn lại (do chênh prev_calculated)
return copyN;
}
//+------------------------------------------------------------------+