diff --git a/Indicators/MyIndicators/ADX.md b/Indicators/MyIndicators/ADX.md index e46d7ea..cdcbb4f 100644 --- a/Indicators/MyIndicators/ADX.md +++ b/Indicators/MyIndicators/ADX.md @@ -1,91 +1,91 @@ -# Average Directional Index (ADX) - -## 1. Summary (Introduction) - -The Average Directional Index (ADX), developed by J. Welles Wilder, is a widely used technical indicator designed to measure the **strength of a trend**, regardless of its direction. It does not indicate whether the trend is bullish or bearish, but only quantifies its momentum. - -The ADX system consists of three lines: - -- **ADX Line:** The main line that indicates trend strength. -- **+DI (Positive Directional Indicator):** A line that measures the strength of the upward price movement. -- **-DI (Negative Directional Indicator):** A line that measures the strength of the downward price movement. - -It is a powerful tool for traders to distinguish between trending and non-trending (ranging) market conditions. - -## 2. Mathematical Foundations and Calculation Logic - -The ADX calculation is a complex, multi-stage process that relies heavily on Wilder's smoothing technique (a specific type of Smoothed or Running Moving Average - SMMA/RMA). - -### Required Components - -- **ADX Period (N):** The lookback period for all calculations (e.g., 14). -- **Directional Movement (+DM, -DM):** Measures the portion of the current bar's range that is outside the previous bar's range. -- **True Range (TR):** The standard measure of a single bar's volatility. - -### Calculation Steps (Algorithm) - -1. **Calculate Directional Movement and True Range:** For each period, calculate: - - - $\text{Up Move} = \text{High}_i - \text{High}_{i-1}$ - - $\text{Down Move} = \text{Low}_{i-1} - \text{Low}_i$ - - If $\text{Up Move} > \text{Down Move}$ and $\text{Up Move} > 0$, then $\text{+DM} = \text{Up Move}$, else $\text{+DM} = 0$. - - If $\text{Down Move} > \text{Up Move}$ and $\text{Down Move} > 0$, then $\text{-DM} = \text{Down Move}$, else $\text{-DM} = 0$. - - $\text{True Range (TR)} = \text{Max}[(\text{High}_i - \text{Low}_i), \text{Abs}(\text{High}_i - \text{Close}_{i-1}), \text{Abs}(\text{Low}_i - \text{Close}_{i-1})]$ - -2. **Smooth +DM, -DM, and TR:** Apply Wilder's smoothing method over the period `N`. - - - **Initialization:** The first value is the sum of the first `N` periods. - $\text{Smoothed +DM}_{N} = \sum_{i=1}^{N} \text{+DM}_i$ - - **Recursive Calculation:** - $\text{Smoothed +DM}_i = \text{Smoothed +DM}_{i-1} - \frac{\text{Smoothed +DM}_{i-1}}{N} + \text{+DM}_i$ - - _(The same logic applies to -DM and TR)_ - -3. **Calculate Directional Indicators (+DI, -DI):** - $\text{+DI}_i = 100 \times \frac{\text{Smoothed +DM}_i}{\text{Smoothed TR}_i}$ - $\text{-DI}_i = 100 \times \frac{\text{Smoothed -DM}_i}{\text{Smoothed TR}_i}$ - -4. **Calculate the Directional Index (DX):** - $\text{DX}_i = 100 \times \frac{\text{Abs}(\text{+DI}_i - \text{-DI}_i)}{\text{+DI}_i + \text{-DI}_i}$ - -5. **Calculate the Final ADX:** The ADX is a Wilder-smoothed moving average of the DX. - - **Initialization:** The first ADX value is a simple average of the first `N` DX values. - - **Recursive Calculation:** - $\text{ADX}_i = \frac{(\text{ADX}_{i-1} \times (N-1)) + \text{DX}_i}{N}$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored to be highly robust, clear, and consistent with our established "Wilder Algorithm". - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a complex, multi-stage indicator like the ADX, this is the most reliable method to prevent calculation errors. - -- **Consensus Wilder Algorithm:** The implementation strictly follows our established two-step algorithm for Wilder's smoothing: - - 1. **Robust Initialization:** The first smoothed value is calculated non-recursively (as a simple sum for `+DM`, `-DM`, `TR`, and as a simple average for `ADX`). - 2. **Efficient Recursive Calculation:** All subsequent values are calculated using the efficient formula: `Previous Value - (Previous Value / N) + Current Value`. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the complex logic easy to follow: - - 1. **Step 1:** Raw `+DM`, `-DM`, and `TR` values are calculated and stored in temporary arrays. - 2. **Step 2:** The raw values are smoothed using our Wilder algorithm. - 3. **Step 3:** The `+DI`, `-DI`, and `DX` values are calculated from the smoothed data. - 4. **Step 4:** The final `ADX` line is calculated by applying the Wilder algorithm to the `DX` values. - -- **Heikin Ashi Variant (`ADX_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. - - This results in a smoother ADX system that reflects the momentum of the underlying Heikin Ashi trend, effectively filtering out some of the market noise that can cause the +DI and -DI lines to cross frequently. - -## 4. Parameters - -- **ADX Period (`InpPeriodADX`):** The lookback period used for all internal calculations (+DM, -DM, TR, and the final ADX smoothing). Wilder's original recommendation and the most common value is `14`. - -## 5. Usage and Interpretation - -- **Trend Strength:** The primary signal is the ADX line itself. - - **ADX < 25:** Weak or non-existent trend (ranging market). Trend-following strategies should be avoided. - - **ADX > 25:** Strong trend. The higher the ADX, the stronger the trend. - - **Rising ADX:** The trend is gaining strength. - - **Falling ADX:** The trend is losing strength. -- **Trend Direction (+DI and -DI Crossover):** - - When the **+DI line (green) crosses above the -DI line (red)**, it suggests the start of a bullish trend. - - When the **-DI line (red) crosses above the +DI line (green)**, it suggests the start of a bearish trend. -- **Trade Confirmation:** A common strategy is to wait for a +DI/-DI crossover and then confirm that the ADX line is above 25 (or rising) before entering a trade. This helps to filter out signals that occur in weak or non-trending markets. +# Average Directional Index (ADX) + +## 1. Summary (Introduction) + +The Average Directional Index (ADX), developed by J. Welles Wilder, is a widely used technical indicator designed to measure the **strength of a trend**, regardless of its direction. It does not indicate whether the trend is bullish or bearish, but only quantifies its momentum. + +The ADX system consists of three lines: + +- **ADX Line:** The main line that indicates trend strength. +- **+DI (Positive Directional Indicator):** A line that measures the strength of the upward price movement. +- **-DI (Negative Directional Indicator):** A line that measures the strength of the downward price movement. + +It is a powerful tool for traders to distinguish between trending and non-trending (ranging) market conditions. + +## 2. Mathematical Foundations and Calculation Logic + +The ADX calculation is a complex, multi-stage process that relies heavily on Wilder's smoothing technique (a specific type of Smoothed or Running Moving Average - SMMA/RMA). + +### Required Components + +- **ADX Period (N):** The lookback period for all calculations (e.g., 14). +- **Directional Movement (+DM, -DM):** Measures the portion of the current bar's range that is outside the previous bar's range. +- **True Range (TR):** The standard measure of a single bar's volatility. + +### Calculation Steps (Algorithm) + +1. **Calculate Directional Movement and True Range:** For each period, calculate: + + - $\text{Up Move} = \text{High}_i - \text{High}_{i-1}$ + - $\text{Down Move} = \text{Low}_{i-1} - \text{Low}_i$ + - If $\text{Up Move} > \text{Down Move}$ and $\text{Up Move} > 0$, then $\text{+DM} = \text{Up Move}$, else $\text{+DM} = 0$. + - If $\text{Down Move} > \text{Up Move}$ and $\text{Down Move} > 0$, then $\text{-DM} = \text{Down Move}$, else $\text{-DM} = 0$. + - $\text{True Range (TR)} = \text{Max}[(\text{High}_i - \text{Low}_i), \text{Abs}(\text{High}_i - \text{Close}_{i-1}), \text{Abs}(\text{Low}_i - \text{Close}_{i-1})]$ + +2. **Smooth +DM, -DM, and TR:** Apply Wilder's smoothing method over the period `N`. + + - **Initialization:** The first value is the sum of the first `N` periods. + $\text{Smoothed +DM}_{N} = \sum_{i=1}^{N} \text{+DM}_i$ + - **Recursive Calculation:** + $\text{Smoothed +DM}_i = \text{Smoothed +DM}_{i-1} - \frac{\text{Smoothed +DM}_{i-1}}{N} + \text{+DM}_i$ + - _(The same logic applies to -DM and TR)_ + +3. **Calculate Directional Indicators (+DI, -DI):** + $\text{+DI}_i = 100 \times \frac{\text{Smoothed +DM}_i}{\text{Smoothed TR}_i}$ + $\text{-DI}_i = 100 \times \frac{\text{Smoothed -DM}_i}{\text{Smoothed TR}_i}$ + +4. **Calculate the Directional Index (DX):** + $\text{DX}_i = 100 \times \frac{\text{Abs}(\text{+DI}_i - \text{-DI}_i)}{\text{+DI}_i + \text{-DI}_i}$ + +5. **Calculate the Final ADX:** The ADX is a Wilder-smoothed moving average of the DX. + - **Initialization:** The first ADX value is a simple average of the first `N` DX values. + - **Recursive Calculation:** + $\text{ADX}_i = \frac{(\text{ADX}_{i-1} \times (N-1)) + \text{DX}_i}{N}$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored to be highly robust, clear, and consistent with our established "Wilder Algorithm". + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a complex, multi-stage indicator like the ADX, this is the most reliable method to prevent calculation errors. + +- **Consensus Wilder Algorithm:** The implementation strictly follows our established two-step algorithm for Wilder's smoothing: + + 1. **Robust Initialization:** The first smoothed value is calculated non-recursively (as a simple sum for `+DM`, `-DM`, `TR`, and as a simple average for `ADX`). + 2. **Efficient Recursive Calculation:** All subsequent values are calculated using the efficient formula: `Previous Value - (Previous Value / N) + Current Value`. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the complex logic easy to follow: + + 1. **Step 1:** Raw `+DM`, `-DM`, and `TR` values are calculated and stored in temporary arrays. + 2. **Step 2:** The raw values are smoothed using our Wilder algorithm. + 3. **Step 3:** The `+DI`, `-DI`, and `DX` values are calculated from the smoothed data. + 4. **Step 4:** The final `ADX` line is calculated by applying the Wilder algorithm to the `DX` values. + +- **Heikin Ashi Variant (`ADX_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. + - This results in a smoother ADX system that reflects the momentum of the underlying Heikin Ashi trend, effectively filtering out some of the market noise that can cause the +DI and -DI lines to cross frequently. + +## 4. Parameters + +- **ADX Period (`InpPeriodADX`):** The lookback period used for all internal calculations (+DM, -DM, TR, and the final ADX smoothing). Wilder's original recommendation and the most common value is `14`. + +## 5. Usage and Interpretation + +- **Trend Strength:** The primary signal is the ADX line itself. + - **ADX < 25:** Weak or non-existent trend (ranging market). Trend-following strategies should be avoided. + - **ADX > 25:** Strong trend. The higher the ADX, the stronger the trend. + - **Rising ADX:** The trend is gaining strength. + - **Falling ADX:** The trend is losing strength. +- **Trend Direction (+DI and -DI Crossover):** + - When the **+DI line (green) crosses above the -DI line (red)**, it suggests the start of a bullish trend. + - When the **-DI line (red) crosses above the +DI line (green)**, it suggests the start of a bearish trend. +- **Trade Confirmation:** A common strategy is to wait for a +DI/-DI crossover and then confirm that the ADX line is above 25 (or rising) before entering a trade. This helps to filter out signals that occur in weak or non-trending markets. diff --git a/Indicators/MyIndicators/ADX.mq5 b/Indicators/MyIndicators/ADX.mq5 index 16a1cf0..7615ec3 100644 --- a/Indicators/MyIndicators/ADX.mq5 +++ b/Indicators/MyIndicators/ADX.mq5 @@ -1,179 +1,179 @@ -//+------------------------------------------------------------------+ -//| ADX.mq5 | -//| Copyright 2025, xxxxxxxx (Based on MetaQuotes ADXW) | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "1.00" -#property description "ADX by Welles Wilder on standard price data." - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 7 // 3 for plotting, 4 for calculations -#property indicator_plots 3 - -//--- Plot 1: ADX line (Main trend strength) -#property indicator_label1 "ADX" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: +DI line (Positive Directional Indicator) -#property indicator_label2 "+DI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrLimeGreen -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Plot 3: -DI line (Negative Directional Indicator) -#property indicator_label3 "-DI" -#property indicator_type3 DRAW_LINE -#property indicator_color3 clrTomato -#property indicator_style3 STYLE_DOT -#property indicator_width3 1 - -//--- Input Parameters --- -input int InpPeriodADX = 14; // Period for ADX calculations - -//--- Indicator Buffers --- -double BufferADX[]; -double BufferPDI[]; -double BufferNDI[]; -double BufferSmoothed_PDM[]; -double BufferSmoothed_NDM[]; -double BufferSmoothed_TR[]; -double BufferDX[]; - -//--- Global Objects and Variables --- -int g_ExtADXPeriod; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtADXPeriod = (InpPeriodADX < 1) ? 1 : InpPeriodADX; - - SetIndexBuffer(0, BufferADX, INDICATOR_DATA); - SetIndexBuffer(1, BufferPDI, INDICATOR_DATA); - SetIndexBuffer(2, BufferNDI, INDICATOR_DATA); - SetIndexBuffer(3, BufferSmoothed_PDM, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferSmoothed_NDM, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferSmoothed_TR, INDICATOR_CALCULATIONS); - SetIndexBuffer(6, BufferDX, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferADX, false); - ArraySetAsSeries(BufferPDI, false); - ArraySetAsSeries(BufferNDI, false); - ArraySetAsSeries(BufferSmoothed_PDM, false); - ArraySetAsSeries(BufferSmoothed_NDM, false); - ArraySetAsSeries(BufferSmoothed_TR, false); - ArraySetAsSeries(BufferDX, false); - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtADXPeriod * 2 - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtADXPeriod); - PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtADXPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ADXW(%d)", g_ExtADXPeriod)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < g_ExtADXPeriod * 2) - return(0); - -//--- STEP 1: Calculate raw +DM, -DM, and TR from standard prices - double pDM[], nDM[], TR[]; - ArrayResize(pDM, rates_total); - ArrayResize(nDM, rates_total); - ArrayResize(TR, rates_total); - - for(int i = 1; i < rates_total; i++) - { - pDM[i] = high[i] - high[i-1]; - nDM[i] = low[i-1] - low[i]; - - if(pDM[i] < 0 || pDM[i] < nDM[i]) - pDM[i] = 0; - if(nDM[i] < 0 || nDM[i] < pDM[i]) - nDM[i] = 0; - - TR[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); - } - -//--- STEP 2: Calculate Smoothed PDM, NDM, and TR - for(int i = g_ExtADXPeriod; i < rates_total; i++) - { - if(i == g_ExtADXPeriod) // First calculation is a simple sum - { - double sum_pdm=0, sum_ndm=0, sum_tr=0; - for(int j=1; j<=g_ExtADXPeriod; j++) - { - sum_pdm += pDM[j]; - sum_ndm += nDM[j]; - sum_tr += TR[j]; - } - BufferSmoothed_PDM[i] = sum_pdm; - BufferSmoothed_NDM[i] = sum_ndm; - BufferSmoothed_TR[i] = sum_tr; - } - else // Subsequent calculations use Wilder's smoothing - { - BufferSmoothed_PDM[i] = BufferSmoothed_PDM[i-1] - (BufferSmoothed_PDM[i-1] / g_ExtADXPeriod) + pDM[i]; - BufferSmoothed_NDM[i] = BufferSmoothed_NDM[i-1] - (BufferSmoothed_NDM[i-1] / g_ExtADXPeriod) + nDM[i]; - BufferSmoothed_TR[i] = BufferSmoothed_TR[i-1] - (BufferSmoothed_TR[i-1] / g_ExtADXPeriod) + TR[i]; - } - } - -//--- STEP 3: Calculate +DI, -DI, and DX - for(int i = g_ExtADXPeriod; i < rates_total; i++) - { - if(BufferSmoothed_TR[i] != 0.0) - { - BufferPDI[i] = (BufferSmoothed_PDM[i] / BufferSmoothed_TR[i]) * 100.0; - BufferNDI[i] = (BufferSmoothed_NDM[i] / BufferSmoothed_TR[i]) * 100.0; - } - - double di_sum = BufferPDI[i] + BufferNDI[i]; - if(di_sum != 0.0) - BufferDX[i] = MathAbs(BufferPDI[i] - BufferNDI[i]) / di_sum * 100.0; - else - BufferDX[i] = 0.0; - } - -//--- STEP 4: Smooth DX to get the final ADX value - for(int i = g_ExtADXPeriod * 2 - 1; i < rates_total; i++) - { - if(i == g_ExtADXPeriod * 2 - 1) // First ADX value is a simple average - { - double sum_dx = 0; - for(int j=i-g_ExtADXPeriod+1; j<=i; j++) - sum_dx += BufferDX[j]; - BufferADX[i] = sum_dx / g_ExtADXPeriod; - } - else // Subsequent ADX values are smoothed - { - BufferADX[i] = (BufferADX[i-1] * (g_ExtADXPeriod - 1) + BufferDX[i]) / g_ExtADXPeriod; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ADX.mq5 | +//| Copyright 2025, xxxxxxxx (Based on MetaQuotes ADXW) | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "1.00" +#property description "ADX by Welles Wilder on standard price data." + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 7 // 3 for plotting, 4 for calculations +#property indicator_plots 3 + +//--- Plot 1: ADX line (Main trend strength) +#property indicator_label1 "ADX" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: +DI line (Positive Directional Indicator) +#property indicator_label2 "+DI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrLimeGreen +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Plot 3: -DI line (Negative Directional Indicator) +#property indicator_label3 "-DI" +#property indicator_type3 DRAW_LINE +#property indicator_color3 clrTomato +#property indicator_style3 STYLE_DOT +#property indicator_width3 1 + +//--- Input Parameters --- +input int InpPeriodADX = 14; // Period for ADX calculations + +//--- Indicator Buffers --- +double BufferADX[]; +double BufferPDI[]; +double BufferNDI[]; +double BufferSmoothed_PDM[]; +double BufferSmoothed_NDM[]; +double BufferSmoothed_TR[]; +double BufferDX[]; + +//--- Global Objects and Variables --- +int g_ExtADXPeriod; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtADXPeriod = (InpPeriodADX < 1) ? 1 : InpPeriodADX; + + SetIndexBuffer(0, BufferADX, INDICATOR_DATA); + SetIndexBuffer(1, BufferPDI, INDICATOR_DATA); + SetIndexBuffer(2, BufferNDI, INDICATOR_DATA); + SetIndexBuffer(3, BufferSmoothed_PDM, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferSmoothed_NDM, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferSmoothed_TR, INDICATOR_CALCULATIONS); + SetIndexBuffer(6, BufferDX, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferADX, false); + ArraySetAsSeries(BufferPDI, false); + ArraySetAsSeries(BufferNDI, false); + ArraySetAsSeries(BufferSmoothed_PDM, false); + ArraySetAsSeries(BufferSmoothed_NDM, false); + ArraySetAsSeries(BufferSmoothed_TR, false); + ArraySetAsSeries(BufferDX, false); + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtADXPeriod * 2 - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtADXPeriod); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtADXPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ADXW(%d)", g_ExtADXPeriod)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total < g_ExtADXPeriod * 2) + return(0); + +//--- STEP 1: Calculate raw +DM, -DM, and TR from standard prices + double pDM[], nDM[], TR[]; + ArrayResize(pDM, rates_total); + ArrayResize(nDM, rates_total); + ArrayResize(TR, rates_total); + + for(int i = 1; i < rates_total; i++) + { + pDM[i] = high[i] - high[i-1]; + nDM[i] = low[i-1] - low[i]; + + if(pDM[i] < 0 || pDM[i] < nDM[i]) + pDM[i] = 0; + if(nDM[i] < 0 || nDM[i] < pDM[i]) + nDM[i] = 0; + + TR[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); + } + +//--- STEP 2: Calculate Smoothed PDM, NDM, and TR + for(int i = g_ExtADXPeriod; i < rates_total; i++) + { + if(i == g_ExtADXPeriod) // First calculation is a simple sum + { + double sum_pdm=0, sum_ndm=0, sum_tr=0; + for(int j=1; j<=g_ExtADXPeriod; j++) + { + sum_pdm += pDM[j]; + sum_ndm += nDM[j]; + sum_tr += TR[j]; + } + BufferSmoothed_PDM[i] = sum_pdm; + BufferSmoothed_NDM[i] = sum_ndm; + BufferSmoothed_TR[i] = sum_tr; + } + else // Subsequent calculations use Wilder's smoothing + { + BufferSmoothed_PDM[i] = BufferSmoothed_PDM[i-1] - (BufferSmoothed_PDM[i-1] / g_ExtADXPeriod) + pDM[i]; + BufferSmoothed_NDM[i] = BufferSmoothed_NDM[i-1] - (BufferSmoothed_NDM[i-1] / g_ExtADXPeriod) + nDM[i]; + BufferSmoothed_TR[i] = BufferSmoothed_TR[i-1] - (BufferSmoothed_TR[i-1] / g_ExtADXPeriod) + TR[i]; + } + } + +//--- STEP 3: Calculate +DI, -DI, and DX + for(int i = g_ExtADXPeriod; i < rates_total; i++) + { + if(BufferSmoothed_TR[i] != 0.0) + { + BufferPDI[i] = (BufferSmoothed_PDM[i] / BufferSmoothed_TR[i]) * 100.0; + BufferNDI[i] = (BufferSmoothed_NDM[i] / BufferSmoothed_TR[i]) * 100.0; + } + + double di_sum = BufferPDI[i] + BufferNDI[i]; + if(di_sum != 0.0) + BufferDX[i] = MathAbs(BufferPDI[i] - BufferNDI[i]) / di_sum * 100.0; + else + BufferDX[i] = 0.0; + } + +//--- STEP 4: Smooth DX to get the final ADX value + for(int i = g_ExtADXPeriod * 2 - 1; i < rates_total; i++) + { + if(i == g_ExtADXPeriod * 2 - 1) // First ADX value is a simple average + { + double sum_dx = 0; + for(int j=i-g_ExtADXPeriod+1; j<=i; j++) + sum_dx += BufferDX[j]; + BufferADX[i] = sum_dx / g_ExtADXPeriod; + } + else // Subsequent ADX values are smoothed + { + BufferADX[i] = (BufferADX[i-1] * (g_ExtADXPeriod - 1) + BufferDX[i]) / g_ExtADXPeriod; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ADX_HeikinAshi.mq5 b/Indicators/MyIndicators/ADX_HeikinAshi.mq5 index 7288274..9adfaa0 100644 --- a/Indicators/MyIndicators/ADX_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/ADX_HeikinAshi.mq5 @@ -1,232 +1,232 @@ -//+------------------------------------------------------------------+ -//| ADX_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx (Based on MetaQuotes ADXW) | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "4.00" // Refactored for full recalculation and stability -#property description "ADX by Welles Wilder on Heikin Ashi data." - -// --- Standard and Custom Includes --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 7 // 3 for plotting, 4 for calculations -#property indicator_plots 3 - -//--- Plot 1: ADX line (Main trend strength) -#property indicator_label1 "HA_ADX" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: +DI line (Positive Directional Indicator) -#property indicator_label2 "HA_+DI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrLimeGreen -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Plot 3: -DI line (Negative Directional Indicator) -#property indicator_label3 "HA_-DI" -#property indicator_type3 DRAW_LINE -#property indicator_color3 clrTomato -#property indicator_style3 STYLE_DOT -#property indicator_width3 1 - -//--- Input Parameters --- -input int InpPeriodADX = 14; // Period for ADX calculations - -//--- Indicator Buffers --- -double BufferHA_ADX[]; -double BufferHA_PDI[]; -double BufferHA_NDI[]; -double BufferSmoothed_PDM[]; -double BufferSmoothed_NDM[]; -double BufferSmoothed_TR[]; -double BufferDX[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtADXPeriod; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store the ADX period - g_ExtADXPeriod = (InpPeriodADX < 1) ? 1 : InpPeriodADX; - -//--- Map the buffers - SetIndexBuffer(0, BufferHA_ADX, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_PDI, INDICATOR_DATA); - SetIndexBuffer(2, BufferHA_NDI, INDICATOR_DATA); - SetIndexBuffer(3, BufferSmoothed_PDM, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferSmoothed_NDM, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferSmoothed_TR, INDICATOR_CALCULATIONS); - SetIndexBuffer(6, BufferDX, INDICATOR_CALCULATIONS); - -//--- Set all buffers as non-timeseries for stable calculation - ArraySetAsSeries(BufferHA_ADX, false); - ArraySetAsSeries(BufferHA_PDI, false); - ArraySetAsSeries(BufferHA_NDI, false); - ArraySetAsSeries(BufferSmoothed_PDM, false); - ArraySetAsSeries(BufferSmoothed_NDM, false); - ArraySetAsSeries(BufferSmoothed_TR, false); - ArraySetAsSeries(BufferDX, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtADXPeriod * 2 - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtADXPeriod); - PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtADXPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ADXW(%d)", g_ExtADXPeriod)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Custom indicator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data for the calculation - if(rates_total < g_ExtADXPeriod + 1) - return(0); - -//--- Resize intermediate buffers to match the available bars - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars using our toolkit - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate raw +DM, -DM, and TR - double pDM[], nDM[], TR[]; - ArrayResize(pDM, rates_total); - ArrayResize(nDM, rates_total); - ArrayResize(TR, rates_total); - - for(int i = 1; i < rates_total; i++) - { - double ha_high = ExtHaHighBuffer[i]; - double prev_ha_high = ExtHaHighBuffer[i-1]; - double ha_low = ExtHaLowBuffer[i]; - double prev_ha_low = ExtHaLowBuffer[i-1]; - double prev_ha_close = ExtHaCloseBuffer[i-1]; - - pDM[i] = ha_high - prev_ha_high; - nDM[i] = prev_ha_low - ha_low; - - if(pDM[i] < 0 || pDM[i] < nDM[i]) - pDM[i] = 0; - if(nDM[i] < 0 || nDM[i] < pDM[i]) - nDM[i] = 0; - - TR[i] = MathMax(ha_high, prev_ha_close) - MathMin(ha_low, prev_ha_close); - } - -//--- STEP 3: Calculate Smoothed PDM, NDM, and TR - for(int i = g_ExtADXPeriod; i < rates_total; i++) - { - if(i == g_ExtADXPeriod) // First calculation is a simple sum - { - double sum_pdm=0, sum_ndm=0, sum_tr=0; - for(int j=1; j<=g_ExtADXPeriod; j++) - { - sum_pdm += pDM[j]; - sum_ndm += nDM[j]; - sum_tr += TR[j]; - } - BufferSmoothed_PDM[i] = sum_pdm; - BufferSmoothed_NDM[i] = sum_ndm; - BufferSmoothed_TR[i] = sum_tr; - } - else // Subsequent calculations use Wilder's smoothing - { - BufferSmoothed_PDM[i] = BufferSmoothed_PDM[i-1] - (BufferSmoothed_PDM[i-1] / g_ExtADXPeriod) + pDM[i]; - BufferSmoothed_NDM[i] = BufferSmoothed_NDM[i-1] - (BufferSmoothed_NDM[i-1] / g_ExtADXPeriod) + nDM[i]; - BufferSmoothed_TR[i] = BufferSmoothed_TR[i-1] - (BufferSmoothed_TR[i-1] / g_ExtADXPeriod) + TR[i]; - } - } - -//--- STEP 4: Calculate +DI, -DI, and DX - for(int i = g_ExtADXPeriod; i < rates_total; i++) - { - if(BufferSmoothed_TR[i] != 0.0) - { - BufferHA_PDI[i] = (BufferSmoothed_PDM[i] / BufferSmoothed_TR[i]) * 100.0; - BufferHA_NDI[i] = (BufferSmoothed_NDM[i] / BufferSmoothed_TR[i]) * 100.0; - } - - double di_sum = BufferHA_PDI[i] + BufferHA_NDI[i]; - if(di_sum != 0.0) - BufferDX[i] = MathAbs(BufferHA_PDI[i] - BufferHA_NDI[i]) / di_sum * 100.0; - else - BufferDX[i] = 0.0; - } - -//--- STEP 5: Smooth DX to get the final ADX value - for(int i = g_ExtADXPeriod * 2 - 1; i < rates_total; i++) - { - if(i == g_ExtADXPeriod * 2 - 1) // First ADX value is a simple average - { - double sum_dx = 0; - for(int j=i-g_ExtADXPeriod+1; j<=i; j++) - sum_dx += BufferDX[j]; - BufferHA_ADX[i] = sum_dx / g_ExtADXPeriod; - } - else // Subsequent ADX values are smoothed - { - BufferHA_ADX[i] = (BufferHA_ADX[i-1] * (g_ExtADXPeriod - 1) + BufferDX[i]) / g_ExtADXPeriod; - } - } - -//--- Return value of rates_total to signal a full recalculation - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ADX_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx (Based on MetaQuotes ADXW) | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "4.00" // Refactored for full recalculation and stability +#property description "ADX by Welles Wilder on Heikin Ashi data." + +// --- Standard and Custom Includes --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 7 // 3 for plotting, 4 for calculations +#property indicator_plots 3 + +//--- Plot 1: ADX line (Main trend strength) +#property indicator_label1 "HA_ADX" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: +DI line (Positive Directional Indicator) +#property indicator_label2 "HA_+DI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrLimeGreen +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Plot 3: -DI line (Negative Directional Indicator) +#property indicator_label3 "HA_-DI" +#property indicator_type3 DRAW_LINE +#property indicator_color3 clrTomato +#property indicator_style3 STYLE_DOT +#property indicator_width3 1 + +//--- Input Parameters --- +input int InpPeriodADX = 14; // Period for ADX calculations + +//--- Indicator Buffers --- +double BufferHA_ADX[]; +double BufferHA_PDI[]; +double BufferHA_NDI[]; +double BufferSmoothed_PDM[]; +double BufferSmoothed_NDM[]; +double BufferSmoothed_TR[]; +double BufferDX[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtADXPeriod; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store the ADX period + g_ExtADXPeriod = (InpPeriodADX < 1) ? 1 : InpPeriodADX; + +//--- Map the buffers + SetIndexBuffer(0, BufferHA_ADX, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_PDI, INDICATOR_DATA); + SetIndexBuffer(2, BufferHA_NDI, INDICATOR_DATA); + SetIndexBuffer(3, BufferSmoothed_PDM, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferSmoothed_NDM, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferSmoothed_TR, INDICATOR_CALCULATIONS); + SetIndexBuffer(6, BufferDX, INDICATOR_CALCULATIONS); + +//--- Set all buffers as non-timeseries for stable calculation + ArraySetAsSeries(BufferHA_ADX, false); + ArraySetAsSeries(BufferHA_PDI, false); + ArraySetAsSeries(BufferHA_NDI, false); + ArraySetAsSeries(BufferSmoothed_PDM, false); + ArraySetAsSeries(BufferSmoothed_NDM, false); + ArraySetAsSeries(BufferSmoothed_TR, false); + ArraySetAsSeries(BufferDX, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtADXPeriod * 2 - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtADXPeriod); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtADXPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ADXW(%d)", g_ExtADXPeriod)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Custom indicator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data for the calculation + if(rates_total < g_ExtADXPeriod + 1) + return(0); + +//--- Resize intermediate buffers to match the available bars + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars using our toolkit + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate raw +DM, -DM, and TR + double pDM[], nDM[], TR[]; + ArrayResize(pDM, rates_total); + ArrayResize(nDM, rates_total); + ArrayResize(TR, rates_total); + + for(int i = 1; i < rates_total; i++) + { + double ha_high = ExtHaHighBuffer[i]; + double prev_ha_high = ExtHaHighBuffer[i-1]; + double ha_low = ExtHaLowBuffer[i]; + double prev_ha_low = ExtHaLowBuffer[i-1]; + double prev_ha_close = ExtHaCloseBuffer[i-1]; + + pDM[i] = ha_high - prev_ha_high; + nDM[i] = prev_ha_low - ha_low; + + if(pDM[i] < 0 || pDM[i] < nDM[i]) + pDM[i] = 0; + if(nDM[i] < 0 || nDM[i] < pDM[i]) + nDM[i] = 0; + + TR[i] = MathMax(ha_high, prev_ha_close) - MathMin(ha_low, prev_ha_close); + } + +//--- STEP 3: Calculate Smoothed PDM, NDM, and TR + for(int i = g_ExtADXPeriod; i < rates_total; i++) + { + if(i == g_ExtADXPeriod) // First calculation is a simple sum + { + double sum_pdm=0, sum_ndm=0, sum_tr=0; + for(int j=1; j<=g_ExtADXPeriod; j++) + { + sum_pdm += pDM[j]; + sum_ndm += nDM[j]; + sum_tr += TR[j]; + } + BufferSmoothed_PDM[i] = sum_pdm; + BufferSmoothed_NDM[i] = sum_ndm; + BufferSmoothed_TR[i] = sum_tr; + } + else // Subsequent calculations use Wilder's smoothing + { + BufferSmoothed_PDM[i] = BufferSmoothed_PDM[i-1] - (BufferSmoothed_PDM[i-1] / g_ExtADXPeriod) + pDM[i]; + BufferSmoothed_NDM[i] = BufferSmoothed_NDM[i-1] - (BufferSmoothed_NDM[i-1] / g_ExtADXPeriod) + nDM[i]; + BufferSmoothed_TR[i] = BufferSmoothed_TR[i-1] - (BufferSmoothed_TR[i-1] / g_ExtADXPeriod) + TR[i]; + } + } + +//--- STEP 4: Calculate +DI, -DI, and DX + for(int i = g_ExtADXPeriod; i < rates_total; i++) + { + if(BufferSmoothed_TR[i] != 0.0) + { + BufferHA_PDI[i] = (BufferSmoothed_PDM[i] / BufferSmoothed_TR[i]) * 100.0; + BufferHA_NDI[i] = (BufferSmoothed_NDM[i] / BufferSmoothed_TR[i]) * 100.0; + } + + double di_sum = BufferHA_PDI[i] + BufferHA_NDI[i]; + if(di_sum != 0.0) + BufferDX[i] = MathAbs(BufferHA_PDI[i] - BufferHA_NDI[i]) / di_sum * 100.0; + else + BufferDX[i] = 0.0; + } + +//--- STEP 5: Smooth DX to get the final ADX value + for(int i = g_ExtADXPeriod * 2 - 1; i < rates_total; i++) + { + if(i == g_ExtADXPeriod * 2 - 1) // First ADX value is a simple average + { + double sum_dx = 0; + for(int j=i-g_ExtADXPeriod+1; j<=i; j++) + sum_dx += BufferDX[j]; + BufferHA_ADX[i] = sum_dx / g_ExtADXPeriod; + } + else // Subsequent ADX values are smoothed + { + BufferHA_ADX[i] = (BufferHA_ADX[i-1] * (g_ExtADXPeriod - 1) + BufferDX[i]) / g_ExtADXPeriod; + } + } + +//--- Return value of rates_total to signal a full recalculation + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ALMA.md b/Indicators/MyIndicators/ALMA.md index 10de96e..0a882e0 100644 --- a/Indicators/MyIndicators/ALMA.md +++ b/Indicators/MyIndicators/ALMA.md @@ -1,67 +1,67 @@ -# Arnaud Legoux Moving Average (ALMA) - -## 1. Summary (Introduction) - -The Arnaud Legoux Moving Average (ALMA) was developed by Arnaud Legoux and Dimitrios Kouzis-Loukas. It was designed to address two common problems with traditional moving averages: lag and smoothness. The ALMA attempts to strike a better balance between responsiveness and smoothness, providing a high-fidelity trend line that reduces lag significantly while still filtering out minor price noise. - -It achieves this by applying a Gaussian filter to the moving average calculation, which is shifted according to a user-defined "offset" parameter. This allows the filter to be more weighted towards recent bars, thus reducing lag. - -## 2. Mathematical Foundations and Calculation Logic - -The ALMA is a sophisticated weighted moving average that uses a Gaussian distribution for its weights. Unlike a simple or exponential moving average, the weights are not linear or exponentially decaying but follow a bell curve. - -### Required Components - -- **Window Size (N):** The lookback period for the moving average. -- **Offset (O):** A parameter between 0 and 1 that shifts the focus of the bell curve. An offset of 0.85 (the default) means the most weight is applied to bars that are 85% of the way through the lookback window, emphasizing more recent data. -- **Sigma (S):** A parameter that controls the "flatness" or "sharpness" of the bell curve. A larger sigma creates a flatter curve (more like an SMA), while a smaller sigma creates a sharper curve (more focused weights). -- **Source Price (P):** The price series used for the calculation (e.g., Close). - -### Calculation Steps (Algorithm) - -For each bar `i`, the ALMA is calculated by taking a weighted sum of the prices in the lookback window from `i - (N - 1)` to `i`. - -1. **Calculate Gaussian Weight:** For each point `j` within the lookback window (where `j` goes from `0` to `N-1`), a weight is calculated based on a Gaussian function. - - - First, calculate the `m` and `s` parameters from the user inputs: - $m = O \times (N - 1)$ - $s = \frac{N}{S}$ - - Then, calculate the weight for each point `j`: - $\text{Weight}_j = e^{-\frac{(j - m)^2}{2s^2}}$ - Where `e` is Euler's number. - -2. **Calculate the Weighted Sum:** Multiply each price in the window by its corresponding weight and sum the results. - $\text{Weighted Sum}_i = \sum_{j=0}^{N-1} P_{i - (N - 1) + j} \times \text{Weight}_j$ - -3. **Normalize and Calculate Final ALMA:** Divide the weighted sum by the sum of all weights to get the final ALMA value. - $\text{ALMA}_i = \frac{\text{Weighted Sum}_i}{\sum_{j=0}^{N-1} \text{Weight}_j}$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refocused to be a completely self-contained, robust, and accurate indicator. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. As ALMA is not a recursive indicator, this is a straightforward and highly stable approach. - -- **Self-Contained Price Handling:** The indicator does not use external handles like `iMA`. It directly processes the price arrays (`open`, `high`, `low`, `close`) provided by `OnCalculate`. A `for` loop and `switch` block prepare a single `price_source[]` array based on the user's `InpAppliedPrice` selection, including all standard and calculated price types (e.g., `PRICE_TYPICAL`). - -- **Accurate Indexing:** The implementation uses the correct indexing logic (`price_index = i - (g_ExtAlmaPeriod - 1) + j`) within the calculation loop. This ensures that the weights are applied to the correct prices within the sliding window, perfectly matching the standard definition of the indicator. - -- **Integrated Calculation Loop:** The `OnCalculate` function uses a single, efficient main `for` loop to calculate the ALMA for each bar. The complex weighting and summation logic is handled within this loop, making the code clear and easy to follow. - -- **Heikin Ashi Variant (`ALMA_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. - - This combines the advanced smoothing of the ALMA formula with the noise-filtering properties of Heikin Ashi candles, resulting in an exceptionally smooth and responsive trend line. - -## 4. Parameters - -- **Window Size / Period (`InpAlmaPeriod`):** The lookback period for the moving average. Default is `9`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation. Default is `PRICE_CLOSE`. -- **Offset (`InpAlmaOffset`):** Controls the focus of the moving average. A value closer to `1` makes the ALMA more responsive (less lag), while a value closer to `0` makes it smoother (more lag). Default is `0.85`. -- **Sigma (`InpAlmaSigma`):** Controls the smoothness of the moving average. A larger value makes the line smoother, while a smaller value makes it follow the price more closely. Default is `6.0`. - -## 5. Usage and Interpretation - -- **Trend Identification:** The ALMA is primarily used as a high-fidelity trend line. When the price is above the ALMA and the ALMA is rising, the trend is considered bullish. When the price is below the ALMA and the ALMA is falling, the trend is considered bearish. -- **Dynamic Support and Resistance:** The line itself can act as a very reliable level of dynamic support in an uptrend or resistance in a downtrend. -- **Crossover Signals:** Crossovers of the price and the ALMA line can be used as trade signals. Due to its reduced lag, these signals are generally faster than those from traditional moving averages. -- **Caution:** While the ALMA is an advanced moving average, it is still a lagging indicator. It performs best in trending markets and can produce false signals in sideways or choppy conditions. +# Arnaud Legoux Moving Average (ALMA) + +## 1. Summary (Introduction) + +The Arnaud Legoux Moving Average (ALMA) was developed by Arnaud Legoux and Dimitrios Kouzis-Loukas. It was designed to address two common problems with traditional moving averages: lag and smoothness. The ALMA attempts to strike a better balance between responsiveness and smoothness, providing a high-fidelity trend line that reduces lag significantly while still filtering out minor price noise. + +It achieves this by applying a Gaussian filter to the moving average calculation, which is shifted according to a user-defined "offset" parameter. This allows the filter to be more weighted towards recent bars, thus reducing lag. + +## 2. Mathematical Foundations and Calculation Logic + +The ALMA is a sophisticated weighted moving average that uses a Gaussian distribution for its weights. Unlike a simple or exponential moving average, the weights are not linear or exponentially decaying but follow a bell curve. + +### Required Components + +- **Window Size (N):** The lookback period for the moving average. +- **Offset (O):** A parameter between 0 and 1 that shifts the focus of the bell curve. An offset of 0.85 (the default) means the most weight is applied to bars that are 85% of the way through the lookback window, emphasizing more recent data. +- **Sigma (S):** A parameter that controls the "flatness" or "sharpness" of the bell curve. A larger sigma creates a flatter curve (more like an SMA), while a smaller sigma creates a sharper curve (more focused weights). +- **Source Price (P):** The price series used for the calculation (e.g., Close). + +### Calculation Steps (Algorithm) + +For each bar `i`, the ALMA is calculated by taking a weighted sum of the prices in the lookback window from `i - (N - 1)` to `i`. + +1. **Calculate Gaussian Weight:** For each point `j` within the lookback window (where `j` goes from `0` to `N-1`), a weight is calculated based on a Gaussian function. + + - First, calculate the `m` and `s` parameters from the user inputs: + $m = O \times (N - 1)$ + $s = \frac{N}{S}$ + - Then, calculate the weight for each point `j`: + $\text{Weight}_j = e^{-\frac{(j - m)^2}{2s^2}}$ + Where `e` is Euler's number. + +2. **Calculate the Weighted Sum:** Multiply each price in the window by its corresponding weight and sum the results. + $\text{Weighted Sum}_i = \sum_{j=0}^{N-1} P_{i - (N - 1) + j} \times \text{Weight}_j$ + +3. **Normalize and Calculate Final ALMA:** Divide the weighted sum by the sum of all weights to get the final ALMA value. + $\text{ALMA}_i = \frac{\text{Weighted Sum}_i}{\sum_{j=0}^{N-1} \text{Weight}_j}$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refocused to be a completely self-contained, robust, and accurate indicator. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. As ALMA is not a recursive indicator, this is a straightforward and highly stable approach. + +- **Self-Contained Price Handling:** The indicator does not use external handles like `iMA`. It directly processes the price arrays (`open`, `high`, `low`, `close`) provided by `OnCalculate`. A `for` loop and `switch` block prepare a single `price_source[]` array based on the user's `InpAppliedPrice` selection, including all standard and calculated price types (e.g., `PRICE_TYPICAL`). + +- **Accurate Indexing:** The implementation uses the correct indexing logic (`price_index = i - (g_ExtAlmaPeriod - 1) + j`) within the calculation loop. This ensures that the weights are applied to the correct prices within the sliding window, perfectly matching the standard definition of the indicator. + +- **Integrated Calculation Loop:** The `OnCalculate` function uses a single, efficient main `for` loop to calculate the ALMA for each bar. The complex weighting and summation logic is handled within this loop, making the code clear and easy to follow. + +- **Heikin Ashi Variant (`ALMA_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. + - This combines the advanced smoothing of the ALMA formula with the noise-filtering properties of Heikin Ashi candles, resulting in an exceptionally smooth and responsive trend line. + +## 4. Parameters + +- **Window Size / Period (`InpAlmaPeriod`):** The lookback period for the moving average. Default is `9`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation. Default is `PRICE_CLOSE`. +- **Offset (`InpAlmaOffset`):** Controls the focus of the moving average. A value closer to `1` makes the ALMA more responsive (less lag), while a value closer to `0` makes it smoother (more lag). Default is `0.85`. +- **Sigma (`InpAlmaSigma`):** Controls the smoothness of the moving average. A larger value makes the line smoother, while a smaller value makes it follow the price more closely. Default is `6.0`. + +## 5. Usage and Interpretation + +- **Trend Identification:** The ALMA is primarily used as a high-fidelity trend line. When the price is above the ALMA and the ALMA is rising, the trend is considered bullish. When the price is below the ALMA and the ALMA is falling, the trend is considered bearish. +- **Dynamic Support and Resistance:** The line itself can act as a very reliable level of dynamic support in an uptrend or resistance in a downtrend. +- **Crossover Signals:** Crossovers of the price and the ALMA line can be used as trade signals. Due to its reduced lag, these signals are generally faster than those from traditional moving averages. +- **Caution:** While the ALMA is an advanced moving average, it is still a lagging indicator. It performs best in trending markets and can produce false signals in sideways or choppy conditions. diff --git a/Indicators/MyIndicators/ALMA.mq5 b/Indicators/MyIndicators/ALMA.mq5 index 7087e23..57ff0b0 100644 --- a/Indicators/MyIndicators/ALMA.mq5 +++ b/Indicators/MyIndicators/ALMA.mq5 @@ -1,134 +1,134 @@ -//+------------------------------------------------------------------+ -//| ALMA.mq5| -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored to be self-contained and stable -#property description "Arnaud Legoux Moving Average (ALMA)" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: ALMA line -#property indicator_label1 "ALMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrMediumVioletRed -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAlmaPeriod = 9; // Window size (period) -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price -input double InpAlmaOffset = 0.85; // Offset (0 to 1) -input double InpAlmaSigma = 6.0; // Sigma (smoothness) - -//--- Indicator Buffers --- -double BufferALMA[]; - -//--- Global Variables --- -int g_ExtAlmaPeriod; -double g_ExtAlmaOffset; -double g_ExtAlmaSigma; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input parameters - g_ExtAlmaPeriod = (InpAlmaPeriod < 1) ? 1 : InpAlmaPeriod; - g_ExtAlmaOffset = InpAlmaOffset; - g_ExtAlmaSigma = (InpAlmaSigma <= 0) ? 0.01 : InpAlmaSigma; - -//--- Map the buffer and set as non-timeseries - SetIndexBuffer(0, BufferALMA, INDICATOR_DATA); - ArraySetAsSeries(BufferALMA, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAlmaPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ALMA(%d, %.2f, %.1f)", g_ExtAlmaPeriod, g_ExtAlmaOffset, g_ExtAlmaSigma)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Arnaud Legoux Moving Average calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < g_ExtAlmaPeriod) - return(0); - -//--- STEP 1: Prepare the source price array - double price_source[]; - ArrayResize(price_source, rates_total); - for(int i=0; i 0) - BufferALMA[i] = sum / norm; - else - BufferALMA[i] = 0.0; - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ALMA.mq5| +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored to be self-contained and stable +#property description "Arnaud Legoux Moving Average (ALMA)" + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: ALMA line +#property indicator_label1 "ALMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrMediumVioletRed +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAlmaPeriod = 9; // Window size (period) +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price +input double InpAlmaOffset = 0.85; // Offset (0 to 1) +input double InpAlmaSigma = 6.0; // Sigma (smoothness) + +//--- Indicator Buffers --- +double BufferALMA[]; + +//--- Global Variables --- +int g_ExtAlmaPeriod; +double g_ExtAlmaOffset; +double g_ExtAlmaSigma; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input parameters + g_ExtAlmaPeriod = (InpAlmaPeriod < 1) ? 1 : InpAlmaPeriod; + g_ExtAlmaOffset = InpAlmaOffset; + g_ExtAlmaSigma = (InpAlmaSigma <= 0) ? 0.01 : InpAlmaSigma; + +//--- Map the buffer and set as non-timeseries + SetIndexBuffer(0, BufferALMA, INDICATOR_DATA); + ArraySetAsSeries(BufferALMA, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAlmaPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ALMA(%d, %.2f, %.1f)", g_ExtAlmaPeriod, g_ExtAlmaOffset, g_ExtAlmaSigma)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Arnaud Legoux Moving Average calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total < g_ExtAlmaPeriod) + return(0); + +//--- STEP 1: Prepare the source price array + double price_source[]; + ArrayResize(price_source, rates_total); + for(int i=0; i 0) + BufferALMA[i] = sum / norm; + else + BufferALMA[i] = 0.0; + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ALMA_HeikinAshi.mq5 b/Indicators/MyIndicators/ALMA_HeikinAshi.mq5 index f1bc266..32ff240 100644 --- a/Indicators/MyIndicators/ALMA_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/ALMA_HeikinAshi.mq5 @@ -1,172 +1,172 @@ -//+------------------------------------------------------------------+ -//| ALMA_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.01" // Fixed indexing logic in ALMA calculation -#property description "Arnaud Legoux Moving Average (ALMA) on Heikin Ashi data" - -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: ALMA line -#property indicator_label1 "HA_ALMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrMediumVioletRed -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Enum for selecting Heikin Ashi price source --- -enum ENUM_HA_APPLIED_PRICE - { - HA_PRICE_CLOSE, // Heikin Ashi Close - HA_PRICE_OPEN, // Heikin Ashi Open - HA_PRICE_HIGH, // Heikin Ashi High - HA_PRICE_LOW, // Heikin Ashi Low - }; - -//--- Input Parameters --- -input int InpAlmaPeriod = 9; -input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; -input double InpAlmaOffset = 0.85; -input double InpAlmaSigma = 6.0; - -//--- Indicator Buffers --- -double BufferHA_ALMA[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtAlmaPeriod; -double g_ExtAlmaOffset; -double g_ExtAlmaSigma; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAlmaPeriod = (InpAlmaPeriod < 1) ? 1 : InpAlmaPeriod; - g_ExtAlmaOffset = InpAlmaOffset; - g_ExtAlmaSigma = (InpAlmaSigma <= 0) ? 0.01 : InpAlmaSigma; - - SetIndexBuffer(0, BufferHA_ALMA, INDICATOR_DATA); - ArraySetAsSeries(BufferHA_ALMA, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAlmaPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ALMA(%d, %.2f, %.1f)", g_ExtAlmaPeriod, g_ExtAlmaOffset, g_ExtAlmaSigma)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Arnaud Legoux Moving Average calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < g_ExtAlmaPeriod) - return(0); - -//--- Resize intermediate buffers to match the available bars - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars using our toolkit - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Select the source price array for ALMA calculation - double source_array[]; - switch(InpAppliedPrice) - { - case HA_PRICE_OPEN: - ArrayCopy(source_array, ExtHaOpenBuffer); - break; - case HA_PRICE_HIGH: - ArrayCopy(source_array, ExtHaHighBuffer); - break; - case HA_PRICE_LOW: - ArrayCopy(source_array, ExtHaLowBuffer); - break; - default: // HA_PRICE_CLOSE - ArrayCopy(source_array, ExtHaCloseBuffer); - break; - } - -//--- STEP 3: Calculate ALMA based on the selected HA price array - double m = g_ExtAlmaOffset * (g_ExtAlmaPeriod - 1.0); - double s = (double)g_ExtAlmaPeriod / g_ExtAlmaSigma; - -// The main loop iterates through all bars that can be calculated - for(int i = g_ExtAlmaPeriod - 1; i < rates_total; i++) - { - double sum = 0.0; - double norm = 0.0; - - // The inner loop calculates the weighted sum for the current bar 'i' - for(int j = 0; j < g_ExtAlmaPeriod; j++) - { - double weight = MathExp(-1 * MathPow(j - m, 2) / (2 * s * s)); - - // *** FIX: Reverted to the original, correct indexing logic *** - // This ensures the weight for position 'j' is applied to the correct price in the window. - int price_index = i - (g_ExtAlmaPeriod - 1) + j; - - sum += source_array[price_index] * weight; - norm += weight; - } - - if(norm > 0) - BufferHA_ALMA[i] = sum / norm; - else - BufferHA_ALMA[i] = 0.0; - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ALMA_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.01" // Fixed indexing logic in ALMA calculation +#property description "Arnaud Legoux Moving Average (ALMA) on Heikin Ashi data" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: ALMA line +#property indicator_label1 "HA_ALMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrMediumVioletRed +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Enum for selecting Heikin Ashi price source --- +enum ENUM_HA_APPLIED_PRICE + { + HA_PRICE_CLOSE, // Heikin Ashi Close + HA_PRICE_OPEN, // Heikin Ashi Open + HA_PRICE_HIGH, // Heikin Ashi High + HA_PRICE_LOW, // Heikin Ashi Low + }; + +//--- Input Parameters --- +input int InpAlmaPeriod = 9; +input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; +input double InpAlmaOffset = 0.85; +input double InpAlmaSigma = 6.0; + +//--- Indicator Buffers --- +double BufferHA_ALMA[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtAlmaPeriod; +double g_ExtAlmaOffset; +double g_ExtAlmaSigma; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAlmaPeriod = (InpAlmaPeriod < 1) ? 1 : InpAlmaPeriod; + g_ExtAlmaOffset = InpAlmaOffset; + g_ExtAlmaSigma = (InpAlmaSigma <= 0) ? 0.01 : InpAlmaSigma; + + SetIndexBuffer(0, BufferHA_ALMA, INDICATOR_DATA); + ArraySetAsSeries(BufferHA_ALMA, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAlmaPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ALMA(%d, %.2f, %.1f)", g_ExtAlmaPeriod, g_ExtAlmaOffset, g_ExtAlmaSigma)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Arnaud Legoux Moving Average calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total < g_ExtAlmaPeriod) + return(0); + +//--- Resize intermediate buffers to match the available bars + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars using our toolkit + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Select the source price array for ALMA calculation + double source_array[]; + switch(InpAppliedPrice) + { + case HA_PRICE_OPEN: + ArrayCopy(source_array, ExtHaOpenBuffer); + break; + case HA_PRICE_HIGH: + ArrayCopy(source_array, ExtHaHighBuffer); + break; + case HA_PRICE_LOW: + ArrayCopy(source_array, ExtHaLowBuffer); + break; + default: // HA_PRICE_CLOSE + ArrayCopy(source_array, ExtHaCloseBuffer); + break; + } + +//--- STEP 3: Calculate ALMA based on the selected HA price array + double m = g_ExtAlmaOffset * (g_ExtAlmaPeriod - 1.0); + double s = (double)g_ExtAlmaPeriod / g_ExtAlmaSigma; + +// The main loop iterates through all bars that can be calculated + for(int i = g_ExtAlmaPeriod - 1; i < rates_total; i++) + { + double sum = 0.0; + double norm = 0.0; + + // The inner loop calculates the weighted sum for the current bar 'i' + for(int j = 0; j < g_ExtAlmaPeriod; j++) + { + double weight = MathExp(-1 * MathPow(j - m, 2) / (2 * s * s)); + + // *** FIX: Reverted to the original, correct indexing logic *** + // This ensures the weight for position 'j' is applied to the correct price in the window. + int price_index = i - (g_ExtAlmaPeriod - 1) + j; + + sum += source_array[price_index] * weight; + norm += weight; + } + + if(norm > 0) + BufferHA_ALMA[i] = sum / norm; + else + BufferHA_ALMA[i] = 0.0; + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ATR.md b/Indicators/MyIndicators/ATR.md index 6b34b0c..0952ff6 100644 --- a/Indicators/MyIndicators/ATR.md +++ b/Indicators/MyIndicators/ATR.md @@ -1,64 +1,64 @@ -# Average True Range (ATR) - -## 1. Summary (Introduction) - -The Average True Range (ATR) is a technical analysis indicator developed by J. Welles Wilder, introduced in his 1978 book "New Concepts in Technical Trading Systems." The ATR is not used to indicate price direction; rather, it is a measure of **volatility**. - -It calculates the "true range" for each period and then smooths these values, providing a representation of the average size of the price range over a given time. High ATR values indicate high volatility, while low ATR values indicate low volatility or a period of consolidation. It is a foundational tool for many other indicators (like Supertrend, Keltner Channels) and for risk management strategies, such as setting stop-loss levels. - -## 2. Mathematical Foundations and Calculation Logic - -The ATR is based on the concept of the "True Range" (TR), which provides a more comprehensive measure of a single period's volatility than the simple High-Low range. - -### Required Components - -- **Period (N):** The lookback period for the smoothing calculation (e.g., 14). -- **Price Data:** The `High`, `Low`, and `Close` of each bar. - -### Calculation Steps (Algorithm) - -1. **Calculate the True Range (TR):** For each bar, the True Range is the **greatest** of the following three values: - - - The current High minus the current Low: $\text{High}_i - \text{Low}_i$ - - The absolute value of the current High minus the previous Close: $\text{Abs}(\text{High}_i - \text{Close}_{i-1})$ - - The absolute value of the current Low minus the previous Close: $\text{Abs}(\text{Low}_i - \text{Close}_{i-1})$ - $\text{TR}_i = \text{Max}[(\text{High}_i - \text{Low}_i), \text{Abs}(\text{High}_i - \text{Close}_{i-1}), \text{Abs}(\text{Low}_i - \text{Close}_{i-1})]$ - -2. **Calculate the Average True Range (ATR):** The ATR is a smoothed moving average of the True Range values, calculated using Wilder's specific smoothing method (also known as a Running Moving Average - RMA, or a specific type of Smoothed Moving Average - SMMA). - - **Initialization:** The first ATR value is a simple average of the first `N` TR values. - $\text{ATR}_{N} = \frac{1}{N} \sum_{i=1}^{N} \text{TR}_i$ - - **Recursive Calculation:** All subsequent values are calculated using the following formula: - $\text{ATR}_i = \frac{(\text{ATR}_{i-1} \times (N-1)) + \text{TR}_i}{N}$ - -_Note: This smoothing method is the globally accepted standard for ATR, as used by platforms like TradingView. The built-in `iATR` in MetaTrader uses a different, non-standard smoothing algorithm._ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is a self-contained, robust, and accurate representation of the classic Wilder's ATR. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the recursive ATR calculation remains stable and accurate, especially during timeframe changes or history loading. - -- **Consensus Wilder Algorithm:** The implementation strictly follows our established two-step algorithm for Wilder's smoothing: - - 1. **Robust Initialization:** The first ATR value (`BufferATR[g_ExtAtrPeriod]`) is calculated as a simple average of the first `N` True Range values. This provides a stable starting point for the recursive calculation. - 2. **Efficient Recursive Calculation:** All subsequent values are calculated using the efficient recursive formula, which is mathematically identical to Wilder's original method. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: - - 1. **Step 1:** A `for` loop calculates the True Range for every bar and stores the results in a temporary `tr[]` array. - 2. **Step 2:** A second `for` loop iterates through the `tr[]` array and applies our robust Wilder's smoothing algorithm to calculate the final `BufferATR` values. - -- **Heikin Ashi Variant (`ATR_HeikinAshi.mq5`):** - - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values to calculate the True Range. - - This results in a "smoothed volatility" measure, which reflects the volatility of the underlying Heikin Ashi trend rather than the raw market price. This can be useful for setting stop-losses in a Heikin Ashi-based trading system. - -## 4. Parameters - -- **ATR Period (`InpAtrPeriod`):** The lookback and smoothing period for the indicator. Wilder's original recommendation and the most common value is `14`. - -## 5. Usage and Interpretation - -- **Volatility Gauge:** The ATR's primary function is to measure volatility. A rising ATR indicates that volatility is increasing, meaning daily trading ranges are widening. A falling ATR indicates that volatility is decreasing and the market is entering a period of consolidation. -- **Stop-Loss Placement:** ATR is a cornerstone of modern risk management. A common technique is to place a stop-loss at a multiple of the ATR (e.g., 2 x ATR) below a long entry price or above a short entry price. This adapts the stop-loss distance to the current market conditions. -- **Position Sizing:** ATR can be used to normalize position sizes across different instruments. By calculating a position size based on a fixed risk amount (e.g., 1% of account equity) and the instrument's ATR, a trader can take on similar levels of risk regardless of whether they are trading a volatile or a quiet instrument. -- **Caution:** ATR does not provide any information about trend direction. A high ATR could be present in a strong uptrend, a strong downtrend, or a volatile ranging market. It should always be used in conjunction with other trend or momentum indicators. +# Average True Range (ATR) + +## 1. Summary (Introduction) + +The Average True Range (ATR) is a technical analysis indicator developed by J. Welles Wilder, introduced in his 1978 book "New Concepts in Technical Trading Systems." The ATR is not used to indicate price direction; rather, it is a measure of **volatility**. + +It calculates the "true range" for each period and then smooths these values, providing a representation of the average size of the price range over a given time. High ATR values indicate high volatility, while low ATR values indicate low volatility or a period of consolidation. It is a foundational tool for many other indicators (like Supertrend, Keltner Channels) and for risk management strategies, such as setting stop-loss levels. + +## 2. Mathematical Foundations and Calculation Logic + +The ATR is based on the concept of the "True Range" (TR), which provides a more comprehensive measure of a single period's volatility than the simple High-Low range. + +### Required Components + +- **Period (N):** The lookback period for the smoothing calculation (e.g., 14). +- **Price Data:** The `High`, `Low`, and `Close` of each bar. + +### Calculation Steps (Algorithm) + +1. **Calculate the True Range (TR):** For each bar, the True Range is the **greatest** of the following three values: + + - The current High minus the current Low: $\text{High}_i - \text{Low}_i$ + - The absolute value of the current High minus the previous Close: $\text{Abs}(\text{High}_i - \text{Close}_{i-1})$ + - The absolute value of the current Low minus the previous Close: $\text{Abs}(\text{Low}_i - \text{Close}_{i-1})$ + $\text{TR}_i = \text{Max}[(\text{High}_i - \text{Low}_i), \text{Abs}(\text{High}_i - \text{Close}_{i-1}), \text{Abs}(\text{Low}_i - \text{Close}_{i-1})]$ + +2. **Calculate the Average True Range (ATR):** The ATR is a smoothed moving average of the True Range values, calculated using Wilder's specific smoothing method (also known as a Running Moving Average - RMA, or a specific type of Smoothed Moving Average - SMMA). + - **Initialization:** The first ATR value is a simple average of the first `N` TR values. + $\text{ATR}_{N} = \frac{1}{N} \sum_{i=1}^{N} \text{TR}_i$ + - **Recursive Calculation:** All subsequent values are calculated using the following formula: + $\text{ATR}_i = \frac{(\text{ATR}_{i-1} \times (N-1)) + \text{TR}_i}{N}$ + +_Note: This smoothing method is the globally accepted standard for ATR, as used by platforms like TradingView. The built-in `iATR` in MetaTrader uses a different, non-standard smoothing algorithm._ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is a self-contained, robust, and accurate representation of the classic Wilder's ATR. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the recursive ATR calculation remains stable and accurate, especially during timeframe changes or history loading. + +- **Consensus Wilder Algorithm:** The implementation strictly follows our established two-step algorithm for Wilder's smoothing: + + 1. **Robust Initialization:** The first ATR value (`BufferATR[g_ExtAtrPeriod]`) is calculated as a simple average of the first `N` True Range values. This provides a stable starting point for the recursive calculation. + 2. **Efficient Recursive Calculation:** All subsequent values are calculated using the efficient recursive formula, which is mathematically identical to Wilder's original method. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: + + 1. **Step 1:** A `for` loop calculates the True Range for every bar and stores the results in a temporary `tr[]` array. + 2. **Step 2:** A second `for` loop iterates through the `tr[]` array and applies our robust Wilder's smoothing algorithm to calculate the final `BufferATR` values. + +- **Heikin Ashi Variant (`ATR_HeikinAshi.mq5`):** + - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values to calculate the True Range. + - This results in a "smoothed volatility" measure, which reflects the volatility of the underlying Heikin Ashi trend rather than the raw market price. This can be useful for setting stop-losses in a Heikin Ashi-based trading system. + +## 4. Parameters + +- **ATR Period (`InpAtrPeriod`):** The lookback and smoothing period for the indicator. Wilder's original recommendation and the most common value is `14`. + +## 5. Usage and Interpretation + +- **Volatility Gauge:** The ATR's primary function is to measure volatility. A rising ATR indicates that volatility is increasing, meaning daily trading ranges are widening. A falling ATR indicates that volatility is decreasing and the market is entering a period of consolidation. +- **Stop-Loss Placement:** ATR is a cornerstone of modern risk management. A common technique is to place a stop-loss at a multiple of the ATR (e.g., 2 x ATR) below a long entry price or above a short entry price. This adapts the stop-loss distance to the current market conditions. +- **Position Sizing:** ATR can be used to normalize position sizes across different instruments. By calculating a position size based on a fixed risk amount (e.g., 1% of account equity) and the instrument's ATR, a trader can take on similar levels of risk regardless of whether they are trading a volatile or a quiet instrument. +- **Caution:** ATR does not provide any information about trend direction. A high ATR could be present in a strong uptrend, a strong downtrend, or a volatile ranging market. It should always be used in conjunction with other trend or momentum indicators. diff --git a/Indicators/MyIndicators/ATR.mq5 b/Indicators/MyIndicators/ATR.mq5 index deda5fd..8d05658 100644 --- a/Indicators/MyIndicators/ATR.mq5 +++ b/Indicators/MyIndicators/ATR.mq5 @@ -1,99 +1,99 @@ -//+------------------------------------------------------------------+ -//| ATR.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "1.00" -#property description "Average True Range" - -//--- Indicator Window and Plot Properties --- -#property indicator_separate_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: ATR line -#property indicator_label1 "ATR" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Input Parameters --- -input int InpAtrPeriod = 14; // ATR Period - -//--- Indicator Buffers --- -double BufferATR[]; - -//--- Global Variables --- -int g_ExtAtrPeriod; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - - SetIndexBuffer(0, BufferATR, INDICATOR_DATA); - ArraySetAsSeries(BufferATR, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ATR(%d)", g_ExtAtrPeriod)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Average True Range calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- STEP 1: Calculate True Range - double tr[]; - ArrayResize(tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - double range1 = high[i] - low[i]; - double range2 = MathAbs(high[i] - close[i-1]); - double range3 = MathAbs(low[i] - close[i-1]); - tr[i] = MathMax(range1, MathMax(range2, range3)); - } - -//--- STEP 2: Calculate ATR (Wilder's Smoothing) - for(int i = 1; i < rates_total; i++) - { - if(i == g_ExtAtrPeriod) // Initialization with a simple average of TR - { - double sum_tr = 0; - for(int j = 1; j <= g_ExtAtrPeriod; j++) - { - sum_tr += tr[j]; - } - BufferATR[i] = sum_tr / g_ExtAtrPeriod; - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ATR.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "1.00" +#property description "Average True Range" + +//--- Indicator Window and Plot Properties --- +#property indicator_separate_window +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: ATR line +#property indicator_label1 "ATR" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Input Parameters --- +input int InpAtrPeriod = 14; // ATR Period + +//--- Indicator Buffers --- +double BufferATR[]; + +//--- Global Variables --- +int g_ExtAtrPeriod; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + + SetIndexBuffer(0, BufferATR, INDICATOR_DATA); + ArraySetAsSeries(BufferATR, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ATR(%d)", g_ExtAtrPeriod)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Average True Range calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- STEP 1: Calculate True Range + double tr[]; + ArrayResize(tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + double range1 = high[i] - low[i]; + double range2 = MathAbs(high[i] - close[i-1]); + double range3 = MathAbs(low[i] - close[i-1]); + tr[i] = MathMax(range1, MathMax(range2, range3)); + } + +//--- STEP 2: Calculate ATR (Wilder's Smoothing) + for(int i = 1; i < rates_total; i++) + { + if(i == g_ExtAtrPeriod) // Initialization with a simple average of TR + { + double sum_tr = 0; + for(int j = 1; j <= g_ExtAtrPeriod; j++) + { + sum_tr += tr[j]; + } + BufferATR[i] = sum_tr / g_ExtAtrPeriod; + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ATR_HeikinAshi.mq5 b/Indicators/MyIndicators/ATR_HeikinAshi.mq5 index d047140..3809c95 100644 --- a/Indicators/MyIndicators/ATR_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/ATR_HeikinAshi.mq5 @@ -1,131 +1,131 @@ -//+------------------------------------------------------------------+ -//| ATR_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "1.00" -#property description "Average True Range on Heikin Ashi data" - -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_separate_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: ATR line -#property indicator_label1 "HA_ATR" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Input Parameters --- -input int InpAtrPeriod = 14; // ATR Period - -//--- Indicator Buffers --- -double BufferHA_ATR[]; - -//--- Global Objects and Variables --- -int g_ExtAtrPeriod; -CHeikinAshi_Calculator *g_ha_calculator; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - - SetIndexBuffer(0, BufferHA_ATR, INDICATOR_DATA); - ArraySetAsSeries(BufferHA_ATR, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ATR(%d)", g_ExtAtrPeriod)); - - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Average True Range on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- Intermediate Heikin Ashi Buffers - double ha_open[], ha_high[], ha_low[], ha_close[]; - ArrayResize(ha_open, rates_total); - ArrayResize(ha_high, rates_total); - ArrayResize(ha_low, rates_total); - ArrayResize(ha_close, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); - -//--- STEP 2: Calculate Heikin Ashi True Range - double ha_tr[]; - ArrayResize(ha_tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - double range1 = ha_high[i] - ha_low[i]; - double range2 = MathAbs(ha_high[i] - ha_close[i-1]); - double range3 = MathAbs(ha_low[i] - ha_close[i-1]); - ha_tr[i] = MathMax(range1, MathMax(range2, range3)); - } - -//--- STEP 3: Calculate ATR (Wilder's Smoothing) on HA_TR - for(int i = 1; i < rates_total; i++) - { - if(i == g_ExtAtrPeriod) // Initialization with a simple average of HA_TR - { - double sum_tr = 0; - for(int j = 1; j <= g_ExtAtrPeriod; j++) - { - sum_tr += ha_tr[j]; - } - BufferHA_ATR[i] = sum_tr / g_ExtAtrPeriod; - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ATR_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "1.00" +#property description "Average True Range on Heikin Ashi data" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_separate_window +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: ATR line +#property indicator_label1 "HA_ATR" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Input Parameters --- +input int InpAtrPeriod = 14; // ATR Period + +//--- Indicator Buffers --- +double BufferHA_ATR[]; + +//--- Global Objects and Variables --- +int g_ExtAtrPeriod; +CHeikinAshi_Calculator *g_ha_calculator; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + + SetIndexBuffer(0, BufferHA_ATR, INDICATOR_DATA); + ArraySetAsSeries(BufferHA_ATR, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_ATR(%d)", g_ExtAtrPeriod)); + + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Average True Range on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- Intermediate Heikin Ashi Buffers + double ha_open[], ha_high[], ha_low[], ha_close[]; + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + +//--- STEP 2: Calculate Heikin Ashi True Range + double ha_tr[]; + ArrayResize(ha_tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + double range1 = ha_high[i] - ha_low[i]; + double range2 = MathAbs(ha_high[i] - ha_close[i-1]); + double range3 = MathAbs(ha_low[i] - ha_close[i-1]); + ha_tr[i] = MathMax(range1, MathMax(range2, range3)); + } + +//--- STEP 3: Calculate ATR (Wilder's Smoothing) on HA_TR + for(int i = 1; i < rates_total; i++) + { + if(i == g_ExtAtrPeriod) // Initialization with a simple average of HA_TR + { + double sum_tr = 0; + for(int j = 1; j <= g_ExtAtrPeriod; j++) + { + sum_tr += ha_tr[j]; + } + BufferHA_ATR[i] = sum_tr / g_ExtAtrPeriod; + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ATR_TrailingStop.md b/Indicators/MyIndicators/ATR_TrailingStop.md index b3931c0..55ad98e 100644 --- a/Indicators/MyIndicators/ATR_TrailingStop.md +++ b/Indicators/MyIndicators/ATR_TrailingStop.md @@ -1,74 +1,74 @@ -# ATR Trailing Stop (Chandelier Exit) - -## 1. Summary (Introduction) - -The ATR Trailing Stop, also widely known as the Chandelier Exit, is a volatility-based indicator developed by Charles Le Beau and featured in Alexander Elder's books. It is designed to help traders stay in a trend for as long as possible while protecting profits from significant reversals. - -The indicator calculates a stop-loss level by measuring the market's recent volatility (using the Average True Range - ATR) and placing the stop a certain multiple of that volatility away from the recent price extreme. Its name, "Chandelier Exit," comes from the idea of hanging the stop-loss from the "chandelier" of the highest high (for long positions) or lowest low (for short positions) of the trend. - -## 2. Mathematical Foundations and Calculation Logic - -The indicator uses the highest high or lowest low over a period as an anchor and then subtracts or adds a multiple of the ATR to create the trailing stop level. - -### Required Components - -- **ATR Period (N):** The lookback period for all calculations (ATR, Highest High, Lowest Low). -- **ATR Multiplier (M):** The factor by which the ATR is multiplied to determine the stop distance. -- **Price Data:** The `High`, `Low`, and `Close` of each bar. - -### Calculation Steps (Algorithm) - -1. **Calculate the Average True Range (ATR):** Compute the ATR for the period `N` using Wilder's smoothing method. - -2. **Calculate the Raw Stop Levels:** For each bar, determine the two potential stop levels: - - - **Long Stop (for uptrends):** Find the highest high over the last `N` bars and subtract the ATR value multiplied by the factor `M`. - $\text{Long Stop}_i = \text{Highest High}_{N} - (M \times \text{ATR}_i)$ - - **Short Stop (for downtrends):** Find the lowest low over the last `N` bars and add the ATR value multiplied by the factor `M`. - $\text{Short Stop}_i = \text{Lowest Low}_{N} + (M \times \text{ATR}_i)$ - -3. **Determine the Trend Direction:** The trend is determined by comparing the closing price to the _previous_ bar's stop levels. - - - The trend flips to **up** if the current `Close` crosses **above** the previous bar's `Short Stop`. - - The trend flips to **down** if the current `Close` crosses **below** the previous bar's `Long Stop`. - - If neither condition is met, the trend continues from the previous bar. - -4. **Plot the Final Trailing Stop Line:** The final plotted line incorporates the "trailing" logic, meaning it can only move in the direction of the trend (up for a long stop, down for a short stop). - - If the trend is **up**: - $\text{Final Stop}_i = \text{Max}(\text{Long Stop}_i, \text{Final Stop}_{i-1})$ - - If the trend is **down**: - $\text{Final Stop}_i = \text{Min}(\text{Short Stop}_i, \text{Final Stop}_{i-1})$ - - _(Note: On a trend flip, the new stop is taken directly from the calculated `Long Stop` or `Short Stop` without comparison to the previous value.)_ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is a self-contained, robust, and accurate representation of the Chandelier Exit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like this, it is the most reliable method to ensure stability. - -- **Consensus Wilder Algorithm:** The ATR calculation strictly follows our established two-step algorithm for Wilder's smoothing (manual SMA for initialization, followed by the recursive formula), ensuring consistency with our other indicators and the global standard (e.g., TradingView). - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the complex logic easy to follow: - - 1. **Step 1:** The True Range is calculated. - 2. **Step 2:** The ATR is calculated from the True Range values. - 3. **Step 3:** The raw `Long Stop` and `Short Stop` levels are calculated. - 4. **Step 4:** The final loop determines the trend and applies the "trailing" logic to plot the final stop line. - -- **Visual Representation:** The implementation ensures that trend changes are represented by a clean, vertical line connecting the old and new trend lines, providing continuous visual information. - -- **Heikin Ashi Variant (`ATR_TrailingStop_HeikinAshi.mq5`):** - - Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values for all its inputs (ATR, Highest/Lowest price, and trend-flip condition). - - This results in a smoother trailing stop that is less susceptible to being triggered by the "noise" of standard candlesticks, making it well-suited for Heikin Ashi-based trend-following strategies. - -## 4. Parameters - -- **ATR Period (`InpAtrPeriod`):** The lookback period for the ATR and the Highest High / Lowest Low calculations. A longer period results in a smoother, slower-reacting stop. Default is `22`. -- **Multiplier (`InpMultiplier`):** The factor to multiply the ATR by. A larger multiplier places the stop further from the price, allowing for more room for pullbacks but resulting in a larger potential loss if the stop is hit. A smaller multiplier keeps the stop tighter. Common values range from 2.5 to 3.5. Default is `3.0`. - -## 5. Usage and Interpretation - -- **Trailing Stop-Loss:** The primary use of the indicator is as a dynamic, volatility-adjusted trailing stop-loss. In an uptrend, the blue line represents the suggested stop level. In a downtrend, the red line represents the suggested stop level. -- **Trend Identification:** The color and position of the line provide a clear indication of the trend. A blue line below the price indicates an uptrend; a red line above the price indicates a downtrend. -- **Exit Signal:** The primary signal is for exiting a trade. A long position would be closed when the price closes below the blue line. A short position would be closed when the price closes above the red line. -- **Caution:** The Chandelier Exit is an excellent tool for letting profits run in a strong trend. However, in sideways or choppy markets, it can lead to being stopped out prematurely. It is most effective when used to manage trades within an already identified, strong trend. +# ATR Trailing Stop (Chandelier Exit) + +## 1. Summary (Introduction) + +The ATR Trailing Stop, also widely known as the Chandelier Exit, is a volatility-based indicator developed by Charles Le Beau and featured in Alexander Elder's books. It is designed to help traders stay in a trend for as long as possible while protecting profits from significant reversals. + +The indicator calculates a stop-loss level by measuring the market's recent volatility (using the Average True Range - ATR) and placing the stop a certain multiple of that volatility away from the recent price extreme. Its name, "Chandelier Exit," comes from the idea of hanging the stop-loss from the "chandelier" of the highest high (for long positions) or lowest low (for short positions) of the trend. + +## 2. Mathematical Foundations and Calculation Logic + +The indicator uses the highest high or lowest low over a period as an anchor and then subtracts or adds a multiple of the ATR to create the trailing stop level. + +### Required Components + +- **ATR Period (N):** The lookback period for all calculations (ATR, Highest High, Lowest Low). +- **ATR Multiplier (M):** The factor by which the ATR is multiplied to determine the stop distance. +- **Price Data:** The `High`, `Low`, and `Close` of each bar. + +### Calculation Steps (Algorithm) + +1. **Calculate the Average True Range (ATR):** Compute the ATR for the period `N` using Wilder's smoothing method. + +2. **Calculate the Raw Stop Levels:** For each bar, determine the two potential stop levels: + + - **Long Stop (for uptrends):** Find the highest high over the last `N` bars and subtract the ATR value multiplied by the factor `M`. + $\text{Long Stop}_i = \text{Highest High}_{N} - (M \times \text{ATR}_i)$ + - **Short Stop (for downtrends):** Find the lowest low over the last `N` bars and add the ATR value multiplied by the factor `M`. + $\text{Short Stop}_i = \text{Lowest Low}_{N} + (M \times \text{ATR}_i)$ + +3. **Determine the Trend Direction:** The trend is determined by comparing the closing price to the _previous_ bar's stop levels. + + - The trend flips to **up** if the current `Close` crosses **above** the previous bar's `Short Stop`. + - The trend flips to **down** if the current `Close` crosses **below** the previous bar's `Long Stop`. + - If neither condition is met, the trend continues from the previous bar. + +4. **Plot the Final Trailing Stop Line:** The final plotted line incorporates the "trailing" logic, meaning it can only move in the direction of the trend (up for a long stop, down for a short stop). + - If the trend is **up**: + $\text{Final Stop}_i = \text{Max}(\text{Long Stop}_i, \text{Final Stop}_{i-1})$ + - If the trend is **down**: + $\text{Final Stop}_i = \text{Min}(\text{Short Stop}_i, \text{Final Stop}_{i-1})$ + - _(Note: On a trend flip, the new stop is taken directly from the calculated `Long Stop` or `Short Stop` without comparison to the previous value.)_ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is a self-contained, robust, and accurate representation of the Chandelier Exit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like this, it is the most reliable method to ensure stability. + +- **Consensus Wilder Algorithm:** The ATR calculation strictly follows our established two-step algorithm for Wilder's smoothing (manual SMA for initialization, followed by the recursive formula), ensuring consistency with our other indicators and the global standard (e.g., TradingView). + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the complex logic easy to follow: + + 1. **Step 1:** The True Range is calculated. + 2. **Step 2:** The ATR is calculated from the True Range values. + 3. **Step 3:** The raw `Long Stop` and `Short Stop` levels are calculated. + 4. **Step 4:** The final loop determines the trend and applies the "trailing" logic to plot the final stop line. + +- **Visual Representation:** The implementation ensures that trend changes are represented by a clean, vertical line connecting the old and new trend lines, providing continuous visual information. + +- **Heikin Ashi Variant (`ATR_TrailingStop_HeikinAshi.mq5`):** + - Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values for all its inputs (ATR, Highest/Lowest price, and trend-flip condition). + - This results in a smoother trailing stop that is less susceptible to being triggered by the "noise" of standard candlesticks, making it well-suited for Heikin Ashi-based trend-following strategies. + +## 4. Parameters + +- **ATR Period (`InpAtrPeriod`):** The lookback period for the ATR and the Highest High / Lowest Low calculations. A longer period results in a smoother, slower-reacting stop. Default is `22`. +- **Multiplier (`InpMultiplier`):** The factor to multiply the ATR by. A larger multiplier places the stop further from the price, allowing for more room for pullbacks but resulting in a larger potential loss if the stop is hit. A smaller multiplier keeps the stop tighter. Common values range from 2.5 to 3.5. Default is `3.0`. + +## 5. Usage and Interpretation + +- **Trailing Stop-Loss:** The primary use of the indicator is as a dynamic, volatility-adjusted trailing stop-loss. In an uptrend, the blue line represents the suggested stop level. In a downtrend, the red line represents the suggested stop level. +- **Trend Identification:** The color and position of the line provide a clear indication of the trend. A blue line below the price indicates an uptrend; a red line above the price indicates a downtrend. +- **Exit Signal:** The primary signal is for exiting a trade. A long position would be closed when the price closes below the blue line. A short position would be closed when the price closes above the red line. +- **Caution:** The Chandelier Exit is an excellent tool for letting profits run in a strong trend. However, in sideways or choppy markets, it can lead to being stopped out prematurely. It is most effective when used to manage trades within an already identified, strong trend. diff --git a/Indicators/MyIndicators/ATR_TrailingStop.mq5 b/Indicators/MyIndicators/ATR_TrailingStop.mq5 index 1f19e41..5fe0b50 100644 --- a/Indicators/MyIndicators/ATR_TrailingStop.mq5 +++ b/Indicators/MyIndicators/ATR_TrailingStop.mq5 @@ -1,204 +1,204 @@ -//+------------------------------------------------------------------+ -//| ATR_TrailingStop.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.00" // Final version using robust, staged calculation -#property description "ATR Trailing Stop (Chandelier Exit) using Wilder's ATR" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 6 // Main, Color, ATR, Long, Short, Trend -#property indicator_plots 1 - -//--- Plot 1: ATR Trailing Stop line -#property indicator_label1 "ATR Trailing Stop" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrDodgerBlue, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAtrPeriod = 22; // ATR Period -input double InpMultiplier = 3.0; // ATR Multiplier - -//--- Indicator Buffers --- -double BufferStopLine[]; -double BufferColor[]; -double BufferATR[]; -double BufferLongStop[]; -double BufferShortStop[]; -double BufferTrend[]; - -//--- Global Variables --- -int g_ExtAtrPeriod; -double g_ExtMultiplier; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtMultiplier = (InpMultiplier <= 0) ? 3.0 : InpMultiplier; - - SetIndexBuffer(0, BufferStopLine, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferLongStop, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferShortStop, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferStopLine, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferATR, false); - ArraySetAsSeries(BufferLongStop, false); - ArraySetAsSeries(BufferShortStop, false); - ArraySetAsSeries(BufferTrend, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ATR Stop(%d, %.1f)", g_ExtAtrPeriod, g_ExtMultiplier)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| ATR Trailing Stop calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- STEP 1: Calculate True Range - double tr[]; - ArrayResize(tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); - } - -//--- STEP 2: Calculate ATR (Wilder's Smoothing) - for(int i = g_ExtAtrPeriod; i < rates_total; i++) - { - if(i == g_ExtAtrPeriod) // Initialization with a simple average of TR - { - double atr_sum = 0; - for(int j = 1; j <= g_ExtAtrPeriod; j++) - atr_sum += tr[j]; - BufferATR[i] = atr_sum / g_ExtAtrPeriod; - } - else // Recursive calculation - { - BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; - } - } - -//--- STEP 3: Calculate Raw Stop Levels - for(int i = g_ExtAtrPeriod -1; i < rates_total; i++) - { - BufferLongStop[i] = Highest(high, g_ExtAtrPeriod, i) - g_ExtMultiplier * BufferATR[i]; - BufferShortStop[i] = Lowest(low, g_ExtAtrPeriod, i) + g_ExtMultiplier * BufferATR[i]; - } - -//--- STEP 4: Determine Trend and Final Stop Line - for(int i = g_ExtAtrPeriod; i < rates_total; i++) - { - // Determine trend direction - if(i == g_ExtAtrPeriod) // Initialization - { - if(close[i] > close[i-1]) - BufferTrend[i] = 1; - else - BufferTrend[i] = -1; - } - else - { - if(close[i] > BufferShortStop[i-1]) - BufferTrend[i] = 1; - else - if(close[i] < BufferLongStop[i-1]) - BufferTrend[i] = -1; - else - BufferTrend[i] = BufferTrend[i-1]; - } - - // Set the final Stop Line value and color - if(BufferTrend[i] == 1) - { - // Trailing logic: stop can only go up or stay - if(BufferLongStop[i] > BufferStopLine[i-1] || BufferTrend[i-1] == -1) - BufferStopLine[i] = BufferLongStop[i]; - else - BufferStopLine[i] = BufferStopLine[i-1]; - - BufferColor[i] = 0; // Blue - } - else // Trend is -1 - { - // Trailing logic: stop can only go down or stay - if(BufferShortStop[i] < BufferStopLine[i-1] || BufferStopLine[i-1] == 0 || BufferTrend[i-1] == 1) - BufferStopLine[i] = BufferShortStop[i]; - else - BufferStopLine[i] = BufferStopLine[i-1]; - - BufferColor[i] = 1; // Tomato - } - - // Connect lines on trend change - if(BufferTrend[i] != BufferTrend[i-1]) - { - if(BufferTrend[i] == 1) - BufferStopLine[i-1] = BufferLongStop[i]; - else - BufferStopLine[i-1] = BufferShortStop[i]; - } - } - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res < array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res > array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ATR_TrailingStop.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.00" // Final version using robust, staged calculation +#property description "ATR Trailing Stop (Chandelier Exit) using Wilder's ATR" + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 6 // Main, Color, ATR, Long, Short, Trend +#property indicator_plots 1 + +//--- Plot 1: ATR Trailing Stop line +#property indicator_label1 "ATR Trailing Stop" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrDodgerBlue, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAtrPeriod = 22; // ATR Period +input double InpMultiplier = 3.0; // ATR Multiplier + +//--- Indicator Buffers --- +double BufferStopLine[]; +double BufferColor[]; +double BufferATR[]; +double BufferLongStop[]; +double BufferShortStop[]; +double BufferTrend[]; + +//--- Global Variables --- +int g_ExtAtrPeriod; +double g_ExtMultiplier; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtMultiplier = (InpMultiplier <= 0) ? 3.0 : InpMultiplier; + + SetIndexBuffer(0, BufferStopLine, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferLongStop, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferShortStop, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferStopLine, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferATR, false); + ArraySetAsSeries(BufferLongStop, false); + ArraySetAsSeries(BufferShortStop, false); + ArraySetAsSeries(BufferTrend, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("ATR Stop(%d, %.1f)", g_ExtAtrPeriod, g_ExtMultiplier)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| ATR Trailing Stop calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- STEP 1: Calculate True Range + double tr[]; + ArrayResize(tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); + } + +//--- STEP 2: Calculate ATR (Wilder's Smoothing) + for(int i = g_ExtAtrPeriod; i < rates_total; i++) + { + if(i == g_ExtAtrPeriod) // Initialization with a simple average of TR + { + double atr_sum = 0; + for(int j = 1; j <= g_ExtAtrPeriod; j++) + atr_sum += tr[j]; + BufferATR[i] = atr_sum / g_ExtAtrPeriod; + } + else // Recursive calculation + { + BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; + } + } + +//--- STEP 3: Calculate Raw Stop Levels + for(int i = g_ExtAtrPeriod -1; i < rates_total; i++) + { + BufferLongStop[i] = Highest(high, g_ExtAtrPeriod, i) - g_ExtMultiplier * BufferATR[i]; + BufferShortStop[i] = Lowest(low, g_ExtAtrPeriod, i) + g_ExtMultiplier * BufferATR[i]; + } + +//--- STEP 4: Determine Trend and Final Stop Line + for(int i = g_ExtAtrPeriod; i < rates_total; i++) + { + // Determine trend direction + if(i == g_ExtAtrPeriod) // Initialization + { + if(close[i] > close[i-1]) + BufferTrend[i] = 1; + else + BufferTrend[i] = -1; + } + else + { + if(close[i] > BufferShortStop[i-1]) + BufferTrend[i] = 1; + else + if(close[i] < BufferLongStop[i-1]) + BufferTrend[i] = -1; + else + BufferTrend[i] = BufferTrend[i-1]; + } + + // Set the final Stop Line value and color + if(BufferTrend[i] == 1) + { + // Trailing logic: stop can only go up or stay + if(BufferLongStop[i] > BufferStopLine[i-1] || BufferTrend[i-1] == -1) + BufferStopLine[i] = BufferLongStop[i]; + else + BufferStopLine[i] = BufferStopLine[i-1]; + + BufferColor[i] = 0; // Blue + } + else // Trend is -1 + { + // Trailing logic: stop can only go down or stay + if(BufferShortStop[i] < BufferStopLine[i-1] || BufferStopLine[i-1] == 0 || BufferTrend[i-1] == 1) + BufferStopLine[i] = BufferShortStop[i]; + else + BufferStopLine[i] = BufferStopLine[i-1]; + + BufferColor[i] = 1; // Tomato + } + + // Connect lines on trend change + if(BufferTrend[i] != BufferTrend[i-1]) + { + if(BufferTrend[i] == 1) + BufferStopLine[i-1] = BufferLongStop[i]; + else + BufferStopLine[i-1] = BufferShortStop[i]; + } + } + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res < array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res > array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/ATR_TrailingStop_HeikinAshi.mq5 b/Indicators/MyIndicators/ATR_TrailingStop_HeikinAshi.mq5 index 34ea83f..fe26df1 100644 --- a/Indicators/MyIndicators/ATR_TrailingStop_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/ATR_TrailingStop_HeikinAshi.mq5 @@ -1,230 +1,230 @@ -//+------------------------------------------------------------------+ -//| ATR_TrailingStop_HeikinAshi.mq5| -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "1.00" -#property description "ATR Trailing Stop (Chandelier Exit) on Heikin Ashi data" - -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 6 // Main, Color, ATR, Long, Short, Trend -#property indicator_plots 1 - -//--- Plot 1: ATR Trailing Stop line -#property indicator_label1 "HA ATR Trailing Stop" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrDodgerBlue, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAtrPeriod = 22; // ATR Period -input double InpMultiplier = 3.0; // ATR Multiplier - -//--- Indicator Buffers --- -double BufferStopLine[]; -double BufferColor[]; -double BufferHA_ATR[]; -double BufferLongStop[]; -double BufferShortStop[]; -double BufferTrend[]; - -//--- Global Objects and Variables --- -int g_ExtAtrPeriod; -double g_ExtMultiplier; -CHeikinAshi_Calculator *g_ha_calculator; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtMultiplier = (InpMultiplier <= 0) ? 3.0 : InpMultiplier; - - SetIndexBuffer(0, BufferStopLine, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferHA_ATR, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferLongStop, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferShortStop, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferStopLine, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferHA_ATR, false); - ArraySetAsSeries(BufferLongStop, false); - ArraySetAsSeries(BufferShortStop, false); - ArraySetAsSeries(BufferTrend, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA ATR Stop(%d, %.1f)", g_ExtAtrPeriod, g_ExtMultiplier)); - - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| ATR Trailing Stop on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- Intermediate Heikin Ashi Buffers - double ha_open[], ha_high[], ha_low[], ha_close[]; - ArrayResize(ha_open, rates_total); - ArrayResize(ha_high, rates_total); - ArrayResize(ha_low, rates_total); - ArrayResize(ha_close, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); - -//--- STEP 2: Calculate Heikin Ashi True Range - double ha_tr[]; - ArrayResize(ha_tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - ha_tr[i] = MathMax(ha_high[i], ha_close[i-1]) - MathMin(ha_low[i], ha_close[i-1]); - } - -//--- STEP 3: Calculate ATR and Trailing Stop in staged loops -// --- 3a: Calculate Heikin Ashi ATR --- - for(int i = g_ExtAtrPeriod; i < rates_total; i++) - { - if(i == g_ExtAtrPeriod) - { - double atr_sum = 0; - for(int j = 1; j <= g_ExtAtrPeriod; j++) - atr_sum += ha_tr[j]; - BufferHA_ATR[i] = atr_sum / g_ExtAtrPeriod; - } - else - { - BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; - } - } - -// --- 3b: Calculate Raw Stop Levels from HA data --- - for(int i = g_ExtAtrPeriod -1; i < rates_total; i++) - { - BufferLongStop[i] = Highest(ha_high, g_ExtAtrPeriod, i) - g_ExtMultiplier * BufferHA_ATR[i]; - BufferShortStop[i] = Lowest(ha_low, g_ExtAtrPeriod, i) + g_ExtMultiplier * BufferHA_ATR[i]; - } - -// --- 3c: Determine Trend and Final Stop Line --- - for(int i = g_ExtAtrPeriod; i < rates_total; i++) - { - if(i == g_ExtAtrPeriod) - { - if(ha_close[i] > ha_close[i-1]) - BufferTrend[i] = 1; - else - BufferTrend[i] = -1; - } - else - { - if(ha_close[i] > BufferShortStop[i-1]) - BufferTrend[i] = 1; - else - if(ha_close[i] < BufferLongStop[i-1]) - BufferTrend[i] = -1; - else - BufferTrend[i] = BufferTrend[i-1]; - } - - if(BufferTrend[i] == 1) - { - if(BufferLongStop[i] > BufferStopLine[i-1] || BufferTrend[i-1] == -1) - BufferStopLine[i] = BufferLongStop[i]; - else - BufferStopLine[i] = BufferStopLine[i-1]; - BufferColor[i] = 0; - } - else - { - if(BufferShortStop[i] < BufferStopLine[i-1] || BufferStopLine[i-1] == 0 || BufferTrend[i-1] == 1) - BufferStopLine[i] = BufferShortStop[i]; - else - BufferStopLine[i] = BufferStopLine[i-1]; - BufferColor[i] = 1; - } - - if(BufferTrend[i] != BufferTrend[i-1]) - { - if(BufferTrend[i] == 1) - BufferStopLine[i-1] = BufferLongStop[i]; - else - BufferStopLine[i-1] = BufferShortStop[i]; - } - } - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res < array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res > array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| ATR_TrailingStop_HeikinAshi.mq5| +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "1.00" +#property description "ATR Trailing Stop (Chandelier Exit) on Heikin Ashi data" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 6 // Main, Color, ATR, Long, Short, Trend +#property indicator_plots 1 + +//--- Plot 1: ATR Trailing Stop line +#property indicator_label1 "HA ATR Trailing Stop" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrDodgerBlue, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAtrPeriod = 22; // ATR Period +input double InpMultiplier = 3.0; // ATR Multiplier + +//--- Indicator Buffers --- +double BufferStopLine[]; +double BufferColor[]; +double BufferHA_ATR[]; +double BufferLongStop[]; +double BufferShortStop[]; +double BufferTrend[]; + +//--- Global Objects and Variables --- +int g_ExtAtrPeriod; +double g_ExtMultiplier; +CHeikinAshi_Calculator *g_ha_calculator; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtMultiplier = (InpMultiplier <= 0) ? 3.0 : InpMultiplier; + + SetIndexBuffer(0, BufferStopLine, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferHA_ATR, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferLongStop, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferShortStop, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferStopLine, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferHA_ATR, false); + ArraySetAsSeries(BufferLongStop, false); + ArraySetAsSeries(BufferShortStop, false); + ArraySetAsSeries(BufferTrend, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA ATR Stop(%d, %.1f)", g_ExtAtrPeriod, g_ExtMultiplier)); + + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| ATR Trailing Stop on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- Intermediate Heikin Ashi Buffers + double ha_open[], ha_high[], ha_low[], ha_close[]; + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + +//--- STEP 2: Calculate Heikin Ashi True Range + double ha_tr[]; + ArrayResize(ha_tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + ha_tr[i] = MathMax(ha_high[i], ha_close[i-1]) - MathMin(ha_low[i], ha_close[i-1]); + } + +//--- STEP 3: Calculate ATR and Trailing Stop in staged loops +// --- 3a: Calculate Heikin Ashi ATR --- + for(int i = g_ExtAtrPeriod; i < rates_total; i++) + { + if(i == g_ExtAtrPeriod) + { + double atr_sum = 0; + for(int j = 1; j <= g_ExtAtrPeriod; j++) + atr_sum += ha_tr[j]; + BufferHA_ATR[i] = atr_sum / g_ExtAtrPeriod; + } + else + { + BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; + } + } + +// --- 3b: Calculate Raw Stop Levels from HA data --- + for(int i = g_ExtAtrPeriod -1; i < rates_total; i++) + { + BufferLongStop[i] = Highest(ha_high, g_ExtAtrPeriod, i) - g_ExtMultiplier * BufferHA_ATR[i]; + BufferShortStop[i] = Lowest(ha_low, g_ExtAtrPeriod, i) + g_ExtMultiplier * BufferHA_ATR[i]; + } + +// --- 3c: Determine Trend and Final Stop Line --- + for(int i = g_ExtAtrPeriod; i < rates_total; i++) + { + if(i == g_ExtAtrPeriod) + { + if(ha_close[i] > ha_close[i-1]) + BufferTrend[i] = 1; + else + BufferTrend[i] = -1; + } + else + { + if(ha_close[i] > BufferShortStop[i-1]) + BufferTrend[i] = 1; + else + if(ha_close[i] < BufferLongStop[i-1]) + BufferTrend[i] = -1; + else + BufferTrend[i] = BufferTrend[i-1]; + } + + if(BufferTrend[i] == 1) + { + if(BufferLongStop[i] > BufferStopLine[i-1] || BufferTrend[i-1] == -1) + BufferStopLine[i] = BufferLongStop[i]; + else + BufferStopLine[i] = BufferStopLine[i-1]; + BufferColor[i] = 0; + } + else + { + if(BufferShortStop[i] < BufferStopLine[i-1] || BufferStopLine[i-1] == 0 || BufferTrend[i-1] == 1) + BufferStopLine[i] = BufferShortStop[i]; + else + BufferStopLine[i] = BufferStopLine[i-1]; + BufferColor[i] = 1; + } + + if(BufferTrend[i] != BufferTrend[i-1]) + { + if(BufferTrend[i] == 1) + BufferStopLine[i-1] = BufferLongStop[i]; + else + BufferStopLine[i-1] = BufferShortStop[i]; + } + } + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res < array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res > array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Chart_HeikinAshi.md b/Indicators/MyIndicators/Chart_HeikinAshi.md index 48b66d4..b24f5bc 100644 --- a/Indicators/MyIndicators/Chart_HeikinAshi.md +++ b/Indicators/MyIndicators/Chart_HeikinAshi.md @@ -1,63 +1,63 @@ -# Heikin Ashi - -## 1. Summary (Introduction) - -Heikin Ashi, which translates to "average bar" in Japanese, is a candlestick charting technique that modifies how price is displayed on a chart. Unlike standard candlesticks that use the raw Open, High, Low, and Close (OHLC) values for each period, Heikin Ashi uses a formula that incorporates data from the previous bar to create a smoother, more flowing representation of the market trend. - -Its primary purpose is to filter out market noise and make it easier for traders to identify the underlying trend direction and strength. Heikin Ashi charts are characterized by long periods of consecutive bullish (e.g., blue) or bearish (e.g., red) candles with minimal "noise," making them popular among trend-following traders. - -## 2. Mathematical Foundations and Calculation Logic - -The Heikin Ashi candles are calculated using a specific set of formulas that average the price action. - -### Required Components - -- Standard OHLC price data. -- The previous Heikin Ashi Open and Close values. - -### Calculation Steps (Algorithm) - -The calculation for each Heikin Ashi (HA) candle is as follows: - -1. **HA Close:** The average price of the current standard bar. - $\text{HA Close}_i = \frac{\text{Open}_i + \text{High}_i + \text{Low}_i + \text{Close}_i}{4}$ - -2. **HA Open:** The midpoint of the previous Heikin Ashi bar's body. - $\text{HA Open}_i = \frac{\text{HA Open}_{i-1} + \text{HA Close}_{i-1}}{2}$ - -3. **HA High:** The highest value among the current standard High, the current HA Open, and the current HA Close. - $\text{HA High}_i = \text{Max}(\text{High}_i, \text{HA Open}_i, \text{HA Close}_i)$ - -4. **HA Low:** The lowest value among the current standard Low, the current HA Open, and the current HA Close. - $\text{HA Low}_i = \text{Min}(\text{Low}_i, \text{HA Open}_i, \text{HA Close}_i)$ - -_Note: For the very first bar on the chart where no previous HA bar exists, the HA Open is typically calculated as `(Open + Close) / 2`, and the HA High/Low are the same as the standard High/Low._ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is a showcase of our core development principles, designed for maximum stability and modularity. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a recursive indicator like Heikin Ashi (where each bar depends on the previous one), this is the most robust method to ensure the chart displays correctly without glitches, especially during timeframe changes or history loading. - -- **Modularity and Reusability:** The core calculation logic is not located in the main `.mq5` file. Instead, it is encapsulated within the `CHeikinAshi_Calculator` class, which resides in our central `HeikinAshi_Tools.mqh` library. This approach offers several advantages: - - - The main indicator file (`Chart_HeikinAshi.mq5`) remains extremely clean and simple. Its only jobs are to manage the indicator buffers and call the calculator. - - The `CHeikinAshi_Calculator` class can be easily reused by any other indicator in our toolkit that needs Heikin Ashi data, promoting a Don't-Repeat-Yourself (DRY) coding practice. - -- **Clean Object-Oriented Structure:** The indicator uses a pointer (`g_ha_calculator`) to an instance of the `CHeikinAshi_Calculator` class. This object is properly created in `OnInit` and destroyed in `OnDeinit`, preventing any memory leaks and adhering to sound object-oriented programming principles. - -- **Direct Buffer Calculation:** The `CHeikinAshi_Calculator` is designed to be "stateless." It does not manage its own data but calculates the Heikin Ashi values directly into the buffers provided by the calling indicator. This makes the `OnCalculate` function highly efficient, as it avoids any intermediate data copying. - -## 4. Parameters - -The Heikin Ashi indicator itself has no adjustable parameters. Its calculation is based solely on the underlying price data. - -## 5. Usage and Interpretation - -Heikin Ashi charts are read differently from standard candlestick charts. - -- **Strong Uptrend:** Characterized by a series of long-bodied bullish candles (blue) with little to no lower wicks (shadows). -- **Strong Downtrend:** Characterized by a series of long-bodied bearish candles (red) with little to no upper wicks. -- **Trend Weakening / Consolidation:** The appearance of smaller bodies and longer wicks in both directions suggests a potential pause, consolidation, or weakening of the current trend. -- **Trend Reversal:** A change in candle color (e.g., from a series of red to the first blue candle) can signal a potential trend reversal. -- **Caution:** Because Heikin Ashi is a lagging indicator due to its averaging nature, it is slower to react to rapid price changes. The price displayed by the Heikin Ashi candles may differ from the actual market price at which trades can be executed. It is primarily a tool for trend visualization and confirmation, not for precise entry timing. +# Heikin Ashi + +## 1. Summary (Introduction) + +Heikin Ashi, which translates to "average bar" in Japanese, is a candlestick charting technique that modifies how price is displayed on a chart. Unlike standard candlesticks that use the raw Open, High, Low, and Close (OHLC) values for each period, Heikin Ashi uses a formula that incorporates data from the previous bar to create a smoother, more flowing representation of the market trend. + +Its primary purpose is to filter out market noise and make it easier for traders to identify the underlying trend direction and strength. Heikin Ashi charts are characterized by long periods of consecutive bullish (e.g., blue) or bearish (e.g., red) candles with minimal "noise," making them popular among trend-following traders. + +## 2. Mathematical Foundations and Calculation Logic + +The Heikin Ashi candles are calculated using a specific set of formulas that average the price action. + +### Required Components + +- Standard OHLC price data. +- The previous Heikin Ashi Open and Close values. + +### Calculation Steps (Algorithm) + +The calculation for each Heikin Ashi (HA) candle is as follows: + +1. **HA Close:** The average price of the current standard bar. + $\text{HA Close}_i = \frac{\text{Open}_i + \text{High}_i + \text{Low}_i + \text{Close}_i}{4}$ + +2. **HA Open:** The midpoint of the previous Heikin Ashi bar's body. + $\text{HA Open}_i = \frac{\text{HA Open}_{i-1} + \text{HA Close}_{i-1}}{2}$ + +3. **HA High:** The highest value among the current standard High, the current HA Open, and the current HA Close. + $\text{HA High}_i = \text{Max}(\text{High}_i, \text{HA Open}_i, \text{HA Close}_i)$ + +4. **HA Low:** The lowest value among the current standard Low, the current HA Open, and the current HA Close. + $\text{HA Low}_i = \text{Min}(\text{Low}_i, \text{HA Open}_i, \text{HA Close}_i)$ + +_Note: For the very first bar on the chart where no previous HA bar exists, the HA Open is typically calculated as `(Open + Close) / 2`, and the HA High/Low are the same as the standard High/Low._ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is a showcase of our core development principles, designed for maximum stability and modularity. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a recursive indicator like Heikin Ashi (where each bar depends on the previous one), this is the most robust method to ensure the chart displays correctly without glitches, especially during timeframe changes or history loading. + +- **Modularity and Reusability:** The core calculation logic is not located in the main `.mq5` file. Instead, it is encapsulated within the `CHeikinAshi_Calculator` class, which resides in our central `HeikinAshi_Tools.mqh` library. This approach offers several advantages: + + - The main indicator file (`Chart_HeikinAshi.mq5`) remains extremely clean and simple. Its only jobs are to manage the indicator buffers and call the calculator. + - The `CHeikinAshi_Calculator` class can be easily reused by any other indicator in our toolkit that needs Heikin Ashi data, promoting a Don't-Repeat-Yourself (DRY) coding practice. + +- **Clean Object-Oriented Structure:** The indicator uses a pointer (`g_ha_calculator`) to an instance of the `CHeikinAshi_Calculator` class. This object is properly created in `OnInit` and destroyed in `OnDeinit`, preventing any memory leaks and adhering to sound object-oriented programming principles. + +- **Direct Buffer Calculation:** The `CHeikinAshi_Calculator` is designed to be "stateless." It does not manage its own data but calculates the Heikin Ashi values directly into the buffers provided by the calling indicator. This makes the `OnCalculate` function highly efficient, as it avoids any intermediate data copying. + +## 4. Parameters + +The Heikin Ashi indicator itself has no adjustable parameters. Its calculation is based solely on the underlying price data. + +## 5. Usage and Interpretation + +Heikin Ashi charts are read differently from standard candlestick charts. + +- **Strong Uptrend:** Characterized by a series of long-bodied bullish candles (blue) with little to no lower wicks (shadows). +- **Strong Downtrend:** Characterized by a series of long-bodied bearish candles (red) with little to no upper wicks. +- **Trend Weakening / Consolidation:** The appearance of smaller bodies and longer wicks in both directions suggests a potential pause, consolidation, or weakening of the current trend. +- **Trend Reversal:** A change in candle color (e.g., from a series of red to the first blue candle) can signal a potential trend reversal. +- **Caution:** Because Heikin Ashi is a lagging indicator due to its averaging nature, it is slower to react to rapid price changes. The price displayed by the Heikin Ashi candles may differ from the actual market price at which trades can be executed. It is primarily a tool for trend visualization and confirmation, not for precise entry timing. diff --git a/Indicators/MyIndicators/Chart_HeikinAshi.mq5 b/Indicators/MyIndicators/Chart_HeikinAshi.mq5 index 556cbdd..eb6cfec 100644 --- a/Indicators/MyIndicators/Chart_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/Chart_HeikinAshi.mq5 @@ -1,120 +1,120 @@ -//+------------------------------------------------------------------+ -//| Chart_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.00" // Refactored for stability and new calculator -#property description "Draws Heikin Ashi candles on the main chart." - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window // Draw on the main chart window -#property indicator_buffers 5 // 4 for OHLC, 1 for color -#property indicator_plots 1 - -//--- Plot 1: Heikin Ashi Candles -#property indicator_type1 DRAW_COLOR_CANDLES -#property indicator_color1 clrDodgerBlue, clrMaroon // Up and Down colors -#property indicator_label1 "HA Open;HA High;HA Low;HA Close" // Labels for Data Window - -//--- Indicator Buffers --- -double BufferHA_Open[]; -double BufferHA_High[]; -double BufferHA_Low[]; -double BufferHA_Close[]; -double BufferColor[]; // Buffer for candle colors - -//--- Global Objects --- -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Map the buffers to the indicator's internal memory - SetIndexBuffer(0, BufferHA_Open, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_High, INDICATOR_DATA); - SetIndexBuffer(2, BufferHA_Low, INDICATOR_DATA); - SetIndexBuffer(3, BufferHA_Close, INDICATOR_DATA); - SetIndexBuffer(4, BufferColor, INDICATOR_COLOR_INDEX); - -//--- Set buffers to non-timeseries for stable calculation - ArraySetAsSeries(BufferHA_Open, false); - ArraySetAsSeries(BufferHA_High, false); - ArraySetAsSeries(BufferHA_Low, false); - ArraySetAsSeries(BufferHA_Close, false); - ArraySetAsSeries(BufferColor, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); // Use the same precision as the symbol - IndicatorSetString(INDICATOR_SHORTNAME, "Heikin Ashi"); - PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); // Define the empty value for the plot - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data - if(rates_total < 2) - return(0); - -//--- STEP 1: Calculate Heikin Ashi bars directly into the indicator buffers - g_ha_calculator.Calculate(rates_total, open, high, low, close, - BufferHA_Open, BufferHA_High, BufferHA_Low, BufferHA_Close); - -//--- STEP 2: Set the colors for the candles - for(int i = 0; i < rates_total; i++) - { - //--- Set the color for the current candle - // Color index 0 (clrDodgerBlue) for bullish candles - // Color index 1 (clrMaroon) for bearish candles - if(BufferHA_Open[i] < BufferHA_Close[i]) - BufferColor[i] = 0.0; // Bullish - else - BufferColor[i] = 1.0; // Bearish - } - -//--- Return value of rates_total to signal a full recalculation - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Chart_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.00" // Refactored for stability and new calculator +#property description "Draws Heikin Ashi candles on the main chart." + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window // Draw on the main chart window +#property indicator_buffers 5 // 4 for OHLC, 1 for color +#property indicator_plots 1 + +//--- Plot 1: Heikin Ashi Candles +#property indicator_type1 DRAW_COLOR_CANDLES +#property indicator_color1 clrDodgerBlue, clrMaroon // Up and Down colors +#property indicator_label1 "HA Open;HA High;HA Low;HA Close" // Labels for Data Window + +//--- Indicator Buffers --- +double BufferHA_Open[]; +double BufferHA_High[]; +double BufferHA_Low[]; +double BufferHA_Close[]; +double BufferColor[]; // Buffer for candle colors + +//--- Global Objects --- +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Map the buffers to the indicator's internal memory + SetIndexBuffer(0, BufferHA_Open, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_High, INDICATOR_DATA); + SetIndexBuffer(2, BufferHA_Low, INDICATOR_DATA); + SetIndexBuffer(3, BufferHA_Close, INDICATOR_DATA); + SetIndexBuffer(4, BufferColor, INDICATOR_COLOR_INDEX); + +//--- Set buffers to non-timeseries for stable calculation + ArraySetAsSeries(BufferHA_Open, false); + ArraySetAsSeries(BufferHA_High, false); + ArraySetAsSeries(BufferHA_Low, false); + ArraySetAsSeries(BufferHA_Close, false); + ArraySetAsSeries(BufferColor, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); // Use the same precision as the symbol + IndicatorSetString(INDICATOR_SHORTNAME, "Heikin Ashi"); + PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); // Define the empty value for the plot + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data + if(rates_total < 2) + return(0); + +//--- STEP 1: Calculate Heikin Ashi bars directly into the indicator buffers + g_ha_calculator.Calculate(rates_total, open, high, low, close, + BufferHA_Open, BufferHA_High, BufferHA_Low, BufferHA_Close); + +//--- STEP 2: Set the colors for the candles + for(int i = 0; i < rates_total; i++) + { + //--- Set the color for the current candle + // Color index 0 (clrDodgerBlue) for bullish candles + // Color index 1 (clrMaroon) for bearish candles + if(BufferHA_Open[i] < BufferHA_Close[i]) + BufferColor[i] = 0.0; // Bullish + else + BufferColor[i] = 1.0; // Bearish + } + +//--- Return value of rates_total to signal a full recalculation + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/CutlerRSI_MA.md b/Indicators/MyIndicators/CutlerRSI_MA.md index 09a9f51..c2aac69 100644 --- a/Indicators/MyIndicators/CutlerRSI_MA.md +++ b/Indicators/MyIndicators/CutlerRSI_MA.md @@ -1,72 +1,72 @@ -# Cutler's RSI - -## 1. Summary (Introduction) - -Cutler's RSI is a variation of the classic Relative Strength Index (RSI) developed by J. Welles Wilder. While the standard RSI uses Wilder's own smoothing method (a type of Smoothed/Running Moving Average), Cutler's version simplifies the formula by using a **Simple Moving Average (SMA)** to average the positive and negative price changes. - -This modification results in an oscillator that can react slightly differently to price movements compared to the standard RSI. It is still a momentum oscillator used to identify overbought and oversold conditions, but its SMA-based calculation gives it a unique character. - -## 2. Mathematical Foundations and Calculation Logic - -The core difference between Cutler's RSI and the standard RSI lies in the smoothing method applied to the price changes. - -### Required Components - -- **RSI Period (N):** The lookback period for the calculation. -- **Source Price (P):** The price series used for the calculation (e.g., Close). - -### Calculation Steps (Algorithm) - -1. **Calculate Price Changes:** For each period, determine the change in price from the previous period. - $\text{Change}_i = P_i - P_{i-1}$ - -2. **Separate Positive and Negative Changes:** - - - If $\text{Change}_i > 0$, then $\text{Positive Change}_i = \text{Change}_i$ and $\text{Negative Change}_i = 0$. - - If $\text{Change}_i < 0$, then $\text{Positive Change}_i = 0$ and $\text{Negative Change}_i = \text{Abs}(\text{Change}_i)$. - - If $\text{Change}_i = 0$, then both are 0. - -3. **Calculate the Simple Moving Average of Changes:** This is the defining step. Apply an SMA with period `N` to both the positive and negative change series. - $\text{Avg Positive}_i = \text{SMA}(\text{Positive Change}, N)_i$ - $\text{Avg Negative}_i = \text{SMA}(\text{Negative Change}, N)_i$ - -4. **Calculate the Relative Strength (RS) and Final RSI:** - $\text{RS}_i = \frac{\text{Avg Positive}_i}{\text{Avg Negative}_i}$ - $\text{Cutler's RSI}_i = 100 - \frac{100}{1 + \text{RS}_i}$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored for maximum stability, clarity, and computational efficiency. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for all indicators, especially those with a signal line involving recursive calculations (EMA/SMMA), to ensure stability. - -- **Efficient RSI Calculation:** Instead of using multiple loops or inefficient `SimpleMA` calls on every bar, we calculate the Cutler's RSI in a single `for` loop using an efficient **sliding window sum** technique. We maintain a running sum of positive and negative changes, adding the newest value and subtracting the oldest value in each iteration. This is mathematically equivalent to an SMA but significantly faster. - -- **Self-Contained Price Handling:** The indicator does not use external handles like `iMA`. It directly processes the price arrays (`open`, `high`, `low`, `close`) provided by `OnCalculate` and includes logic to handle all standard and calculated `ENUM_APPLIED_PRICE` types. - -- **Robust Signal Line:** The optional signal line (a moving average of the Cutler's RSI line) is calculated using our standard, robust `switch` block. - - - For recursive types (**EMA, SMMA**), the first value is initialized with a **manual SMA** calculation at the correct starting index to prevent the floating-point overflows that plague less robust implementations. - - For non-recursive types (**SMA, LWMA**), we safely use the functions from the `` library. - -- **Heikin Ashi Variant (`CutlerRSI_MA_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_close` values as its input. - - This results in a doubly-smoothed oscillator, ideal for traders who want to focus only on the most significant, sustained momentum shifts. - -## 4. Parameters - -- **RSI Period (`InpPeriodRSI`):** The lookback period for the SMA of price changes. Default is `14`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). -- **MA Period (`InpPeriodMA`):** The lookback period for the optional signal line. -- **MA Method (`InpMethodMA`):** The type of moving average for the signal line. - -## 5. Usage and Interpretation - -The interpretation of Cutler's RSI is identical to the standard RSI. - -- **Overbought/Oversold Levels:** The primary use is to identify overbought (typically above 70) and oversold (typically below 30) conditions. -- **Crossovers:** - - **Signal Line Crossover:** When the Cutler's RSI line crosses above its moving average, it can be seen as a bullish signal. A cross below is a bearish signal. - - **Centerline Crossover:** A crossover of the RSI line above the 50 level indicates that momentum is shifting to bullish. A crossover below 50 indicates bearish momentum. -- **Divergence:** Look for divergences between the RSI and the price action. A bearish divergence (higher price highs, lower RSI highs) can signal a potential top, while a bullish divergence (lower price lows, higher RSI highs) can signal a potential bottom. -- **Caution:** As a momentum oscillator, it is most effective in ranging or moderately trending markets. In a very strong trend, it can remain in overbought or oversold territory for extended periods. +# Cutler's RSI + +## 1. Summary (Introduction) + +Cutler's RSI is a variation of the classic Relative Strength Index (RSI) developed by J. Welles Wilder. While the standard RSI uses Wilder's own smoothing method (a type of Smoothed/Running Moving Average), Cutler's version simplifies the formula by using a **Simple Moving Average (SMA)** to average the positive and negative price changes. + +This modification results in an oscillator that can react slightly differently to price movements compared to the standard RSI. It is still a momentum oscillator used to identify overbought and oversold conditions, but its SMA-based calculation gives it a unique character. + +## 2. Mathematical Foundations and Calculation Logic + +The core difference between Cutler's RSI and the standard RSI lies in the smoothing method applied to the price changes. + +### Required Components + +- **RSI Period (N):** The lookback period for the calculation. +- **Source Price (P):** The price series used for the calculation (e.g., Close). + +### Calculation Steps (Algorithm) + +1. **Calculate Price Changes:** For each period, determine the change in price from the previous period. + $\text{Change}_i = P_i - P_{i-1}$ + +2. **Separate Positive and Negative Changes:** + + - If $\text{Change}_i > 0$, then $\text{Positive Change}_i = \text{Change}_i$ and $\text{Negative Change}_i = 0$. + - If $\text{Change}_i < 0$, then $\text{Positive Change}_i = 0$ and $\text{Negative Change}_i = \text{Abs}(\text{Change}_i)$. + - If $\text{Change}_i = 0$, then both are 0. + +3. **Calculate the Simple Moving Average of Changes:** This is the defining step. Apply an SMA with period `N` to both the positive and negative change series. + $\text{Avg Positive}_i = \text{SMA}(\text{Positive Change}, N)_i$ + $\text{Avg Negative}_i = \text{SMA}(\text{Negative Change}, N)_i$ + +4. **Calculate the Relative Strength (RS) and Final RSI:** + $\text{RS}_i = \frac{\text{Avg Positive}_i}{\text{Avg Negative}_i}$ + $\text{Cutler's RSI}_i = 100 - \frac{100}{1 + \text{RS}_i}$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored for maximum stability, clarity, and computational efficiency. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for all indicators, especially those with a signal line involving recursive calculations (EMA/SMMA), to ensure stability. + +- **Efficient RSI Calculation:** Instead of using multiple loops or inefficient `SimpleMA` calls on every bar, we calculate the Cutler's RSI in a single `for` loop using an efficient **sliding window sum** technique. We maintain a running sum of positive and negative changes, adding the newest value and subtracting the oldest value in each iteration. This is mathematically equivalent to an SMA but significantly faster. + +- **Self-Contained Price Handling:** The indicator does not use external handles like `iMA`. It directly processes the price arrays (`open`, `high`, `low`, `close`) provided by `OnCalculate` and includes logic to handle all standard and calculated `ENUM_APPLIED_PRICE` types. + +- **Robust Signal Line:** The optional signal line (a moving average of the Cutler's RSI line) is calculated using our standard, robust `switch` block. + + - For recursive types (**EMA, SMMA**), the first value is initialized with a **manual SMA** calculation at the correct starting index to prevent the floating-point overflows that plague less robust implementations. + - For non-recursive types (**SMA, LWMA**), we safely use the functions from the `` library. + +- **Heikin Ashi Variant (`CutlerRSI_MA_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_close` values as its input. + - This results in a doubly-smoothed oscillator, ideal for traders who want to focus only on the most significant, sustained momentum shifts. + +## 4. Parameters + +- **RSI Period (`InpPeriodRSI`):** The lookback period for the SMA of price changes. Default is `14`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). +- **MA Period (`InpPeriodMA`):** The lookback period for the optional signal line. +- **MA Method (`InpMethodMA`):** The type of moving average for the signal line. + +## 5. Usage and Interpretation + +The interpretation of Cutler's RSI is identical to the standard RSI. + +- **Overbought/Oversold Levels:** The primary use is to identify overbought (typically above 70) and oversold (typically below 30) conditions. +- **Crossovers:** + - **Signal Line Crossover:** When the Cutler's RSI line crosses above its moving average, it can be seen as a bullish signal. A cross below is a bearish signal. + - **Centerline Crossover:** A crossover of the RSI line above the 50 level indicates that momentum is shifting to bullish. A crossover below 50 indicates bearish momentum. +- **Divergence:** Look for divergences between the RSI and the price action. A bearish divergence (higher price highs, lower RSI highs) can signal a potential top, while a bullish divergence (lower price lows, higher RSI highs) can signal a potential bottom. +- **Caution:** As a momentum oscillator, it is most effective in ranging or moderately trending markets. In a very strong trend, it can remain in overbought or oversold territory for extended periods. diff --git a/Indicators/MyIndicators/CutlerRSI_MA.mq5 b/Indicators/MyIndicators/CutlerRSI_MA.mq5 index 7ea97ce..93930a8 100644 --- a/Indicators/MyIndicators/CutlerRSI_MA.mq5 +++ b/Indicators/MyIndicators/CutlerRSI_MA.mq5 @@ -1,207 +1,207 @@ -//+------------------------------------------------------------------+ -//| CutlerRSI_MA.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and efficiency -#property description "Cutler's RSI (SMA-based) with a signal line." - -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_minimum 0 -#property indicator_maximum 100 -#property indicator_level1 30.0 -#property indicator_level2 50.0 -#property indicator_level3 70.0 - -//--- Buffers and Plots --- -#property indicator_buffers 2 // CutlerRSI and its MA -#property indicator_plots 2 - -//--- Plot 1: MA line (smoothed) -#property indicator_label1 "MA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrRed -#property indicator_style1 STYLE_DOT -#property indicator_width1 1 - -//--- Plot 2: Cutler's RSI line (raw) -#property indicator_label2 "Cutler's RSI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpPeriodRSI = 14; // RSI Period -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // RSI Applied Price -input group "Signal Line Settings" -input int InpPeriodMA = 14; // MA Period -input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // MA Method - -//--- Indicator Buffers --- -double BufferCutlerRSI_MA[]; -double BufferCutlerRSI[]; - -//--- Global Variables --- -int g_ExtPeriodRSI; -int g_ExtPeriodMA; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; - g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; - - SetIndexBuffer(0, BufferCutlerRSI_MA, INDICATOR_DATA); - SetIndexBuffer(1, BufferCutlerRSI, INDICATOR_DATA); - - ArraySetAsSeries(BufferCutlerRSI_MA, false); - ArraySetAsSeries(BufferCutlerRSI, false); - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CutlerRSI(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -// No handles to release, but good practice to have the function - } - -//+------------------------------------------------------------------+ -//| Cutler's RSI calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Prepare the source price array - double price_source[]; - ArrayResize(price_source, rates_total); - for(int i=0; i 0) ? diff : 0; - double neg_change = (diff < 0) ? -diff : 0; - - sum_pos += pos_change; - sum_neg += neg_change; - - // Remove the oldest value from the sum once the window is full - if(i > g_ExtPeriodRSI) - { - double old_diff = price_source[i - g_ExtPeriodRSI] - price_source[i - g_ExtPeriodRSI - 1]; - sum_pos -= (old_diff > 0) ? old_diff : 0; - sum_neg -= (old_diff < 0) ? -old_diff : 0; - } - - if(i >= g_ExtPeriodRSI) - { - if(sum_neg > 0) - { - double rs = (sum_pos / g_ExtPeriodRSI) / (sum_neg / g_ExtPeriodRSI); - BufferCutlerRSI[i] = 100.0 - (100.0 / (1.0 + rs)); - } - else - { - BufferCutlerRSI[i] = 100.0; - } - } - } - -//--- STEP 3: Calculate the signal line (MA of Cutler's RSI) - for(int i = start_pos; i < rates_total; i++) - { - switch(InpMethodMA) - { - case MODE_EMA: - case MODE_SMMA: - if(i == start_pos) - { - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - sum += BufferCutlerRSI[i - j]; - BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; - } - else - { - if(InpMethodMA == MODE_EMA) - { - double pr = 2.0 / (g_ExtPeriodMA + 1.0); - BufferCutlerRSI_MA[i] = BufferCutlerRSI[i] * pr + BufferCutlerRSI_MA[i-1] * (1.0 - pr); - } - else - { - BufferCutlerRSI_MA[i] = (BufferCutlerRSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferCutlerRSI[i]) / g_ExtPeriodMA; - } - } - break; - case MODE_LWMA: - // Standard library function is safe here as it's not recursive - BufferCutlerRSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferCutlerRSI); - break; - default: // MODE_SMA - // Standard library function is safe here as it's not recursive - BufferCutlerRSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferCutlerRSI); - break; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| CutlerRSI_MA.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and efficiency +#property description "Cutler's RSI (SMA-based) with a signal line." + +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_minimum 0 +#property indicator_maximum 100 +#property indicator_level1 30.0 +#property indicator_level2 50.0 +#property indicator_level3 70.0 + +//--- Buffers and Plots --- +#property indicator_buffers 2 // CutlerRSI and its MA +#property indicator_plots 2 + +//--- Plot 1: MA line (smoothed) +#property indicator_label1 "MA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrRed +#property indicator_style1 STYLE_DOT +#property indicator_width1 1 + +//--- Plot 2: Cutler's RSI line (raw) +#property indicator_label2 "Cutler's RSI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpPeriodRSI = 14; // RSI Period +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // RSI Applied Price +input group "Signal Line Settings" +input int InpPeriodMA = 14; // MA Period +input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // MA Method + +//--- Indicator Buffers --- +double BufferCutlerRSI_MA[]; +double BufferCutlerRSI[]; + +//--- Global Variables --- +int g_ExtPeriodRSI; +int g_ExtPeriodMA; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; + g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; + + SetIndexBuffer(0, BufferCutlerRSI_MA, INDICATOR_DATA); + SetIndexBuffer(1, BufferCutlerRSI, INDICATOR_DATA); + + ArraySetAsSeries(BufferCutlerRSI_MA, false); + ArraySetAsSeries(BufferCutlerRSI, false); + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CutlerRSI(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +// No handles to release, but good practice to have the function + } + +//+------------------------------------------------------------------+ +//| Cutler's RSI calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Prepare the source price array + double price_source[]; + ArrayResize(price_source, rates_total); + for(int i=0; i 0) ? diff : 0; + double neg_change = (diff < 0) ? -diff : 0; + + sum_pos += pos_change; + sum_neg += neg_change; + + // Remove the oldest value from the sum once the window is full + if(i > g_ExtPeriodRSI) + { + double old_diff = price_source[i - g_ExtPeriodRSI] - price_source[i - g_ExtPeriodRSI - 1]; + sum_pos -= (old_diff > 0) ? old_diff : 0; + sum_neg -= (old_diff < 0) ? -old_diff : 0; + } + + if(i >= g_ExtPeriodRSI) + { + if(sum_neg > 0) + { + double rs = (sum_pos / g_ExtPeriodRSI) / (sum_neg / g_ExtPeriodRSI); + BufferCutlerRSI[i] = 100.0 - (100.0 / (1.0 + rs)); + } + else + { + BufferCutlerRSI[i] = 100.0; + } + } + } + +//--- STEP 3: Calculate the signal line (MA of Cutler's RSI) + for(int i = start_pos; i < rates_total; i++) + { + switch(InpMethodMA) + { + case MODE_EMA: + case MODE_SMMA: + if(i == start_pos) + { + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + sum += BufferCutlerRSI[i - j]; + BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; + } + else + { + if(InpMethodMA == MODE_EMA) + { + double pr = 2.0 / (g_ExtPeriodMA + 1.0); + BufferCutlerRSI_MA[i] = BufferCutlerRSI[i] * pr + BufferCutlerRSI_MA[i-1] * (1.0 - pr); + } + else + { + BufferCutlerRSI_MA[i] = (BufferCutlerRSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferCutlerRSI[i]) / g_ExtPeriodMA; + } + } + break; + case MODE_LWMA: + // Standard library function is safe here as it's not recursive + BufferCutlerRSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferCutlerRSI); + break; + default: // MODE_SMA + // Standard library function is safe here as it's not recursive + BufferCutlerRSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferCutlerRSI); + break; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/CutlerRSI_MA_HeikinAshi.mq5 b/Indicators/MyIndicators/CutlerRSI_MA_HeikinAshi.mq5 index 4a45636..0d45d9a 100644 --- a/Indicators/MyIndicators/CutlerRSI_MA_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/CutlerRSI_MA_HeikinAshi.mq5 @@ -1,203 +1,203 @@ -//+------------------------------------------------------------------+ -//| CutlerRSI_MA_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.01" // Fixed EMA/SMMA overflow and optimized calculation -#property description "Cutler's RSI (SMA-based) on Heikin Ashi data, with a signal line." - -#include -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_minimum 0 -#property indicator_maximum 100 -#property indicator_level1 30.0 -#property indicator_level2 50.0 -#property indicator_level3 70.0 - -//--- Buffers and Plots --- -#property indicator_buffers 2 // CutlerRSI and its MA -#property indicator_plots 2 - -//--- Plot 1: MA line (smoothed) -#property indicator_label1 "MA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrRed -#property indicator_style1 STYLE_DOT -#property indicator_width1 1 - -//--- Plot 2: Cutler's RSI line (raw) -#property indicator_label2 "HA_CutlerRSI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpPeriodRSI = 14; // RSI Period -input group "Signal Line Settings" -input int InpPeriodMA = 14; // MA Period -input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // MA Method - -//--- Indicator Buffers --- -double BufferCutlerRSI_MA[]; -double BufferCutlerRSI[]; - -//--- Global Objects and Variables --- -int g_ExtPeriodRSI; -int g_ExtPeriodMA; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; - g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; - - SetIndexBuffer(0, BufferCutlerRSI_MA, INDICATOR_DATA); - SetIndexBuffer(1, BufferCutlerRSI, INDICATOR_DATA); - - ArraySetAsSeries(BufferCutlerRSI_MA, false); - ArraySetAsSeries(BufferCutlerRSI, false); - - IndicatorSetInteger(INDICATOR_DIGITS, 2); -// Correct the draw begin for the signal line - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CutlerRSI(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Cutler's RSI on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtPeriodRSI) - return(0); - -//--- Intermediate Heikin Ashi Buffers - double ha_open[], ha_high[], ha_low[], ha_close[]; - ArrayResize(ha_open, rates_total); - ArrayResize(ha_high, rates_total); - ArrayResize(ha_low, rates_total); - ArrayResize(ha_close, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); - -//--- STEP 2: Calculate Cutler's RSI (SMA-based) - double sum_pos = 0, sum_neg = 0; - for(int i = 1; i < rates_total; i++) - { - double diff = ha_close[i] - ha_close[i-1]; - double pos_change = (diff > 0) ? diff : 0; - double neg_change = (diff < 0) ? -diff : 0; - - sum_pos += pos_change; - sum_neg += neg_change; - - // Remove the oldest value from the sum once the window is full - if(i > g_ExtPeriodRSI) - { - double old_diff = ha_close[i - g_ExtPeriodRSI] - ha_close[i - g_ExtPeriodRSI - 1]; - sum_pos -= (old_diff > 0) ? old_diff : 0; - sum_neg -= (old_diff < 0) ? -old_diff : 0; - } - - if(i >= g_ExtPeriodRSI) - { - if(sum_neg > 0) - { - double rs = (sum_pos / g_ExtPeriodRSI) / (sum_neg / g_ExtPeriodRSI); - BufferCutlerRSI[i] = 100.0 - (100.0 / (1.0 + rs)); - } - else - { - BufferCutlerRSI[i] = 100.0; - } - } - } - -//--- STEP 3: Calculate the signal line (MA of Cutler's RSI) -// --- FIX: Correct starting position for the MA calculation --- - int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; - for(int i = ma_start_pos; i < rates_total; i++) - { - switch(InpMethodMA) - { - case MODE_EMA: - if(i == ma_start_pos) - { - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - sum += BufferCutlerRSI[i - j]; - BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; - } - else - { - double pr = 2.0 / (g_ExtPeriodMA + 1.0); - BufferCutlerRSI_MA[i] = BufferCutlerRSI[i] * pr + BufferCutlerRSI_MA[i-1] * (1.0 - pr); - } - break; - case MODE_SMMA: - if(i == ma_start_pos) - { - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - sum += BufferCutlerRSI[i - j]; - BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; - } - else - BufferCutlerRSI_MA[i] = (BufferCutlerRSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferCutlerRSI[i]) / g_ExtPeriodMA; - break; - case MODE_LWMA: - BufferCutlerRSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferCutlerRSI); - break; - default: // MODE_SMA - BufferCutlerRSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferCutlerRSI); - break; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| CutlerRSI_MA_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.01" // Fixed EMA/SMMA overflow and optimized calculation +#property description "Cutler's RSI (SMA-based) on Heikin Ashi data, with a signal line." + +#include +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_minimum 0 +#property indicator_maximum 100 +#property indicator_level1 30.0 +#property indicator_level2 50.0 +#property indicator_level3 70.0 + +//--- Buffers and Plots --- +#property indicator_buffers 2 // CutlerRSI and its MA +#property indicator_plots 2 + +//--- Plot 1: MA line (smoothed) +#property indicator_label1 "MA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrRed +#property indicator_style1 STYLE_DOT +#property indicator_width1 1 + +//--- Plot 2: Cutler's RSI line (raw) +#property indicator_label2 "HA_CutlerRSI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpPeriodRSI = 14; // RSI Period +input group "Signal Line Settings" +input int InpPeriodMA = 14; // MA Period +input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // MA Method + +//--- Indicator Buffers --- +double BufferCutlerRSI_MA[]; +double BufferCutlerRSI[]; + +//--- Global Objects and Variables --- +int g_ExtPeriodRSI; +int g_ExtPeriodMA; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; + g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; + + SetIndexBuffer(0, BufferCutlerRSI_MA, INDICATOR_DATA); + SetIndexBuffer(1, BufferCutlerRSI, INDICATOR_DATA); + + ArraySetAsSeries(BufferCutlerRSI_MA, false); + ArraySetAsSeries(BufferCutlerRSI, false); + + IndicatorSetInteger(INDICATOR_DIGITS, 2); +// Correct the draw begin for the signal line + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CutlerRSI(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Cutler's RSI on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtPeriodRSI) + return(0); + +//--- Intermediate Heikin Ashi Buffers + double ha_open[], ha_high[], ha_low[], ha_close[]; + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + +//--- STEP 2: Calculate Cutler's RSI (SMA-based) + double sum_pos = 0, sum_neg = 0; + for(int i = 1; i < rates_total; i++) + { + double diff = ha_close[i] - ha_close[i-1]; + double pos_change = (diff > 0) ? diff : 0; + double neg_change = (diff < 0) ? -diff : 0; + + sum_pos += pos_change; + sum_neg += neg_change; + + // Remove the oldest value from the sum once the window is full + if(i > g_ExtPeriodRSI) + { + double old_diff = ha_close[i - g_ExtPeriodRSI] - ha_close[i - g_ExtPeriodRSI - 1]; + sum_pos -= (old_diff > 0) ? old_diff : 0; + sum_neg -= (old_diff < 0) ? -old_diff : 0; + } + + if(i >= g_ExtPeriodRSI) + { + if(sum_neg > 0) + { + double rs = (sum_pos / g_ExtPeriodRSI) / (sum_neg / g_ExtPeriodRSI); + BufferCutlerRSI[i] = 100.0 - (100.0 / (1.0 + rs)); + } + else + { + BufferCutlerRSI[i] = 100.0; + } + } + } + +//--- STEP 3: Calculate the signal line (MA of Cutler's RSI) +// --- FIX: Correct starting position for the MA calculation --- + int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; + for(int i = ma_start_pos; i < rates_total; i++) + { + switch(InpMethodMA) + { + case MODE_EMA: + if(i == ma_start_pos) + { + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + sum += BufferCutlerRSI[i - j]; + BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; + } + else + { + double pr = 2.0 / (g_ExtPeriodMA + 1.0); + BufferCutlerRSI_MA[i] = BufferCutlerRSI[i] * pr + BufferCutlerRSI_MA[i-1] * (1.0 - pr); + } + break; + case MODE_SMMA: + if(i == ma_start_pos) + { + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + sum += BufferCutlerRSI[i - j]; + BufferCutlerRSI_MA[i] = sum / g_ExtPeriodMA; + } + else + BufferCutlerRSI_MA[i] = (BufferCutlerRSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferCutlerRSI[i]) / g_ExtPeriodMA; + break; + case MODE_LWMA: + BufferCutlerRSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferCutlerRSI); + break; + default: // MODE_SMA + BufferCutlerRSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferCutlerRSI); + break; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/FisherTransform.md b/Indicators/MyIndicators/FisherTransform.md index ed00ea4..1b9de09 100644 --- a/Indicators/MyIndicators/FisherTransform.md +++ b/Indicators/MyIndicators/FisherTransform.md @@ -1,66 +1,66 @@ -# Fisher Transform - -## 1. Summary (Introduction) - -The Fisher Transform is a technical indicator created by J.H. Ehlers that converts price into a Gaussian normal distribution. The primary purpose of this transformation is to create sharp, clear turning points that are less prone to the lag and ambiguity of many other oscillators. - -The indicator consists of two lines: the Fisher line and a signal line (which is typically the Fisher line's value from the previous bar). It is an unbound oscillator, meaning its values can theoretically extend to infinity, but in practice, it tends to fluctuate around a zero line. Extreme readings suggest that a price reversal is more likely. - -## 2. Mathematical Foundations and Calculation Logic - -The Fisher Transform uses a mathematical formula to normalize price data, making extreme price moves more apparent. - -### Required Components - -- **Period (N):** The lookback period for finding the highest and lowest prices. -- **Source Price:** The indicator typically uses the median price `(High + Low) / 2` as its input. - -### Calculation Steps (Algorithm) - -1. **Transform Price to a Level between -1 and +1:** First, the source price is converted into a value that fluctuates primarily between -1 and +1. This is done by determining the price's position within its highest and lowest range over the last `N` periods. - - - $\text{Price Position}_i = \frac{\text{Source Price}_i - \text{Lowest Price}_{N}}{\text{Highest Price}_{N} - \text{Lowest Price}_{N}} - 0.5$ - - This value is then smoothed, often with a weighted or exponential moving average. The classic formula uses a specific recursive smoothing: - $\text{Value}_i = (0.33 \times 2 \times \text{Price Position}_i) + (0.67 \times \text{Value}_{i-1})$ - - The resulting `Value` is clamped to a range just inside -1 and +1 (e.g., -0.999 to 0.999) to avoid mathematical errors in the next step. - -2. **Apply the Fisher Transform:** The core of the indicator is the application of the Fisher Transform formula to the smoothed `Value` from the previous step. - $\text{Fisher}_i = 0.5 \times \ln\left(\frac{1 + \text{Value}_i}{1 - \text{Value}_i}\right)$ - Where `ln` is the natural logarithm. - -3. **Final Smoothing and Signal Line:** The resulting Fisher value is often smoothed again with its own previous value to create the final, plotted line. The signal line is simply the Fisher line from the previous bar. - $\text{Final Fisher}_i = \text{Fisher}_i + (0.5 \times \text{Final Fisher}_{i-1})$ - $\text{Signal}_i = \text{Final Fisher}_{i-1}$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored to be highly robust, especially concerning the multiple recursive calculations involved. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for indicators with recursive logic to ensure maximum stability and prevent calculation errors. - -- **Robust Initialization:** This is the most critical part of the implementation. The final, recursive calculation of the `BufferFisher` line is highly susceptible to floating-point overflows if not initialized correctly. Our code explicitly handles this: - - - The **first valid value** of the `BufferFisher` line is calculated **without** the recursive component (`+ 0.5 * BufferFisher[i-1]`). - - All subsequent values are then calculated using the full recursive formula, ensuring the calculation chain starts with a stable, valid number. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: - - 1. **Step 1:** A `for` loop prepares the source price data (`hl2`) for the main calculation. - 2. **Step 2:** A single, efficient `for` loop handles the entire Fisher Transform calculation, including the smoothing of the intermediate `Value` buffer and the final, robustly initialized `BufferFisher` calculation. - -- **Heikin Ashi Variant (`FisherTransform_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high` and `ha_low` values to calculate the source price. - - This results in a significantly smoother oscillator, as the input data itself is already filtered, which can help in identifying more significant, underlying momentum shifts. - -## 4. Parameters - -- **Length (`InpLength`):** The lookback period for finding the highest and lowest prices. A shorter period results in a more sensitive, faster-reacting oscillator, while a longer period creates a smoother, slower line. Default is `9`. - -## 5. Usage and Interpretation - -- **Identifying Extremes:** The primary use of the Fisher Transform is to identify extreme price levels that may signal an impending reversal. High positive values (e.g., above +1.5) are considered overbought, and high negative values (e.g., below -1.5) are considered oversold. -- **Crossovers:** - - **Fisher / Signal Line Crossover:** When the Fisher line (blue) crosses above its signal line (orange), it can be considered a buy signal. When it crosses below, it's a sell signal. These are the most common signals generated by the indicator. - - **Zero Line Crossover:** A crossover of the Fisher line above the zero line can also be interpreted as a bullish signal, and a cross below as bearish, though these are less common. -- **Divergence:** Look for divergences between the Fisher Transform and the price action. A bearish divergence (higher price highs, lower Fisher highs) can signal a potential top, while a bullish divergence (lower price lows, higher Fisher lows) can signal a potential bottom. -- **Caution:** The Fisher Transform is a very fast-reacting oscillator and can produce many signals. It is often recommended to wait for the Fisher line to form a clear peak or trough beyond the extreme levels before acting on a signal, rather than trading every crossover. +# Fisher Transform + +## 1. Summary (Introduction) + +The Fisher Transform is a technical indicator created by J.H. Ehlers that converts price into a Gaussian normal distribution. The primary purpose of this transformation is to create sharp, clear turning points that are less prone to the lag and ambiguity of many other oscillators. + +The indicator consists of two lines: the Fisher line and a signal line (which is typically the Fisher line's value from the previous bar). It is an unbound oscillator, meaning its values can theoretically extend to infinity, but in practice, it tends to fluctuate around a zero line. Extreme readings suggest that a price reversal is more likely. + +## 2. Mathematical Foundations and Calculation Logic + +The Fisher Transform uses a mathematical formula to normalize price data, making extreme price moves more apparent. + +### Required Components + +- **Period (N):** The lookback period for finding the highest and lowest prices. +- **Source Price:** The indicator typically uses the median price `(High + Low) / 2` as its input. + +### Calculation Steps (Algorithm) + +1. **Transform Price to a Level between -1 and +1:** First, the source price is converted into a value that fluctuates primarily between -1 and +1. This is done by determining the price's position within its highest and lowest range over the last `N` periods. + + - $\text{Price Position}_i = \frac{\text{Source Price}_i - \text{Lowest Price}_{N}}{\text{Highest Price}_{N} - \text{Lowest Price}_{N}} - 0.5$ + - This value is then smoothed, often with a weighted or exponential moving average. The classic formula uses a specific recursive smoothing: + $\text{Value}_i = (0.33 \times 2 \times \text{Price Position}_i) + (0.67 \times \text{Value}_{i-1})$ + - The resulting `Value` is clamped to a range just inside -1 and +1 (e.g., -0.999 to 0.999) to avoid mathematical errors in the next step. + +2. **Apply the Fisher Transform:** The core of the indicator is the application of the Fisher Transform formula to the smoothed `Value` from the previous step. + $\text{Fisher}_i = 0.5 \times \ln\left(\frac{1 + \text{Value}_i}{1 - \text{Value}_i}\right)$ + Where `ln` is the natural logarithm. + +3. **Final Smoothing and Signal Line:** The resulting Fisher value is often smoothed again with its own previous value to create the final, plotted line. The signal line is simply the Fisher line from the previous bar. + $\text{Final Fisher}_i = \text{Fisher}_i + (0.5 \times \text{Final Fisher}_{i-1})$ + $\text{Signal}_i = \text{Final Fisher}_{i-1}$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored to be highly robust, especially concerning the multiple recursive calculations involved. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for indicators with recursive logic to ensure maximum stability and prevent calculation errors. + +- **Robust Initialization:** This is the most critical part of the implementation. The final, recursive calculation of the `BufferFisher` line is highly susceptible to floating-point overflows if not initialized correctly. Our code explicitly handles this: + + - The **first valid value** of the `BufferFisher` line is calculated **without** the recursive component (`+ 0.5 * BufferFisher[i-1]`). + - All subsequent values are then calculated using the full recursive formula, ensuring the calculation chain starts with a stable, valid number. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: + + 1. **Step 1:** A `for` loop prepares the source price data (`hl2`) for the main calculation. + 2. **Step 2:** A single, efficient `for` loop handles the entire Fisher Transform calculation, including the smoothing of the intermediate `Value` buffer and the final, robustly initialized `BufferFisher` calculation. + +- **Heikin Ashi Variant (`FisherTransform_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high` and `ha_low` values to calculate the source price. + - This results in a significantly smoother oscillator, as the input data itself is already filtered, which can help in identifying more significant, underlying momentum shifts. + +## 4. Parameters + +- **Length (`InpLength`):** The lookback period for finding the highest and lowest prices. A shorter period results in a more sensitive, faster-reacting oscillator, while a longer period creates a smoother, slower line. Default is `9`. + +## 5. Usage and Interpretation + +- **Identifying Extremes:** The primary use of the Fisher Transform is to identify extreme price levels that may signal an impending reversal. High positive values (e.g., above +1.5) are considered overbought, and high negative values (e.g., below -1.5) are considered oversold. +- **Crossovers:** + - **Fisher / Signal Line Crossover:** When the Fisher line (blue) crosses above its signal line (orange), it can be considered a buy signal. When it crosses below, it's a sell signal. These are the most common signals generated by the indicator. + - **Zero Line Crossover:** A crossover of the Fisher line above the zero line can also be interpreted as a bullish signal, and a cross below as bearish, though these are less common. +- **Divergence:** Look for divergences between the Fisher Transform and the price action. A bearish divergence (higher price highs, lower Fisher highs) can signal a potential top, while a bullish divergence (lower price lows, higher Fisher lows) can signal a potential bottom. +- **Caution:** The Fisher Transform is a very fast-reacting oscillator and can produce many signals. It is often recommended to wait for the Fisher line to form a clear peak or trough beyond the extreme levels before acting on a signal, rather than trading every crossover. diff --git a/Indicators/MyIndicators/FisherTransform.mq5 b/Indicators/MyIndicators/FisherTransform.mq5 index 73f6bd9..6260cd6 100644 --- a/Indicators/MyIndicators/FisherTransform.mq5 +++ b/Indicators/MyIndicators/FisherTransform.mq5 @@ -1,181 +1,181 @@ -//+------------------------------------------------------------------+ -//| FisherTransform.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and robust initialization -#property description "Fisher Transform Oscillator" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 1.5 -#property indicator_level2 0.75 -#property indicator_level3 0.0 -#property indicator_level4 -0.75 -#property indicator_level5 -1.5 -#property indicator_levelstyle STYLE_DOT - -//--- Buffers and Plots --- -#property indicator_buffers 3 // Fisher, Trigger, and 1 calculation buffer -#property indicator_plots 2 - -//--- Plot 1: Fisher line -#property indicator_label1 "Fisher" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: Trigger line -#property indicator_label2 "Trigger" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLength = 9; // Length - -//--- Indicator Buffers --- -double BufferFisher[]; -double BufferTrigger[]; -double BufferValue[]; // Calculation buffer for the intermediate 'value' - -//--- Global Variables --- -int g_ExtLength; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input - g_ExtLength = (InpLength < 1) ? 1 : InpLength; - -//--- Map the buffers - SetIndexBuffer(0, BufferFisher, INDICATOR_DATA); - SetIndexBuffer(1, BufferTrigger, INDICATOR_DATA); - SetIndexBuffer(2, BufferValue, INDICATOR_CALCULATIONS); - -//--- Set all buffers to non-timeseries for stable calculation - ArraySetAsSeries(BufferFisher, false); - ArraySetAsSeries(BufferTrigger, false); - ArraySetAsSeries(BufferValue, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, 4); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLength); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLength + 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fisher(%d)", g_ExtLength)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Fisher Transform calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtLength) - return(0); - -//--- STEP 1: Create a buffer for HL2 price - double hl2[]; - ArrayResize(hl2, rates_total); - for(int i=0; i 0.999) - BufferValue[i] = 0.999; - if(BufferValue[i] < -0.999) - BufferValue[i] = -0.999; - - // --- FIX: Robust initialization for the recursive Fisher calculation --- - double log_val = 0.5 * MathLog((1 + BufferValue[i]) / (1 - BufferValue[i])); - - if(i == g_ExtLength) // First calculation (initialization) - { - BufferFisher[i] = log_val; - } - else // Subsequent calculations use the full recursive formula - { - BufferFisher[i] = log_val + 0.5 * BufferFisher[i-1]; - } - - // The trigger is the previous Fisher value - BufferTrigger[i] = BufferFisher[i-1]; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| FisherTransform.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and robust initialization +#property description "Fisher Transform Oscillator" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 1.5 +#property indicator_level2 0.75 +#property indicator_level3 0.0 +#property indicator_level4 -0.75 +#property indicator_level5 -1.5 +#property indicator_levelstyle STYLE_DOT + +//--- Buffers and Plots --- +#property indicator_buffers 3 // Fisher, Trigger, and 1 calculation buffer +#property indicator_plots 2 + +//--- Plot 1: Fisher line +#property indicator_label1 "Fisher" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: Trigger line +#property indicator_label2 "Trigger" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLength = 9; // Length + +//--- Indicator Buffers --- +double BufferFisher[]; +double BufferTrigger[]; +double BufferValue[]; // Calculation buffer for the intermediate 'value' + +//--- Global Variables --- +int g_ExtLength; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input + g_ExtLength = (InpLength < 1) ? 1 : InpLength; + +//--- Map the buffers + SetIndexBuffer(0, BufferFisher, INDICATOR_DATA); + SetIndexBuffer(1, BufferTrigger, INDICATOR_DATA); + SetIndexBuffer(2, BufferValue, INDICATOR_CALCULATIONS); + +//--- Set all buffers to non-timeseries for stable calculation + ArraySetAsSeries(BufferFisher, false); + ArraySetAsSeries(BufferTrigger, false); + ArraySetAsSeries(BufferValue, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, 4); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLength); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLength + 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fisher(%d)", g_ExtLength)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Fisher Transform calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtLength) + return(0); + +//--- STEP 1: Create a buffer for HL2 price + double hl2[]; + ArrayResize(hl2, rates_total); + for(int i=0; i 0.999) + BufferValue[i] = 0.999; + if(BufferValue[i] < -0.999) + BufferValue[i] = -0.999; + + // --- FIX: Robust initialization for the recursive Fisher calculation --- + double log_val = 0.5 * MathLog((1 + BufferValue[i]) / (1 - BufferValue[i])); + + if(i == g_ExtLength) // First calculation (initialization) + { + BufferFisher[i] = log_val; + } + else // Subsequent calculations use the full recursive formula + { + BufferFisher[i] = log_val + 0.5 * BufferFisher[i-1]; + } + + // The trigger is the previous Fisher value + BufferTrigger[i] = BufferFisher[i-1]; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/FisherTransform_HeikinAshi.mq5 b/Indicators/MyIndicators/FisherTransform_HeikinAshi.mq5 index 73dd12b..07ba811 100644 --- a/Indicators/MyIndicators/FisherTransform_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/FisherTransform_HeikinAshi.mq5 @@ -1,226 +1,226 @@ -//+------------------------------------------------------------------+ -//| FisherTransform_HeikinAshi.mq5| -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Fisher Transform Oscillator on Heikin Ashi data" - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 1.5 -#property indicator_level2 0.75 -#property indicator_level3 0.0 -#property indicator_level4 -0.75 -#property indicator_level5 -1.5 -#property indicator_levelstyle STYLE_DOT - -//--- Buffers and Plots --- -#property indicator_buffers 3 // Fisher, Trigger, and 1 calculation buffer -#property indicator_plots 2 - -//--- Plot 1: Fisher line -#property indicator_label1 "HA_Fisher" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: Trigger line -#property indicator_label2 "HA_Trigger" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLength = 9; // Length - -//--- Indicator Buffers --- -double BufferHA_Fisher[]; -double BufferHA_Trigger[]; -double BufferValue[]; // Calculation buffer for the intermediate 'value' - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtLength; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input - g_ExtLength = (InpLength < 1) ? 1 : InpLength; - -//--- Map the buffers - SetIndexBuffer(0, BufferHA_Fisher, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_Trigger, INDICATOR_DATA); - SetIndexBuffer(2, BufferValue, INDICATOR_CALCULATIONS); - -//--- Set all buffers to non-timeseries for stable calculation - ArraySetAsSeries(BufferHA_Fisher, false); - ArraySetAsSeries(BufferHA_Trigger, false); - ArraySetAsSeries(BufferValue, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, 4); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLength); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLength + 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Fisher(%d)", g_ExtLength)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Fisher Transform on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check for enough data - if(rates_total <= g_ExtLength) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Create a buffer for Heikin Ashi HL2 price - double ha_hl2[]; - ArrayResize(ha_hl2, rates_total); - for(int i=0; i 0.999) - BufferValue[i] = 0.999; - if(BufferValue[i] < -0.999) - BufferValue[i] = -0.999; - - // --- FIX: Robust initialization for the recursive calculation --- - double log_val = 0.5 * MathLog((1 + BufferValue[i]) / (1 - BufferValue[i])); - - if(i == g_ExtLength) // First calculation (initialization) - { - // For the very first value, we don't use the recursive part - BufferHA_Fisher[i] = log_val; - } - else // Subsequent calculations use the full recursive formula - { - BufferHA_Fisher[i] = log_val + 0.5 * BufferHA_Fisher[i-1]; - } - - // The trigger is the previous Fisher value - BufferHA_Trigger[i] = BufferHA_Fisher[i-1]; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| FisherTransform_HeikinAshi.mq5| +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Fisher Transform Oscillator on Heikin Ashi data" + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 1.5 +#property indicator_level2 0.75 +#property indicator_level3 0.0 +#property indicator_level4 -0.75 +#property indicator_level5 -1.5 +#property indicator_levelstyle STYLE_DOT + +//--- Buffers and Plots --- +#property indicator_buffers 3 // Fisher, Trigger, and 1 calculation buffer +#property indicator_plots 2 + +//--- Plot 1: Fisher line +#property indicator_label1 "HA_Fisher" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: Trigger line +#property indicator_label2 "HA_Trigger" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLength = 9; // Length + +//--- Indicator Buffers --- +double BufferHA_Fisher[]; +double BufferHA_Trigger[]; +double BufferValue[]; // Calculation buffer for the intermediate 'value' + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtLength; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input + g_ExtLength = (InpLength < 1) ? 1 : InpLength; + +//--- Map the buffers + SetIndexBuffer(0, BufferHA_Fisher, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_Trigger, INDICATOR_DATA); + SetIndexBuffer(2, BufferValue, INDICATOR_CALCULATIONS); + +//--- Set all buffers to non-timeseries for stable calculation + ArraySetAsSeries(BufferHA_Fisher, false); + ArraySetAsSeries(BufferHA_Trigger, false); + ArraySetAsSeries(BufferValue, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, 4); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLength); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLength + 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Fisher(%d)", g_ExtLength)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Fisher Transform on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check for enough data + if(rates_total <= g_ExtLength) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Create a buffer for Heikin Ashi HL2 price + double ha_hl2[]; + ArrayResize(ha_hl2, rates_total); + for(int i=0; i 0.999) + BufferValue[i] = 0.999; + if(BufferValue[i] < -0.999) + BufferValue[i] = -0.999; + + // --- FIX: Robust initialization for the recursive calculation --- + double log_val = 0.5 * MathLog((1 + BufferValue[i]) / (1 - BufferValue[i])); + + if(i == g_ExtLength) // First calculation (initialization) + { + // For the very first value, we don't use the recursive part + BufferHA_Fisher[i] = log_val; + } + else // Subsequent calculations use the full recursive formula + { + BufferHA_Fisher[i] = log_val + 0.5 * BufferHA_Fisher[i-1]; + } + + // The trigger is the previous Fisher value + BufferHA_Trigger[i] = BufferHA_Fisher[i-1]; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Gann_HiLo.md b/Indicators/MyIndicators/Gann_HiLo.md index 681bc6b..d3b7eeb 100644 --- a/Indicators/MyIndicators/Gann_HiLo.md +++ b/Indicators/MyIndicators/Gann_HiLo.md @@ -1,66 +1,66 @@ -# Gann HiLo Activator - -## 1. Summary (Introduction) - -The Gann HiLo Activator is a simple yet effective trend-following indicator developed by Robert Krausz. Despite its name, it is not directly based on the complex methods of W.D. Gann, but rather follows the core principle of using moving averages of previous highs and lows to identify the trend direction. - -The indicator is plotted on the price chart as a single line that changes color and position relative to the price, providing clear, visual signals for trend direction, potential entry points, and trailing stop-loss levels. - -## 2. Mathematical Foundations and Calculation Logic - -The Gann HiLo Activator is based on two separate moving averages: one calculated on the previous `N` bars' high prices, and the other on the previous `N` bars' low prices. The indicator then uses the closing price to determine which of these two moving averages to follow. - -### Required Components - -- **Period (N):** The lookback period for the high and low moving averages. -- **MA Method:** The type of moving average to use (Simple, Exponential, etc.). -- **Source Prices:** The `High[]` and `Low[]` price series. - -### Calculation Steps (Algorithm) - -1. **Calculate the Moving Average of Highs:** Compute the moving average of the high prices over the last `N` bars. - $\text{HiAvg}_i = \text{MA}(\text{High}, N)_i$ - -2. **Calculate the Moving Average of Lows:** Compute the moving average of the low prices over the last `N` bars. - $\text{LoAvg}_i = \text{MA}(\text{Low}, N)_i$ - -3. **Determine the Trend Direction:** The trend is determined by comparing the current closing price to the moving averages of the _previous_ bar. - - - If the current `Close` is **above** the previous bar's `HiAvg`, the trend is **up**. - - If the current `Close` is **below** the previous bar's `LoAvg`, the trend is **down**. - - If the `Close` is between the two previous averages, the trend **continues** from the previous bar. - -4. **Plot the Gann HiLo Activator Line:** - - If the trend is **up**, the indicator line is plotted at the level of the **LoAvg**. - - If the trend is **down**, the indicator line is plotted at the level of the **HiAvg**. - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored to be a completely self-contained, robust, and accurate indicator, consistent with our established coding principles. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like the Gann HiLo, this is the most reliable method to prevent calculation errors and ensure stability. - -- **Fully Manual MA Calculations:** To guarantee 100% accuracy and consistency within our `non-timeseries` calculation model, we have implemented all moving average types (**SMA, EMA, SMMA, LWMA**) **manually**. The indicator is completely independent of the `` standard library. This approach provides full control and ensures predictable behavior. - - - **Recursive MAs (EMA/SMMA)** are carefully initialized with a manual Simple Moving Average to prevent floating-point overflows. - - **SMA** is calculated using an efficient sliding-window sum technique. - -- **Integrated Calculation Loop:** The `OnCalculate` function uses a single, efficient `for` loop to perform all calculations. Within each iteration, it first computes the `HiAvg` and `LoAvg`, then immediately determines the trend direction and sets the final `GannHiLo` value. This integrated approach is clear and performant. - -- **Visual Representation:** The implementation ensures that trend changes are represented by a clean, vertical line connecting the previous trend's endpoint to the new trend's starting point, providing continuous visual information. - -- **Heikin Ashi Variant (`Gann_HiLo_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high` and `ha_low` values for the moving average calculations and the `ha_close` for determining the trend. - - This results in a significantly smoother indicator, ideal for traders who want to focus on the primary trend and filter out market noise. - -## 4. Parameters - -- **Period (`InpPeriod`):** The lookback period for the high and low moving averages. A shorter period will result in a more responsive line that follows the price closely, while a longer period will create a smoother line that is less sensitive to minor fluctuations. Default is `10`. -- **MA Method (`InpMAMethod`):** The type of moving average to use for the high and low calculations (SMA, EMA, SMMA, LWMA). Default is `MODE_SMA`. - -## 5. Usage and Interpretation - -- **Trend Identification:** The primary use of the Gann HiLo is to identify the current market trend. A blue line below the price indicates an uptrend. A red line above the price indicates a downtrend. -- **Trailing Stop-Loss:** The indicator is exceptionally well-suited for use as a trailing stop-loss. In an uptrend, a trader might place their stop-loss just below the blue line. In a downtrend, the stop-loss could be placed just above the red line. -- **Trade Signals:** A change in the indicator's color can be interpreted as a trade signal. A flip from red to blue suggests a potential buy signal, while a flip from blue to red suggests a potential sell signal. -- **Caution:** Like all trend-following indicators, the Gann HiLo is most effective in trending markets. In sideways or ranging markets, it can produce frequent false signals ("whipsaws") as the price oscillates around the two moving averages. +# Gann HiLo Activator + +## 1. Summary (Introduction) + +The Gann HiLo Activator is a simple yet effective trend-following indicator developed by Robert Krausz. Despite its name, it is not directly based on the complex methods of W.D. Gann, but rather follows the core principle of using moving averages of previous highs and lows to identify the trend direction. + +The indicator is plotted on the price chart as a single line that changes color and position relative to the price, providing clear, visual signals for trend direction, potential entry points, and trailing stop-loss levels. + +## 2. Mathematical Foundations and Calculation Logic + +The Gann HiLo Activator is based on two separate moving averages: one calculated on the previous `N` bars' high prices, and the other on the previous `N` bars' low prices. The indicator then uses the closing price to determine which of these two moving averages to follow. + +### Required Components + +- **Period (N):** The lookback period for the high and low moving averages. +- **MA Method:** The type of moving average to use (Simple, Exponential, etc.). +- **Source Prices:** The `High[]` and `Low[]` price series. + +### Calculation Steps (Algorithm) + +1. **Calculate the Moving Average of Highs:** Compute the moving average of the high prices over the last `N` bars. + $\text{HiAvg}_i = \text{MA}(\text{High}, N)_i$ + +2. **Calculate the Moving Average of Lows:** Compute the moving average of the low prices over the last `N` bars. + $\text{LoAvg}_i = \text{MA}(\text{Low}, N)_i$ + +3. **Determine the Trend Direction:** The trend is determined by comparing the current closing price to the moving averages of the _previous_ bar. + + - If the current `Close` is **above** the previous bar's `HiAvg`, the trend is **up**. + - If the current `Close` is **below** the previous bar's `LoAvg`, the trend is **down**. + - If the `Close` is between the two previous averages, the trend **continues** from the previous bar. + +4. **Plot the Gann HiLo Activator Line:** + - If the trend is **up**, the indicator line is plotted at the level of the **LoAvg**. + - If the trend is **down**, the indicator line is plotted at the level of the **HiAvg**. + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored to be a completely self-contained, robust, and accurate indicator, consistent with our established coding principles. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like the Gann HiLo, this is the most reliable method to prevent calculation errors and ensure stability. + +- **Fully Manual MA Calculations:** To guarantee 100% accuracy and consistency within our `non-timeseries` calculation model, we have implemented all moving average types (**SMA, EMA, SMMA, LWMA**) **manually**. The indicator is completely independent of the `` standard library. This approach provides full control and ensures predictable behavior. + + - **Recursive MAs (EMA/SMMA)** are carefully initialized with a manual Simple Moving Average to prevent floating-point overflows. + - **SMA** is calculated using an efficient sliding-window sum technique. + +- **Integrated Calculation Loop:** The `OnCalculate` function uses a single, efficient `for` loop to perform all calculations. Within each iteration, it first computes the `HiAvg` and `LoAvg`, then immediately determines the trend direction and sets the final `GannHiLo` value. This integrated approach is clear and performant. + +- **Visual Representation:** The implementation ensures that trend changes are represented by a clean, vertical line connecting the previous trend's endpoint to the new trend's starting point, providing continuous visual information. + +- **Heikin Ashi Variant (`Gann_HiLo_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high` and `ha_low` values for the moving average calculations and the `ha_close` for determining the trend. + - This results in a significantly smoother indicator, ideal for traders who want to focus on the primary trend and filter out market noise. + +## 4. Parameters + +- **Period (`InpPeriod`):** The lookback period for the high and low moving averages. A shorter period will result in a more responsive line that follows the price closely, while a longer period will create a smoother line that is less sensitive to minor fluctuations. Default is `10`. +- **MA Method (`InpMAMethod`):** The type of moving average to use for the high and low calculations (SMA, EMA, SMMA, LWMA). Default is `MODE_SMA`. + +## 5. Usage and Interpretation + +- **Trend Identification:** The primary use of the Gann HiLo is to identify the current market trend. A blue line below the price indicates an uptrend. A red line above the price indicates a downtrend. +- **Trailing Stop-Loss:** The indicator is exceptionally well-suited for use as a trailing stop-loss. In an uptrend, a trader might place their stop-loss just below the blue line. In a downtrend, the stop-loss could be placed just above the red line. +- **Trade Signals:** A change in the indicator's color can be interpreted as a trade signal. A flip from red to blue suggests a potential buy signal, while a flip from blue to red suggests a potential sell signal. +- **Caution:** Like all trend-following indicators, the Gann HiLo is most effective in trending markets. In sideways or ranging markets, it can produce frequent false signals ("whipsaws") as the price oscillates around the two moving averages. diff --git a/Indicators/MyIndicators/Gann_HiLo.mq5 b/Indicators/MyIndicators/Gann_HiLo.mq5 index 0c29cae..98c0187 100644 --- a/Indicators/MyIndicators/Gann_HiLo.mq5 +++ b/Indicators/MyIndicators/Gann_HiLo.mq5 @@ -1,189 +1,189 @@ -//+------------------------------------------------------------------+ -//| Gann_HiLo.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability with fully manual MA calculations -#property description "Gann HiLo Activator with selectable MA for trend following" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 5 -#property indicator_plots 1 - -//--- Plot 1: Gann HiLo line -#property indicator_label1 "Gann_HiLo" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrDodgerBlue, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpPeriod = 10; // Period for High/Low averages -input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for High/Low averages - -//--- Indicator Buffers --- -double BufferGannHiLo[]; -double BufferColor[]; -double BufferHiAvg[]; -double BufferLoAvg[]; -double BufferTrend[]; - -//--- Global Variables --- -int g_ExtPeriod; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriod = (InpPeriod < 1) ? 1 : InpPeriod; - - SetIndexBuffer(0, BufferGannHiLo, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferHiAvg, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferLoAvg, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferGannHiLo, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferHiAvg, false); - ArraySetAsSeries(BufferLoAvg, false); - ArraySetAsSeries(BufferTrend, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Gann_HiLo(%d)", g_ExtPeriod)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Gann HiLo Activator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtPeriod) - return(0); - -//--- Variables for manual SMA calculation - double sma_sum_high = 0; - double sma_sum_low = 0; - -//--- Main calculation loop - for(int i = 1; i < rates_total; i++) - { - if(i < g_ExtPeriod - 1) - continue; - - // --- STEP 1: Calculate the two moving averages (High and Low) --- - switch(InpMAMethod) - { - case MODE_EMA: - case MODE_SMMA: - if(i == g_ExtPeriod - 1) // Initialization with manual SMA - { - double sum_h=0, sum_l=0; - for(int j=0; j 0) - { - BufferHiAvg[i] = lwma_sum_h / weight_sum; - BufferLoAvg[i] = lwma_sum_l / weight_sum; - } - } - break; - default: // MODE_SMA - if(i == g_ExtPeriod - 1) - { - for(int j=0; j BufferHiAvg[i-1]) - BufferTrend[i] = 1; - else - if(close[i] < BufferLoAvg[i-1]) - BufferTrend[i] = -1; - else - BufferTrend[i] = BufferTrend[i-1]; - - if(BufferTrend[i] == 1) - { - BufferGannHiLo[i] = BufferLoAvg[i]; - BufferColor[i] = 0; - if(BufferTrend[i-1] == -1) - BufferGannHiLo[i-1] = BufferLoAvg[i]; - } - else - { - BufferGannHiLo[i] = BufferHiAvg[i]; - BufferColor[i] = 1; - if(BufferTrend[i-1] == 1) - BufferGannHiLo[i-1] = BufferHiAvg[i]; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Gann_HiLo.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability with fully manual MA calculations +#property description "Gann HiLo Activator with selectable MA for trend following" + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 5 +#property indicator_plots 1 + +//--- Plot 1: Gann HiLo line +#property indicator_label1 "Gann_HiLo" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrDodgerBlue, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpPeriod = 10; // Period for High/Low averages +input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for High/Low averages + +//--- Indicator Buffers --- +double BufferGannHiLo[]; +double BufferColor[]; +double BufferHiAvg[]; +double BufferLoAvg[]; +double BufferTrend[]; + +//--- Global Variables --- +int g_ExtPeriod; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriod = (InpPeriod < 1) ? 1 : InpPeriod; + + SetIndexBuffer(0, BufferGannHiLo, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferHiAvg, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferLoAvg, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferGannHiLo, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferHiAvg, false); + ArraySetAsSeries(BufferLoAvg, false); + ArraySetAsSeries(BufferTrend, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Gann_HiLo(%d)", g_ExtPeriod)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Gann HiLo Activator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtPeriod) + return(0); + +//--- Variables for manual SMA calculation + double sma_sum_high = 0; + double sma_sum_low = 0; + +//--- Main calculation loop + for(int i = 1; i < rates_total; i++) + { + if(i < g_ExtPeriod - 1) + continue; + + // --- STEP 1: Calculate the two moving averages (High and Low) --- + switch(InpMAMethod) + { + case MODE_EMA: + case MODE_SMMA: + if(i == g_ExtPeriod - 1) // Initialization with manual SMA + { + double sum_h=0, sum_l=0; + for(int j=0; j 0) + { + BufferHiAvg[i] = lwma_sum_h / weight_sum; + BufferLoAvg[i] = lwma_sum_l / weight_sum; + } + } + break; + default: // MODE_SMA + if(i == g_ExtPeriod - 1) + { + for(int j=0; j BufferHiAvg[i-1]) + BufferTrend[i] = 1; + else + if(close[i] < BufferLoAvg[i-1]) + BufferTrend[i] = -1; + else + BufferTrend[i] = BufferTrend[i-1]; + + if(BufferTrend[i] == 1) + { + BufferGannHiLo[i] = BufferLoAvg[i]; + BufferColor[i] = 0; + if(BufferTrend[i-1] == -1) + BufferGannHiLo[i-1] = BufferLoAvg[i]; + } + else + { + BufferGannHiLo[i] = BufferHiAvg[i]; + BufferColor[i] = 1; + if(BufferTrend[i-1] == 1) + BufferGannHiLo[i-1] = BufferHiAvg[i]; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Gann_HiLo_HeikinAshi.mq5 b/Indicators/MyIndicators/Gann_HiLo_HeikinAshi.mq5 index 9ea0868..d4b60b2 100644 --- a/Indicators/MyIndicators/Gann_HiLo_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/Gann_HiLo_HeikinAshi.mq5 @@ -1,190 +1,190 @@ -//+------------------------------------------------------------------+ -//| Gann_HiLo_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Gann HiLo Activator on Heikin Ashi data with selectable MA" - -#include -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 5 -#property indicator_plots 1 - -//--- Plot 1: Gann HiLo line -#property indicator_label1 "HA_Gann_HiLo" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrDodgerBlue, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpPeriod = 10; // Period for High/Low averages -input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for High/Low averages - -//--- Indicator Buffers --- -double BufferHA_GannHiLo[]; -double BufferColor[]; -double BufferHiAvg[]; -double BufferLoAvg[]; -double BufferTrend[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtPeriod; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriod = (InpPeriod < 1) ? 1 : InpPeriod; - - SetIndexBuffer(0, BufferHA_GannHiLo, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferHiAvg, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferLoAvg, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferHA_GannHiLo, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferHiAvg, false); - ArraySetAsSeries(BufferLoAvg, false); - ArraySetAsSeries(BufferTrend, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Gann_HiLo(%d)", g_ExtPeriod)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Gann HiLo on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtPeriod) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2 & 3: Calculate MAs, determine trend, and set final value in a single loop - for(int i = 1; i < rates_total; i++) - { - // Skip bars that don't have enough history for the period - if(i < g_ExtPeriod) - continue; - - // --- Calculate the two moving averages on HA High and HA Low --- - switch(InpMAMethod) - { - case MODE_EMA: - if(i == g_ExtPeriod) // Initialization - { - BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); - BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); - } - else // Recursive calculation - { - double pr = 2.0 / (g_ExtPeriod + 1.0); - BufferHiAvg[i] = ExtHaHighBuffer[i] * pr + BufferHiAvg[i-1] * (1.0 - pr); - BufferLoAvg[i] = ExtHaLowBuffer[i] * pr + BufferLoAvg[i-1] * (1.0 - pr); - } - break; - case MODE_SMMA: - if(i == g_ExtPeriod) // Initialization - { - BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); - BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); - } - else // Recursive calculation - { - BufferHiAvg[i] = (BufferHiAvg[i-1] * (g_ExtPeriod - 1) + ExtHaHighBuffer[i]) / g_ExtPeriod; - BufferLoAvg[i] = (BufferLoAvg[i-1] * (g_ExtPeriod - 1) + ExtHaLowBuffer[i]) / g_ExtPeriod; - } - break; - case MODE_LWMA: - BufferHiAvg[i] = LinearWeightedMA(i, g_ExtPeriod, ExtHaHighBuffer); - BufferLoAvg[i] = LinearWeightedMA(i, g_ExtPeriod, ExtHaLowBuffer); - break; - default: // MODE_SMA - BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); - BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); - break; - } - - // --- Determine trend and set the final Gann HiLo value --- - if(ExtHaCloseBuffer[i] > BufferHiAvg[i-1]) // Trend turns up - BufferTrend[i] = 1; - else - if(ExtHaCloseBuffer[i] < BufferLoAvg[i-1]) // Trend turns down - BufferTrend[i] = -1; - else // Trend continues - BufferTrend[i] = BufferTrend[i-1]; - - if(BufferTrend[i] == 1) - { - BufferHA_GannHiLo[i] = BufferLoAvg[i]; - BufferColor[i] = 0; // Blue for up trend - } - else - { - BufferHA_GannHiLo[i] = BufferHiAvg[i]; - BufferColor[i] = 1; // Tomato for down trend - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Gann_HiLo_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Gann HiLo Activator on Heikin Ashi data with selectable MA" + +#include +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 5 +#property indicator_plots 1 + +//--- Plot 1: Gann HiLo line +#property indicator_label1 "HA_Gann_HiLo" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrDodgerBlue, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpPeriod = 10; // Period for High/Low averages +input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for High/Low averages + +//--- Indicator Buffers --- +double BufferHA_GannHiLo[]; +double BufferColor[]; +double BufferHiAvg[]; +double BufferLoAvg[]; +double BufferTrend[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtPeriod; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriod = (InpPeriod < 1) ? 1 : InpPeriod; + + SetIndexBuffer(0, BufferHA_GannHiLo, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferHiAvg, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferLoAvg, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferHA_GannHiLo, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferHiAvg, false); + ArraySetAsSeries(BufferLoAvg, false); + ArraySetAsSeries(BufferTrend, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Gann_HiLo(%d)", g_ExtPeriod)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Gann HiLo on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtPeriod) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2 & 3: Calculate MAs, determine trend, and set final value in a single loop + for(int i = 1; i < rates_total; i++) + { + // Skip bars that don't have enough history for the period + if(i < g_ExtPeriod) + continue; + + // --- Calculate the two moving averages on HA High and HA Low --- + switch(InpMAMethod) + { + case MODE_EMA: + if(i == g_ExtPeriod) // Initialization + { + BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); + BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); + } + else // Recursive calculation + { + double pr = 2.0 / (g_ExtPeriod + 1.0); + BufferHiAvg[i] = ExtHaHighBuffer[i] * pr + BufferHiAvg[i-1] * (1.0 - pr); + BufferLoAvg[i] = ExtHaLowBuffer[i] * pr + BufferLoAvg[i-1] * (1.0 - pr); + } + break; + case MODE_SMMA: + if(i == g_ExtPeriod) // Initialization + { + BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); + BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); + } + else // Recursive calculation + { + BufferHiAvg[i] = (BufferHiAvg[i-1] * (g_ExtPeriod - 1) + ExtHaHighBuffer[i]) / g_ExtPeriod; + BufferLoAvg[i] = (BufferLoAvg[i-1] * (g_ExtPeriod - 1) + ExtHaLowBuffer[i]) / g_ExtPeriod; + } + break; + case MODE_LWMA: + BufferHiAvg[i] = LinearWeightedMA(i, g_ExtPeriod, ExtHaHighBuffer); + BufferLoAvg[i] = LinearWeightedMA(i, g_ExtPeriod, ExtHaLowBuffer); + break; + default: // MODE_SMA + BufferHiAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaHighBuffer); + BufferLoAvg[i] = SimpleMA(i, g_ExtPeriod, ExtHaLowBuffer); + break; + } + + // --- Determine trend and set the final Gann HiLo value --- + if(ExtHaCloseBuffer[i] > BufferHiAvg[i-1]) // Trend turns up + BufferTrend[i] = 1; + else + if(ExtHaCloseBuffer[i] < BufferLoAvg[i-1]) // Trend turns down + BufferTrend[i] = -1; + else // Trend continues + BufferTrend[i] = BufferTrend[i-1]; + + if(BufferTrend[i] == 1) + { + BufferHA_GannHiLo[i] = BufferLoAvg[i]; + BufferColor[i] = 0; // Blue for up trend + } + else + { + BufferHA_GannHiLo[i] = BufferHiAvg[i]; + BufferColor[i] = 1; // Tomato for down trend + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/HMA.md b/Indicators/MyIndicators/HMA.md index ede10de..b70f2a0 100644 --- a/Indicators/MyIndicators/HMA.md +++ b/Indicators/MyIndicators/HMA.md @@ -1,60 +1,60 @@ -# Hull Moving Average (HMA) - -## 1. Summary (Introduction) - -The Hull Moving Average (HMA) was developed by Alan Hull in 2005. Its primary goal is to create a moving average that is both extremely responsive to current price activity and simultaneously smooths out price data effectively. Traditional moving averages often present a trade-off between smoothness and lag; a smoother average lags more, while a faster average is more prone to "whipsaws" or noise. - -The HMA aims to solve this problem by using a unique calculation involving multiple weighted moving averages (WMAs), resulting in a line that closely follows the price while maintaining a high degree of smoothness. - -## 2. Mathematical Foundations and Calculation Logic - -The HMA's formula cleverly combines three separate Weighted Moving Averages (WMAs) to nearly eliminate lag and improve smoothness. - -### Required Components - -- **HMA Period (N):** The main lookback period for the indicator. -- **Source Price (P):** The price series used for the calculation (e.g., Close). - -### Calculation Steps (Algorithm) - -1. **Calculate a WMA with period (N/2):** First, calculate a WMA with a period of half the main HMA period, rounded to the nearest integer. - $\text{WMA}_{\text{half}} = \text{WMA}(P, \text{integer}(\frac{N}{2}))$ - -2. **Calculate a WMA with period (N):** Second, calculate a WMA with the full HMA period. - $\text{WMA}_{\text{full}} = \text{WMA}(P, N)$ - -3. **Calculate the Raw HMA:** Create a new, un-smoothed "raw" HMA series by taking two times the half-period WMA and subtracting the full-period WMA. This step significantly reduces lag. - $\text{Raw HMA}_i = (2 \times \text{WMA}_{\text{half}, i}) - \text{WMA}_{\text{full}, i}$ - -4. **Calculate the Final HMA:** Smooth the `Raw HMA` series with another WMA, this time using a period equal to the square root of the main HMA period, rounded to the nearest integer. This final step reintroduces smoothness to the fast-moving raw line. - $\text{Final HMA}_i = \text{WMA}(\text{Raw HMA}, \text{integer}(\sqrt{N}))_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored to be a completely self-contained, robust, and accurate indicator. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice to ensure maximum stability and prevent calculation errors during timeframe changes or history loading. - -- **Fully Manual WMA Calculation:** To guarantee 100% accuracy and consistency within our `non-timeseries` calculation model, we have implemented the Weighted Moving Average calculation **manually**. The indicator does **not** use the `` standard library. This approach avoids any potential inconsistencies that might arise from using library functions on `non-timeseries` arrays and gives us full control over the calculation logic. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps: - - 1. **Step 1 (Price Preparation):** A single source price array (`price_source[]`) is prepared based on the user's `InpAppliedPrice` selection, including all standard and calculated price types (e.g., `PRICE_TYPICAL`). - 2. **Step 2 (Base WMAs & Raw HMA):** The first `for` loop calculates the two base WMAs (half-period and full-period) and the resulting `Raw HMA`, storing them in their respective calculation buffers. - 3. **Step 3 (Final HMA):** A second `for` loop performs the final smoothing step, calculating a WMA on the `Raw HMA` buffer to produce the final, plotted HMA line. - -- **Heikin Ashi Variant (`HMA_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. - - This results in an exceptionally smooth trend line, combining the advanced smoothing of the HMA formula with the noise-filtering properties of Heikin Ashi candles. - -## 4. Parameters - -- **HMA Period (`InpPeriodHMA`):** The main lookback period for the indicator. This single parameter controls all three internal WMA calculations. Default is `14`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). - -## 5. Usage and Interpretation - -- **Trend Identification:** The HMA is primarily used as a fast and smooth trend line. When the price is above the HMA and the HMA is rising, the trend is considered bullish. When the price is below the HMA and the HMA is falling, the trend is considered bearish. -- **Crossover Signals:** Crossovers of the price and the HMA line can be used as trade signals. Due to its responsiveness, these signals occur with less lag than with traditional moving averages. -- **Trend Direction Filter:** The slope of the HMA itself can be used as a trend filter. A simple rule could be to only consider long trades when the HMA is rising and short trades when it is falling. -- **Caution:** While the HMA is very responsive, it is still a lagging indicator. Its primary strength is in trending markets. In sideways or choppy markets, it can still produce false signals. +# Hull Moving Average (HMA) + +## 1. Summary (Introduction) + +The Hull Moving Average (HMA) was developed by Alan Hull in 2005. Its primary goal is to create a moving average that is both extremely responsive to current price activity and simultaneously smooths out price data effectively. Traditional moving averages often present a trade-off between smoothness and lag; a smoother average lags more, while a faster average is more prone to "whipsaws" or noise. + +The HMA aims to solve this problem by using a unique calculation involving multiple weighted moving averages (WMAs), resulting in a line that closely follows the price while maintaining a high degree of smoothness. + +## 2. Mathematical Foundations and Calculation Logic + +The HMA's formula cleverly combines three separate Weighted Moving Averages (WMAs) to nearly eliminate lag and improve smoothness. + +### Required Components + +- **HMA Period (N):** The main lookback period for the indicator. +- **Source Price (P):** The price series used for the calculation (e.g., Close). + +### Calculation Steps (Algorithm) + +1. **Calculate a WMA with period (N/2):** First, calculate a WMA with a period of half the main HMA period, rounded to the nearest integer. + $\text{WMA}_{\text{half}} = \text{WMA}(P, \text{integer}(\frac{N}{2}))$ + +2. **Calculate a WMA with period (N):** Second, calculate a WMA with the full HMA period. + $\text{WMA}_{\text{full}} = \text{WMA}(P, N)$ + +3. **Calculate the Raw HMA:** Create a new, un-smoothed "raw" HMA series by taking two times the half-period WMA and subtracting the full-period WMA. This step significantly reduces lag. + $\text{Raw HMA}_i = (2 \times \text{WMA}_{\text{half}, i}) - \text{WMA}_{\text{full}, i}$ + +4. **Calculate the Final HMA:** Smooth the `Raw HMA` series with another WMA, this time using a period equal to the square root of the main HMA period, rounded to the nearest integer. This final step reintroduces smoothness to the fast-moving raw line. + $\text{Final HMA}_i = \text{WMA}(\text{Raw HMA}, \text{integer}(\sqrt{N}))_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored to be a completely self-contained, robust, and accurate indicator. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice to ensure maximum stability and prevent calculation errors during timeframe changes or history loading. + +- **Fully Manual WMA Calculation:** To guarantee 100% accuracy and consistency within our `non-timeseries` calculation model, we have implemented the Weighted Moving Average calculation **manually**. The indicator does **not** use the `` standard library. This approach avoids any potential inconsistencies that might arise from using library functions on `non-timeseries` arrays and gives us full control over the calculation logic. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps: + + 1. **Step 1 (Price Preparation):** A single source price array (`price_source[]`) is prepared based on the user's `InpAppliedPrice` selection, including all standard and calculated price types (e.g., `PRICE_TYPICAL`). + 2. **Step 2 (Base WMAs & Raw HMA):** The first `for` loop calculates the two base WMAs (half-period and full-period) and the resulting `Raw HMA`, storing them in their respective calculation buffers. + 3. **Step 3 (Final HMA):** A second `for` loop performs the final smoothing step, calculating a WMA on the `Raw HMA` buffer to produce the final, plotted HMA line. + +- **Heikin Ashi Variant (`HMA_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. + - This results in an exceptionally smooth trend line, combining the advanced smoothing of the HMA formula with the noise-filtering properties of Heikin Ashi candles. + +## 4. Parameters + +- **HMA Period (`InpPeriodHMA`):** The main lookback period for the indicator. This single parameter controls all three internal WMA calculations. Default is `14`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). + +## 5. Usage and Interpretation + +- **Trend Identification:** The HMA is primarily used as a fast and smooth trend line. When the price is above the HMA and the HMA is rising, the trend is considered bullish. When the price is below the HMA and the HMA is falling, the trend is considered bearish. +- **Crossover Signals:** Crossovers of the price and the HMA line can be used as trade signals. Due to its responsiveness, these signals occur with less lag than with traditional moving averages. +- **Trend Direction Filter:** The slope of the HMA itself can be used as a trend filter. A simple rule could be to only consider long trades when the HMA is rising and short trades when it is falling. +- **Caution:** While the HMA is very responsive, it is still a lagging indicator. Its primary strength is in trending markets. In sideways or choppy markets, it can still produce false signals. diff --git a/Indicators/MyIndicators/HMA.mq5 b/Indicators/MyIndicators/HMA.mq5 index 570efe6..a6a1bcf 100644 --- a/Indicators/MyIndicators/HMA.mq5 +++ b/Indicators/MyIndicators/HMA.mq5 @@ -1,163 +1,163 @@ -//+------------------------------------------------------------------+ -//| HMA.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.00" // Fully manual, self-contained, and accurate -#property description "Hull Moving Average (HMA)" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 4 // HMA, and 3 calculation buffers -#property indicator_plots 1 - -//--- Plot 1: HMA line -#property indicator_label1 "HMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDeepPink -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpPeriodHMA = 14; -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; - -//--- Indicator Buffers --- -double BufferHMA[]; -double BufferWMA_Half[]; -double BufferWMA_Full[]; -double BufferRawHMA[]; - -//--- Global Variables --- -int g_ExtPeriodHMA; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriodHMA = (InpPeriodHMA < 1) ? 1 : InpPeriodHMA; - - SetIndexBuffer(0, BufferHMA, INDICATOR_DATA); - SetIndexBuffer(1, BufferWMA_Half, INDICATOR_CALCULATIONS); - SetIndexBuffer(2, BufferWMA_Full, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRawHMA, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferHMA, false); - ArraySetAsSeries(BufferWMA_Half, false); - ArraySetAsSeries(BufferWMA_Full, false); - ArraySetAsSeries(BufferRawHMA, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HMA(%d)", g_ExtPeriodHMA)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Hull Moving Average calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Prepare the source price array - double price_source[]; - ArrayResize(price_source, rates_total); - for(int i=0; i 0) - BufferWMA_Half[i] = lwma_sum_half / weight_sum_half; - - // Manual WMA for full period - double lwma_sum_full = 0; - double weight_sum_full = 0; - for(int j=0; j 0) - BufferWMA_Full[i] = lwma_sum_full / weight_sum_full; - - // Calculate Raw HMA - BufferRawHMA[i] = 2 * BufferWMA_Half[i] - BufferWMA_Full[i]; - } - -// --- Second Pass: Calculate final HMA --- - for(int i = start_pos; i < rates_total; i++) - { - // Manual WMA for sqrt period on Raw HMA data - double lwma_sum_sqrt = 0; - double weight_sum_sqrt = 0; - for(int j=0; j 0) - BufferHMA[i] = lwma_sum_sqrt / weight_sum_sqrt; - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| HMA.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.00" // Fully manual, self-contained, and accurate +#property description "Hull Moving Average (HMA)" + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 4 // HMA, and 3 calculation buffers +#property indicator_plots 1 + +//--- Plot 1: HMA line +#property indicator_label1 "HMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDeepPink +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpPeriodHMA = 14; +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; + +//--- Indicator Buffers --- +double BufferHMA[]; +double BufferWMA_Half[]; +double BufferWMA_Full[]; +double BufferRawHMA[]; + +//--- Global Variables --- +int g_ExtPeriodHMA; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriodHMA = (InpPeriodHMA < 1) ? 1 : InpPeriodHMA; + + SetIndexBuffer(0, BufferHMA, INDICATOR_DATA); + SetIndexBuffer(1, BufferWMA_Half, INDICATOR_CALCULATIONS); + SetIndexBuffer(2, BufferWMA_Full, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRawHMA, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferHMA, false); + ArraySetAsSeries(BufferWMA_Half, false); + ArraySetAsSeries(BufferWMA_Full, false); + ArraySetAsSeries(BufferRawHMA, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HMA(%d)", g_ExtPeriodHMA)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Hull Moving Average calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Prepare the source price array + double price_source[]; + ArrayResize(price_source, rates_total); + for(int i=0; i 0) + BufferWMA_Half[i] = lwma_sum_half / weight_sum_half; + + // Manual WMA for full period + double lwma_sum_full = 0; + double weight_sum_full = 0; + for(int j=0; j 0) + BufferWMA_Full[i] = lwma_sum_full / weight_sum_full; + + // Calculate Raw HMA + BufferRawHMA[i] = 2 * BufferWMA_Half[i] - BufferWMA_Full[i]; + } + +// --- Second Pass: Calculate final HMA --- + for(int i = start_pos; i < rates_total; i++) + { + // Manual WMA for sqrt period on Raw HMA data + double lwma_sum_sqrt = 0; + double weight_sum_sqrt = 0; + for(int j=0; j 0) + BufferHMA[i] = lwma_sum_sqrt / weight_sum_sqrt; + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/HMA_HeikinAshi.mq5 b/Indicators/MyIndicators/HMA_HeikinAshi.mq5 index 1173715..11f9018 100644 --- a/Indicators/MyIndicators/HMA_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/HMA_HeikinAshi.mq5 @@ -1,169 +1,169 @@ -//+------------------------------------------------------------------+ -//| HMA_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Hull Moving Average (HMA) on Heikin Ashi data" - -#include -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 4 -#property indicator_plots 1 - -//--- Plot 1: HMA line -#property indicator_label1 "HA_HMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDeepPink -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Enum for selecting Heikin Ashi price source --- -enum ENUM_HA_APPLIED_PRICE - { - HA_PRICE_CLOSE, // Heikin Ashi Close - HA_PRICE_OPEN, // Heikin Ashi Open - HA_PRICE_HIGH, // Heikin Ashi High - HA_PRICE_LOW, // Heikin Ashi Low - }; - -//--- Input Parameters --- -input int InpPeriodHMA = 14; -input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; - -//--- Indicator Buffers --- -double BufferHA_HMA[]; -double BufferWMA_Half[]; -double BufferWMA_Full[]; -double BufferRawHMA[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtPeriodHMA; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtPeriodHMA = (InpPeriodHMA < 1) ? 1 : InpPeriodHMA; - - SetIndexBuffer(0, BufferHA_HMA, INDICATOR_DATA); - SetIndexBuffer(1, BufferWMA_Half, INDICATOR_CALCULATIONS); - SetIndexBuffer(2, BufferWMA_Full, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRawHMA, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferHA_HMA, false); - ArraySetAsSeries(BufferWMA_Half, false); - ArraySetAsSeries(BufferWMA_Full, false); - ArraySetAsSeries(BufferRawHMA, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_HMA(%d)", g_ExtPeriodHMA)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Hull Moving Average on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2; - if(rates_total <= start_pos) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Select the source Heikin Ashi price array - double ha_price_source[]; - switch(InpAppliedPrice) - { - case HA_PRICE_OPEN: - ArrayCopy(ha_price_source, ExtHaOpenBuffer); - break; - case HA_PRICE_HIGH: - ArrayCopy(ha_price_source, ExtHaHighBuffer); - break; - case HA_PRICE_LOW: - ArrayCopy(ha_price_source, ExtHaLowBuffer); - break; - default: - ArrayCopy(ha_price_source, ExtHaCloseBuffer); - break; - } - -//--- STEP 3: Calculate all HMA components in a single, efficient loop - int period_half = (int)MathMax(1, MathRound(g_ExtPeriodHMA / 2.0)); - int period_sqrt = (int)MathMax(1, MathRound(MathSqrt(g_ExtPeriodHMA))); - - for(int i = g_ExtPeriodHMA - 1; i < rates_total; i++) - { - // Calculate the two base WMAs - BufferWMA_Half[i] = LinearWeightedMA(i, period_half, ha_price_source); - BufferWMA_Full[i] = LinearWeightedMA(i, g_ExtPeriodHMA, ha_price_source); - - // Calculate the raw HMA - BufferRawHMA[i] = 2 * BufferWMA_Half[i] - BufferWMA_Full[i]; - } - -//--- STEP 4: Smooth the raw HMA with the final WMA - for(int i = start_pos; i < rates_total; i++) - { - BufferHA_HMA[i] = LinearWeightedMA(i, period_sqrt, BufferRawHMA); - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| HMA_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Hull Moving Average (HMA) on Heikin Ashi data" + +#include +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 4 +#property indicator_plots 1 + +//--- Plot 1: HMA line +#property indicator_label1 "HA_HMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDeepPink +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Enum for selecting Heikin Ashi price source --- +enum ENUM_HA_APPLIED_PRICE + { + HA_PRICE_CLOSE, // Heikin Ashi Close + HA_PRICE_OPEN, // Heikin Ashi Open + HA_PRICE_HIGH, // Heikin Ashi High + HA_PRICE_LOW, // Heikin Ashi Low + }; + +//--- Input Parameters --- +input int InpPeriodHMA = 14; +input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; + +//--- Indicator Buffers --- +double BufferHA_HMA[]; +double BufferWMA_Half[]; +double BufferWMA_Full[]; +double BufferRawHMA[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtPeriodHMA; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtPeriodHMA = (InpPeriodHMA < 1) ? 1 : InpPeriodHMA; + + SetIndexBuffer(0, BufferHA_HMA, INDICATOR_DATA); + SetIndexBuffer(1, BufferWMA_Half, INDICATOR_CALCULATIONS); + SetIndexBuffer(2, BufferWMA_Full, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRawHMA, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferHA_HMA, false); + ArraySetAsSeries(BufferWMA_Half, false); + ArraySetAsSeries(BufferWMA_Full, false); + ArraySetAsSeries(BufferRawHMA, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_HMA(%d)", g_ExtPeriodHMA)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Hull Moving Average on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtPeriodHMA + (int)MathFloor(MathSqrt(g_ExtPeriodHMA)) - 2; + if(rates_total <= start_pos) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Select the source Heikin Ashi price array + double ha_price_source[]; + switch(InpAppliedPrice) + { + case HA_PRICE_OPEN: + ArrayCopy(ha_price_source, ExtHaOpenBuffer); + break; + case HA_PRICE_HIGH: + ArrayCopy(ha_price_source, ExtHaHighBuffer); + break; + case HA_PRICE_LOW: + ArrayCopy(ha_price_source, ExtHaLowBuffer); + break; + default: + ArrayCopy(ha_price_source, ExtHaCloseBuffer); + break; + } + +//--- STEP 3: Calculate all HMA components in a single, efficient loop + int period_half = (int)MathMax(1, MathRound(g_ExtPeriodHMA / 2.0)); + int period_sqrt = (int)MathMax(1, MathRound(MathSqrt(g_ExtPeriodHMA))); + + for(int i = g_ExtPeriodHMA - 1; i < rates_total; i++) + { + // Calculate the two base WMAs + BufferWMA_Half[i] = LinearWeightedMA(i, period_half, ha_price_source); + BufferWMA_Full[i] = LinearWeightedMA(i, g_ExtPeriodHMA, ha_price_source); + + // Calculate the raw HMA + BufferRawHMA[i] = 2 * BufferWMA_Half[i] - BufferWMA_Full[i]; + } + +//--- STEP 4: Smooth the raw HMA with the final WMA + for(int i = start_pos; i < rates_total; i++) + { + BufferHA_HMA[i] = LinearWeightedMA(i, period_sqrt, BufferRawHMA); + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/KeltnerChannel.md b/Indicators/MyIndicators/KeltnerChannel.md index 756d13d..4d3be96 100644 --- a/Indicators/MyIndicators/KeltnerChannel.md +++ b/Indicators/MyIndicators/KeltnerChannel.md @@ -1,72 +1,72 @@ -# Keltner Channel - -## 1. Summary (Introduction) - -The Keltner Channel is a volatility-based technical indicator developed by Chester W. Keltner in his 1960 book "How to Make Money in Commodities." The modern version was later updated by Linda Bradford Raschke, who introduced the use of an Exponential Moving Average (EMA) for the centerline and the Average True Range (ATR) for calculating the channel width. - -The indicator consists of three lines: a central moving average line, an upper band, and a lower band. It is primarily used to identify trend direction, spot potential trend reversals or continuations through breakouts, and gauge volatility. - -## 2. Mathematical Foundations and Calculation Logic - -The Keltner Channel is constructed by creating a channel around a central moving average, with the width of the channel determined by the market's volatility. - -### Required Components - -- **Middle Line (Basis):** A moving average of a selected price. The most common version uses an Exponential Moving Average (EMA) of the Typical Price `(High + Low + Close) / 3`. -- **ATR (Average True Range):** A measure of market volatility. -- **Factor (Multiplier):** A user-defined multiplier that adjusts the width of the channel. - -### Calculation Steps (Algorithm) - -1. **Calculate the Middle Line:** Compute the moving average (e.g., 20-period EMA) of the selected source price. - $\text{Middle Line}_i = \text{MA}(\text{Source Price}, \text{MA Period})_i$ - -2. **Calculate the Average True Range (ATR):** Compute the ATR for a given period (e.g., 10). - -3. **Calculate the Upper and Lower Bands:** Add and subtract a multiple of the ATR from the middle line. - $\text{Upper Band}_i = \text{Middle Line}_i + (\text{Factor} \times \text{ATR}_i)$ - $\text{Lower Band}_i = \text{Middle Line}_i - (\text{Factor} \times \text{ATR}_i)$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementations were refactored based on our core principles to create three distinct, robust, and stable versions of the Keltner Channel. - -- **Stability via Full Recalculation:** All versions employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice to ensure maximum stability and prevent calculation errors, especially with the recursive calculations involved in EMA and ATR. - -- **Robust Manual Calculations:** To ensure 100% accuracy and stability within our `non-timeseries` calculation model, we use fully manual implementations for all moving average types (SMA, EMA, SMMA, LWMA) and for the Wilder's smoothing used in the ATR calculation. Each recursive calculation (EMA, SMMA, ATR) is carefully initialized with a simple average to prevent floating-point overflows. - -- **Clear, Staged Calculation:** The `OnCalculate` function in each version is structured into clear, sequential steps (e.g., Price Preparation, TR Calculation, Integrated MA/ATR/Band Calculation), which improves code readability and maintainability. - -### Our Three Keltner Channel Versions - -1. **Standard Version (`KeltnerChannel.mq5`):** - - - **Concept:** The classic, industry-standard implementation. - - **Logic:** The middle line is a moving average of **standard prices** (e.g., Typical Price). The channel width is determined by the ATR of **standard candlesticks**. - - **Implementation:** To guarantee perfect accuracy with the MetaTrader platform's built-in indicators, this version uses an `iMA` handle for the middle line while calculating the standard ATR manually for consistency. - -2. **Hybrid Heikin Ashi Version (`KeltnerChannel_HeikinAshi.mq5`):** - - - **Concept:** Combines a smoothed Heikin Ashi trend line with real market volatility. - - **Logic:** The middle line is a moving average of **Heikin Ashi prices**. The channel width is determined by the ATR of **standard candlesticks**. - - **Implementation:** Fully self-contained. It uses our `CHeikinAshi_Calculator` for the price data and calculates both the HA-based MA and the standard ATR manually. - -3. **"Pure" Heikin Ashi Version (`KeltnerChannel_HeikinAshi_Pure.mq5`):** - - **Concept:** A fully smoothed channel that reflects the volatility of the underlying Heikin Ashi trend. - - **Logic:** The middle line is a moving average of **Heikin Ashi prices**. The channel width is determined by the ATR calculated from the **Heikin Ashi candlesticks**. - - **Implementation:** Fully self-contained and manual. This version results in narrower, smoother channels compared to the other two. - -## 4. Parameters - -- **MA Period (`InpMaPeriod`):** The lookback period for the middle line moving average. Default is `20`. -- **MA Method (`InpMaMethod`):** The type of moving average for the middle line. Default is `MODE_EMA`. -- **Applied Price (`InpAppliedPrice`):** The source price for the middle line. Default is `PRICE_TYPICAL`. -- **ATR Period (`InpAtrPeriod`):** The lookback period for the ATR calculation. Default is `10`. -- **Multiplier (`InpMultiplier`):** The factor to multiply the ATR by. Default is `2.0`. - -## 5. Usage and Interpretation - -- **Trend Identification:** The slope of the channel helps identify the trend. An upward-sloping channel suggests an uptrend, while a downward-sloping one suggests a downtrend. The middle line acts as the mean of the trend. -- **Breakouts:** A strong close above the upper band can signal the start or continuation of an uptrend. A strong close below the lower band can signal the start or continuation of a downtrend. -- **Overbought/Oversold (in Ranges):** In a sideways market, moves to the upper band can be seen as overbought, and moves to the lower band can be seen as oversold, presenting potential reversal opportunities. -- **Caution:** Like all channel indicators, Keltner Channels can give false breakout signals. It is often used in conjunction with momentum oscillators (like RSI or Stochastics) to confirm the strength of a move. +# Keltner Channel + +## 1. Summary (Introduction) + +The Keltner Channel is a volatility-based technical indicator developed by Chester W. Keltner in his 1960 book "How to Make Money in Commodities." The modern version was later updated by Linda Bradford Raschke, who introduced the use of an Exponential Moving Average (EMA) for the centerline and the Average True Range (ATR) for calculating the channel width. + +The indicator consists of three lines: a central moving average line, an upper band, and a lower band. It is primarily used to identify trend direction, spot potential trend reversals or continuations through breakouts, and gauge volatility. + +## 2. Mathematical Foundations and Calculation Logic + +The Keltner Channel is constructed by creating a channel around a central moving average, with the width of the channel determined by the market's volatility. + +### Required Components + +- **Middle Line (Basis):** A moving average of a selected price. The most common version uses an Exponential Moving Average (EMA) of the Typical Price `(High + Low + Close) / 3`. +- **ATR (Average True Range):** A measure of market volatility. +- **Factor (Multiplier):** A user-defined multiplier that adjusts the width of the channel. + +### Calculation Steps (Algorithm) + +1. **Calculate the Middle Line:** Compute the moving average (e.g., 20-period EMA) of the selected source price. + $\text{Middle Line}_i = \text{MA}(\text{Source Price}, \text{MA Period})_i$ + +2. **Calculate the Average True Range (ATR):** Compute the ATR for a given period (e.g., 10). + +3. **Calculate the Upper and Lower Bands:** Add and subtract a multiple of the ATR from the middle line. + $\text{Upper Band}_i = \text{Middle Line}_i + (\text{Factor} \times \text{ATR}_i)$ + $\text{Lower Band}_i = \text{Middle Line}_i - (\text{Factor} \times \text{ATR}_i)$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementations were refactored based on our core principles to create three distinct, robust, and stable versions of the Keltner Channel. + +- **Stability via Full Recalculation:** All versions employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice to ensure maximum stability and prevent calculation errors, especially with the recursive calculations involved in EMA and ATR. + +- **Robust Manual Calculations:** To ensure 100% accuracy and stability within our `non-timeseries` calculation model, we use fully manual implementations for all moving average types (SMA, EMA, SMMA, LWMA) and for the Wilder's smoothing used in the ATR calculation. Each recursive calculation (EMA, SMMA, ATR) is carefully initialized with a simple average to prevent floating-point overflows. + +- **Clear, Staged Calculation:** The `OnCalculate` function in each version is structured into clear, sequential steps (e.g., Price Preparation, TR Calculation, Integrated MA/ATR/Band Calculation), which improves code readability and maintainability. + +### Our Three Keltner Channel Versions + +1. **Standard Version (`KeltnerChannel.mq5`):** + + - **Concept:** The classic, industry-standard implementation. + - **Logic:** The middle line is a moving average of **standard prices** (e.g., Typical Price). The channel width is determined by the ATR of **standard candlesticks**. + - **Implementation:** To guarantee perfect accuracy with the MetaTrader platform's built-in indicators, this version uses an `iMA` handle for the middle line while calculating the standard ATR manually for consistency. + +2. **Hybrid Heikin Ashi Version (`KeltnerChannel_HeikinAshi.mq5`):** + + - **Concept:** Combines a smoothed Heikin Ashi trend line with real market volatility. + - **Logic:** The middle line is a moving average of **Heikin Ashi prices**. The channel width is determined by the ATR of **standard candlesticks**. + - **Implementation:** Fully self-contained. It uses our `CHeikinAshi_Calculator` for the price data and calculates both the HA-based MA and the standard ATR manually. + +3. **"Pure" Heikin Ashi Version (`KeltnerChannel_HeikinAshi_Pure.mq5`):** + - **Concept:** A fully smoothed channel that reflects the volatility of the underlying Heikin Ashi trend. + - **Logic:** The middle line is a moving average of **Heikin Ashi prices**. The channel width is determined by the ATR calculated from the **Heikin Ashi candlesticks**. + - **Implementation:** Fully self-contained and manual. This version results in narrower, smoother channels compared to the other two. + +## 4. Parameters + +- **MA Period (`InpMaPeriod`):** The lookback period for the middle line moving average. Default is `20`. +- **MA Method (`InpMaMethod`):** The type of moving average for the middle line. Default is `MODE_EMA`. +- **Applied Price (`InpAppliedPrice`):** The source price for the middle line. Default is `PRICE_TYPICAL`. +- **ATR Period (`InpAtrPeriod`):** The lookback period for the ATR calculation. Default is `10`. +- **Multiplier (`InpMultiplier`):** The factor to multiply the ATR by. Default is `2.0`. + +## 5. Usage and Interpretation + +- **Trend Identification:** The slope of the channel helps identify the trend. An upward-sloping channel suggests an uptrend, while a downward-sloping one suggests a downtrend. The middle line acts as the mean of the trend. +- **Breakouts:** A strong close above the upper band can signal the start or continuation of an uptrend. A strong close below the lower band can signal the start or continuation of a downtrend. +- **Overbought/Oversold (in Ranges):** In a sideways market, moves to the upper band can be seen as overbought, and moves to the lower band can be seen as oversold, presenting potential reversal opportunities. +- **Caution:** Like all channel indicators, Keltner Channels can give false breakout signals. It is often used in conjunction with momentum oscillators (like RSI or Stochastics) to confirm the strength of a move. diff --git a/Indicators/MyIndicators/KeltnerChannel.mq5 b/Indicators/MyIndicators/KeltnerChannel.mq5 index 1d0d91f..9654958 100644 --- a/Indicators/MyIndicators/KeltnerChannel.mq5 +++ b/Indicators/MyIndicators/KeltnerChannel.mq5 @@ -1,158 +1,158 @@ -//+------------------------------------------------------------------+ -//| KeltnerChannel.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property version "4.00" // Final Consensus: iMA handle for Middle Line, manual ATR -#property description "Keltner Channels based on ATR" - -#include // Only needed for manual ATR's SMA init - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 4 // Upper, Lower, Middle, and ATR -#property indicator_plots 3 - -//--- Plot 1: Upper Band -#property indicator_label1 "Upper Band" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_DOT - -//--- Plot 2: Lower Band -#property indicator_label2 "Lower Band" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue -#property indicator_style2 STYLE_DOT - -//--- Plot 3: Middle Band (Basis) -#property indicator_label3 "Basis" -#property indicator_type3 DRAW_LINE -#property indicator_color3 clrDodgerBlue -#property indicator_style3 STYLE_SOLID -#property indicator_width3 1 - -//--- Input Parameters --- -input int InpMaPeriod = 20; -input ENUM_MA_METHOD InpMaMethod = MODE_EMA; -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL; -input int InpAtrPeriod = 10; -input double InpMultiplier = 2.0; - -//--- Indicator Buffers --- -double BufferUpper[]; -double BufferLower[]; -double BufferMiddle[]; -double BufferATR[]; - -//--- Global Variables --- -int g_ExtMaPeriod, g_ExtAtrPeriod; -double g_ExtMultiplier; -int g_handle_ma; // Handle for the middle line MA - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; - - SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); - SetIndexBuffer(1, BufferLower, INDICATOR_DATA); - SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); - SetIndexBuffer(3, BufferATR, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferUpper, false); - ArraySetAsSeries(BufferLower, false); - ArraySetAsSeries(BufferMiddle, false); - ArraySetAsSeries(BufferATR, false); - - g_handle_ma = iMA(_Symbol, _Period, g_ExtMaPeriod, 0, InpMaMethod, InpAppliedPrice); - if(g_handle_ma == INVALID_HANDLE) - { - Print("Error creating iMA handle."); - return(INIT_FAILED); - } - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("KC(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - IndicatorRelease(g_handle_ma); - } - -//+------------------------------------------------------------------+ -//| Keltner Channel calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Get Middle Line (MA) values from handle for perfect accuracy - if(CopyBuffer(g_handle_ma, 0, 0, rates_total, BufferMiddle) < rates_total) - { - Print("Error copying iMA buffer data."); - } - -//--- STEP 2: Calculate True Range manually - double tr[]; - ArrayResize(tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); - } - -//--- STEP 3: Calculate ATR and Bands - for(int i = 1; i < rates_total; i++) - { - // --- Calculate ATR (using Wilder's smoothing) --- - if(i == g_ExtAtrPeriod) // Initialization with SMA - { - double atr_sum = 0; - for(int j=1; j<=g_ExtAtrPeriod; j++) - atr_sum += tr[j]; - BufferATR[i] = atr_sum / g_ExtAtrPeriod; - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; - } - - // --- Calculate Upper and Lower bands --- - if(i >= start_pos) - { - BufferUpper[i] = BufferMiddle[i] + (BufferATR[i] * g_ExtMultiplier); - BufferLower[i] = BufferMiddle[i] - (BufferATR[i] * g_ExtMultiplier); - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| KeltnerChannel.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property version "4.00" // Final Consensus: iMA handle for Middle Line, manual ATR +#property description "Keltner Channels based on ATR" + +#include // Only needed for manual ATR's SMA init + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 4 // Upper, Lower, Middle, and ATR +#property indicator_plots 3 + +//--- Plot 1: Upper Band +#property indicator_label1 "Upper Band" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_DOT + +//--- Plot 2: Lower Band +#property indicator_label2 "Lower Band" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue +#property indicator_style2 STYLE_DOT + +//--- Plot 3: Middle Band (Basis) +#property indicator_label3 "Basis" +#property indicator_type3 DRAW_LINE +#property indicator_color3 clrDodgerBlue +#property indicator_style3 STYLE_SOLID +#property indicator_width3 1 + +//--- Input Parameters --- +input int InpMaPeriod = 20; +input ENUM_MA_METHOD InpMaMethod = MODE_EMA; +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL; +input int InpAtrPeriod = 10; +input double InpMultiplier = 2.0; + +//--- Indicator Buffers --- +double BufferUpper[]; +double BufferLower[]; +double BufferMiddle[]; +double BufferATR[]; + +//--- Global Variables --- +int g_ExtMaPeriod, g_ExtAtrPeriod; +double g_ExtMultiplier; +int g_handle_ma; // Handle for the middle line MA + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; + + SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); + SetIndexBuffer(1, BufferLower, INDICATOR_DATA); + SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); + SetIndexBuffer(3, BufferATR, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferUpper, false); + ArraySetAsSeries(BufferLower, false); + ArraySetAsSeries(BufferMiddle, false); + ArraySetAsSeries(BufferATR, false); + + g_handle_ma = iMA(_Symbol, _Period, g_ExtMaPeriod, 0, InpMaMethod, InpAppliedPrice); + if(g_handle_ma == INVALID_HANDLE) + { + Print("Error creating iMA handle."); + return(INIT_FAILED); + } + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("KC(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + IndicatorRelease(g_handle_ma); + } + +//+------------------------------------------------------------------+ +//| Keltner Channel calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Get Middle Line (MA) values from handle for perfect accuracy + if(CopyBuffer(g_handle_ma, 0, 0, rates_total, BufferMiddle) < rates_total) + { + Print("Error copying iMA buffer data."); + } + +//--- STEP 2: Calculate True Range manually + double tr[]; + ArrayResize(tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); + } + +//--- STEP 3: Calculate ATR and Bands + for(int i = 1; i < rates_total; i++) + { + // --- Calculate ATR (using Wilder's smoothing) --- + if(i == g_ExtAtrPeriod) // Initialization with SMA + { + double atr_sum = 0; + for(int j=1; j<=g_ExtAtrPeriod; j++) + atr_sum += tr[j]; + BufferATR[i] = atr_sum / g_ExtAtrPeriod; + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; + } + + // --- Calculate Upper and Lower bands --- + if(i >= start_pos) + { + BufferUpper[i] = BufferMiddle[i] + (BufferATR[i] * g_ExtMultiplier); + BufferLower[i] = BufferMiddle[i] - (BufferATR[i] * g_ExtMultiplier); + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/KeltnerChannel_HeikinAshi.mq5 b/Indicators/MyIndicators/KeltnerChannel_HeikinAshi.mq5 index a1d8b5a..94dd21f 100644 --- a/Indicators/MyIndicators/KeltnerChannel_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/KeltnerChannel_HeikinAshi.mq5 @@ -1,255 +1,255 @@ -//+------------------------------------------------------------------+ -//| KeltnerChannel_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.01" // Corrected OnCalculate signature and SMA logic -#property description "Keltner Channels with HA middle line and Standard ATR" - -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 4 // Upper, Lower, Middle, and ATR -#property indicator_plots 3 - -//--- Plot 1: Upper Band -#property indicator_label1 "HA_Upper" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_DOT - -//--- Plot 2: Lower Band -#property indicator_label2 "HA_Lower" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue -#property indicator_style2 STYLE_DOT - -//--- Plot 3: Middle Band (Basis) -#property indicator_label3 "HA_Basis" -#property indicator_type3 DRAW_LINE -#property indicator_color3 clrDodgerBlue -#property indicator_style3 STYLE_SOLID -#property indicator_width3 1 - -//--- Enum for selecting Heikin Ashi price source for the middle line --- -enum ENUM_HA_APPLIED_PRICE - { - HA_PRICE_CLOSE, // Heikin Ashi Close - HA_PRICE_OPEN, // Heikin Ashi Open - HA_PRICE_HIGH, // Heikin Ashi High - HA_PRICE_LOW, // Heikin Ashi Low - }; - -//--- Input Parameters --- -input int InpMaPeriod = 20; -input ENUM_MA_METHOD InpMaMethod = MODE_EMA; -input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; -input int InpAtrPeriod = 10; -input double InpMultiplier = 2.0; - -//--- Indicator Buffers --- -double BufferUpper[]; -double BufferLower[]; -double BufferMiddle[]; -double BufferATR[]; - -//--- Global Objects and Variables --- -int g_ExtMaPeriod, g_ExtAtrPeriod; -double g_ExtMultiplier; -CHeikinAshi_Calculator *g_ha_calculator; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; - - SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); - SetIndexBuffer(1, BufferLower, INDICATOR_DATA); - SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); - SetIndexBuffer(3, BufferATR, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferUpper, false); - ArraySetAsSeries(BufferLower, false); - ArraySetAsSeries(BufferMiddle, false); - ArraySetAsSeries(BufferATR, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_KC(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); - - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Keltner Channel on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -// --- FIX: Restored the full, correct function signature --- -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[]) - { - int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - if(rates_total <= start_pos) - return(0); - -//--- Intermediate Heikin Ashi Buffers - double ha_open[], ha_high[], ha_low[], ha_close[]; - ArrayResize(ha_open, rates_total); - ArrayResize(ha_high, rates_total); - ArrayResize(ha_low, rates_total); - ArrayResize(ha_close, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); - -//--- STEP 2: Calculate Standard True Range manually - double tr[]; - ArrayResize(tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); - } - -//--- STEP 3: Prepare HA price source for the middle line - double ha_price_source[]; - ArrayResize(ha_price_source, rates_total); - switch(InpAppliedPrice) - { - case HA_PRICE_OPEN: - ArrayCopy(ha_price_source, ha_open); - break; - case HA_PRICE_HIGH: - ArrayCopy(ha_price_source, ha_high); - break; - case HA_PRICE_LOW: - ArrayCopy(ha_price_source, ha_low); - break; - default: - ArrayCopy(ha_price_source, ha_close); - break; - } - -//--- STEP 4: Calculate ATR, Middle, Upper, and Lower bands - double sma_sum = 0; - for(int i = 1; i < rates_total; i++) - { - // --- Calculate Standard ATR (using Wilder's smoothing) --- - if(i == g_ExtAtrPeriod) // Initialization with manual SMA - { - double atr_sum = 0; - for(int j=1; j<=g_ExtAtrPeriod; j++) - atr_sum += tr[j]; - BufferATR[i] = atr_sum / g_ExtAtrPeriod; - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; - } - - // --- Calculate the middle line (MA on HA price) --- - if(i >= g_ExtMaPeriod - 1) - { - switch(InpMaMethod) - { - case MODE_EMA: - case MODE_SMMA: - if(i == g_ExtMaPeriod - 1) - { - double sum = 0; - for(int j=0; j 0) - BufferMiddle[i] = lwma_sum / weight_sum; - } - break; - default: // MODE_SMA - if(i == g_ExtMaPeriod - 1) // First calculation - { - sma_sum = 0; // Re-initialize sum for the first calculation point - for(int j=0; j= start_pos) - { - BufferUpper[i] = BufferMiddle[i] + (BufferATR[i] * g_ExtMultiplier); - BufferLower[i] = BufferMiddle[i] - (BufferATR[i] * g_ExtMultiplier); - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| KeltnerChannel_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.01" // Corrected OnCalculate signature and SMA logic +#property description "Keltner Channels with HA middle line and Standard ATR" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 4 // Upper, Lower, Middle, and ATR +#property indicator_plots 3 + +//--- Plot 1: Upper Band +#property indicator_label1 "HA_Upper" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_DOT + +//--- Plot 2: Lower Band +#property indicator_label2 "HA_Lower" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue +#property indicator_style2 STYLE_DOT + +//--- Plot 3: Middle Band (Basis) +#property indicator_label3 "HA_Basis" +#property indicator_type3 DRAW_LINE +#property indicator_color3 clrDodgerBlue +#property indicator_style3 STYLE_SOLID +#property indicator_width3 1 + +//--- Enum for selecting Heikin Ashi price source for the middle line --- +enum ENUM_HA_APPLIED_PRICE + { + HA_PRICE_CLOSE, // Heikin Ashi Close + HA_PRICE_OPEN, // Heikin Ashi Open + HA_PRICE_HIGH, // Heikin Ashi High + HA_PRICE_LOW, // Heikin Ashi Low + }; + +//--- Input Parameters --- +input int InpMaPeriod = 20; +input ENUM_MA_METHOD InpMaMethod = MODE_EMA; +input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; +input int InpAtrPeriod = 10; +input double InpMultiplier = 2.0; + +//--- Indicator Buffers --- +double BufferUpper[]; +double BufferLower[]; +double BufferMiddle[]; +double BufferATR[]; + +//--- Global Objects and Variables --- +int g_ExtMaPeriod, g_ExtAtrPeriod; +double g_ExtMultiplier; +CHeikinAshi_Calculator *g_ha_calculator; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; + + SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); + SetIndexBuffer(1, BufferLower, INDICATOR_DATA); + SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); + SetIndexBuffer(3, BufferATR, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferUpper, false); + ArraySetAsSeries(BufferLower, false); + ArraySetAsSeries(BufferMiddle, false); + ArraySetAsSeries(BufferATR, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_KC(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); + + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Keltner Channel on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +// --- FIX: Restored the full, correct function signature --- +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[]) + { + int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + if(rates_total <= start_pos) + return(0); + +//--- Intermediate Heikin Ashi Buffers + double ha_open[], ha_high[], ha_low[], ha_close[]; + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + +//--- STEP 2: Calculate Standard True Range manually + double tr[]; + ArrayResize(tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); + } + +//--- STEP 3: Prepare HA price source for the middle line + double ha_price_source[]; + ArrayResize(ha_price_source, rates_total); + switch(InpAppliedPrice) + { + case HA_PRICE_OPEN: + ArrayCopy(ha_price_source, ha_open); + break; + case HA_PRICE_HIGH: + ArrayCopy(ha_price_source, ha_high); + break; + case HA_PRICE_LOW: + ArrayCopy(ha_price_source, ha_low); + break; + default: + ArrayCopy(ha_price_source, ha_close); + break; + } + +//--- STEP 4: Calculate ATR, Middle, Upper, and Lower bands + double sma_sum = 0; + for(int i = 1; i < rates_total; i++) + { + // --- Calculate Standard ATR (using Wilder's smoothing) --- + if(i == g_ExtAtrPeriod) // Initialization with manual SMA + { + double atr_sum = 0; + for(int j=1; j<=g_ExtAtrPeriod; j++) + atr_sum += tr[j]; + BufferATR[i] = atr_sum / g_ExtAtrPeriod; + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferATR[i] = (BufferATR[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod; + } + + // --- Calculate the middle line (MA on HA price) --- + if(i >= g_ExtMaPeriod - 1) + { + switch(InpMaMethod) + { + case MODE_EMA: + case MODE_SMMA: + if(i == g_ExtMaPeriod - 1) + { + double sum = 0; + for(int j=0; j 0) + BufferMiddle[i] = lwma_sum / weight_sum; + } + break; + default: // MODE_SMA + if(i == g_ExtMaPeriod - 1) // First calculation + { + sma_sum = 0; // Re-initialize sum for the first calculation point + for(int j=0; j= start_pos) + { + BufferUpper[i] = BufferMiddle[i] + (BufferATR[i] * g_ExtMultiplier); + BufferLower[i] = BufferMiddle[i] - (BufferATR[i] * g_ExtMultiplier); + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/KeltnerChannel_HeikinAshi_Pure.mq5 b/Indicators/MyIndicators/KeltnerChannel_HeikinAshi_Pure.mq5 index b852dc3..1352dd1 100644 --- a/Indicators/MyIndicators/KeltnerChannel_HeikinAshi_Pure.mq5 +++ b/Indicators/MyIndicators/KeltnerChannel_HeikinAshi_Pure.mq5 @@ -1,230 +1,230 @@ -//+------------------------------------------------------------------+ -//| KeltnerChannel_HeikinAshi_Pure.mq5| -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.01" // Corrected include and completed switch-case -#property description "Keltner Channels based entirely on Heikin Ashi data (including ATR)" - -#include -#include // <-- FIX: Corrected filename - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 4 // Upper, Lower, Middle, and Smoothed ATR -#property indicator_plots 3 - -//--- Plot 1: Upper Band -#property indicator_label1 "HA_Upper" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_DOT - -//--- Plot 2: Lower Band -#property indicator_label2 "HA_Lower" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue -#property indicator_style2 STYLE_DOT - -//--- Plot 3: Middle Band (Basis) -#property indicator_label3 "HA_Basis" -#property indicator_type3 DRAW_LINE -#property indicator_color3 clrDodgerBlue -#property indicator_style3 STYLE_SOLID -#property indicator_width3 1 - -//--- Enum for selecting Heikin Ashi price source for the middle line --- -enum ENUM_HA_APPLIED_PRICE - { - HA_PRICE_CLOSE, // Heikin Ashi Close - HA_PRICE_OPEN, // Heikin Ashi Open - HA_PRICE_HIGH, // Heikin Ashi High - HA_PRICE_LOW, // Heikin Ashi Low - }; - -//--- Input Parameters --- -input int InpMaPeriod = 20; -input ENUM_MA_METHOD InpMaMethod = MODE_EMA; -input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; // HA price for the middle line -input int InpAtrPeriod = 10; -input double InpMultiplier = 2.0; - -//--- Indicator Buffers --- -double BufferUpper[]; -double BufferLower[]; -double BufferMiddle[]; -double BufferHA_ATR[]; // Buffer for Heikin Ashi ATR - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtMaPeriod, g_ExtAtrPeriod; -double g_ExtMultiplier; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; - - SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); - SetIndexBuffer(1, BufferLower, INDICATOR_DATA); - SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); - SetIndexBuffer(3, BufferHA_ATR, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferUpper, false); - ArraySetAsSeries(BufferLower, false); - ArraySetAsSeries(BufferMiddle, false); - ArraySetAsSeries(BufferHA_ATR, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); - PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_KC_Pure(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Keltner Channel on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); - if(rates_total <= start_pos) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate Heikin Ashi True Range - double ha_tr[]; - ArrayResize(ha_tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - ha_tr[i] = MathMax(ExtHaHighBuffer[i], ExtHaCloseBuffer[i-1]) - MathMin(ExtHaLowBuffer[i], ExtHaCloseBuffer[i-1]); - } - -//--- STEP 3: Select the source Heikin Ashi price array for the middle line - double ha_price_source[]; - switch(InpAppliedPrice) - { - case HA_PRICE_OPEN: - ArrayCopy(ha_price_source, ExtHaOpenBuffer); - break; - case HA_PRICE_HIGH: - ArrayCopy(ha_price_source, ExtHaHighBuffer); - break; - case HA_PRICE_LOW: - ArrayCopy(ha_price_source, ExtHaLowBuffer); - break; - default: - ArrayCopy(ha_price_source, ExtHaCloseBuffer); - break; - } - -//--- STEP 4: Calculate HA_ATR, Middle, Upper, and Lower bands in a single loop - for(int i = 1; i < rates_total; i++) - { - // --- Calculate Heikin Ashi ATR (using Wilder's smoothing) --- - if(i == g_ExtAtrPeriod) // Initialization with SMA - { - BufferHA_ATR[i] = SimpleMA(i, g_ExtAtrPeriod, ha_tr); - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; - } - - // --- Calculate the middle line (MA on HA price) --- - if(i >= g_ExtMaPeriod - 1) - { - switch(InpMaMethod) - { - case MODE_EMA: - if(i == g_ExtMaPeriod - 1) - BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); - else - { - double pr = 2.0 / (g_ExtMaPeriod + 1.0); - BufferMiddle[i] = ha_price_source[i] * pr + BufferMiddle[i-1] * (1.0 - pr); - } - break; - // --- FIX: Added missing cases --- - case MODE_SMMA: - if(i == g_ExtMaPeriod - 1) - BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); - else - BufferMiddle[i] = (BufferMiddle[i-1] * (g_ExtMaPeriod - 1) + ha_price_source[i]) / g_ExtMaPeriod; - break; - case MODE_LWMA: - BufferMiddle[i] = LinearWeightedMA(i, g_ExtMaPeriod, ha_price_source); - break; - default: // MODE_SMA - BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); - break; - } - } - - // --- Calculate Upper and Lower bands --- - if(i >= start_pos) - { - BufferUpper[i] = BufferMiddle[i] + (BufferHA_ATR[i] * g_ExtMultiplier); - BufferLower[i] = BufferMiddle[i] - (BufferHA_ATR[i] * g_ExtMultiplier); - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| KeltnerChannel_HeikinAshi_Pure.mq5| +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.01" // Corrected include and completed switch-case +#property description "Keltner Channels based entirely on Heikin Ashi data (including ATR)" + +#include +#include // <-- FIX: Corrected filename + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 4 // Upper, Lower, Middle, and Smoothed ATR +#property indicator_plots 3 + +//--- Plot 1: Upper Band +#property indicator_label1 "HA_Upper" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_DOT + +//--- Plot 2: Lower Band +#property indicator_label2 "HA_Lower" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue +#property indicator_style2 STYLE_DOT + +//--- Plot 3: Middle Band (Basis) +#property indicator_label3 "HA_Basis" +#property indicator_type3 DRAW_LINE +#property indicator_color3 clrDodgerBlue +#property indicator_style3 STYLE_SOLID +#property indicator_width3 1 + +//--- Enum for selecting Heikin Ashi price source for the middle line --- +enum ENUM_HA_APPLIED_PRICE + { + HA_PRICE_CLOSE, // Heikin Ashi Close + HA_PRICE_OPEN, // Heikin Ashi Open + HA_PRICE_HIGH, // Heikin Ashi High + HA_PRICE_LOW, // Heikin Ashi Low + }; + +//--- Input Parameters --- +input int InpMaPeriod = 20; +input ENUM_MA_METHOD InpMaMethod = MODE_EMA; +input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; // HA price for the middle line +input int InpAtrPeriod = 10; +input double InpMultiplier = 2.0; + +//--- Indicator Buffers --- +double BufferUpper[]; +double BufferLower[]; +double BufferMiddle[]; +double BufferHA_ATR[]; // Buffer for Heikin Ashi ATR + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtMaPeriod, g_ExtAtrPeriod; +double g_ExtMultiplier; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtMaPeriod = (InpMaPeriod < 1) ? 1 : InpMaPeriod; + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtMultiplier = (InpMultiplier <= 0) ? 2.0 : InpMultiplier; + + SetIndexBuffer(0, BufferUpper, INDICATOR_DATA); + SetIndexBuffer(1, BufferLower, INDICATOR_DATA); + SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA); + SetIndexBuffer(3, BufferHA_ATR, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferUpper, false); + ArraySetAsSeries(BufferLower, false); + ArraySetAsSeries(BufferMiddle, false); + ArraySetAsSeries(BufferHA_ATR, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + int draw_begin = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, draw_begin); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, g_ExtMaPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_KC_Pure(%d,%d,%.1f)", g_ExtMaPeriod, g_ExtAtrPeriod, g_ExtMultiplier)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Keltner Channel on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = MathMax(g_ExtMaPeriod, g_ExtAtrPeriod); + if(rates_total <= start_pos) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate Heikin Ashi True Range + double ha_tr[]; + ArrayResize(ha_tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + ha_tr[i] = MathMax(ExtHaHighBuffer[i], ExtHaCloseBuffer[i-1]) - MathMin(ExtHaLowBuffer[i], ExtHaCloseBuffer[i-1]); + } + +//--- STEP 3: Select the source Heikin Ashi price array for the middle line + double ha_price_source[]; + switch(InpAppliedPrice) + { + case HA_PRICE_OPEN: + ArrayCopy(ha_price_source, ExtHaOpenBuffer); + break; + case HA_PRICE_HIGH: + ArrayCopy(ha_price_source, ExtHaHighBuffer); + break; + case HA_PRICE_LOW: + ArrayCopy(ha_price_source, ExtHaLowBuffer); + break; + default: + ArrayCopy(ha_price_source, ExtHaCloseBuffer); + break; + } + +//--- STEP 4: Calculate HA_ATR, Middle, Upper, and Lower bands in a single loop + for(int i = 1; i < rates_total; i++) + { + // --- Calculate Heikin Ashi ATR (using Wilder's smoothing) --- + if(i == g_ExtAtrPeriod) // Initialization with SMA + { + BufferHA_ATR[i] = SimpleMA(i, g_ExtAtrPeriod, ha_tr); + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; + } + + // --- Calculate the middle line (MA on HA price) --- + if(i >= g_ExtMaPeriod - 1) + { + switch(InpMaMethod) + { + case MODE_EMA: + if(i == g_ExtMaPeriod - 1) + BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); + else + { + double pr = 2.0 / (g_ExtMaPeriod + 1.0); + BufferMiddle[i] = ha_price_source[i] * pr + BufferMiddle[i-1] * (1.0 - pr); + } + break; + // --- FIX: Added missing cases --- + case MODE_SMMA: + if(i == g_ExtMaPeriod - 1) + BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); + else + BufferMiddle[i] = (BufferMiddle[i-1] * (g_ExtMaPeriod - 1) + ha_price_source[i]) / g_ExtMaPeriod; + break; + case MODE_LWMA: + BufferMiddle[i] = LinearWeightedMA(i, g_ExtMaPeriod, ha_price_source); + break; + default: // MODE_SMA + BufferMiddle[i] = SimpleMA(i, g_ExtMaPeriod, ha_price_source); + break; + } + } + + // --- Calculate Upper and Lower bands --- + if(i >= start_pos) + { + BufferUpper[i] = BufferMiddle[i] + (BufferHA_ATR[i] * g_ExtMultiplier); + BufferLower[i] = BufferMiddle[i] - (BufferHA_ATR[i] * g_ExtMultiplier); + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/McGinleyDynamic.md b/Indicators/MyIndicators/McGinleyDynamic.md index 2920a0c..5f1a6dd 100644 --- a/Indicators/MyIndicators/McGinleyDynamic.md +++ b/Indicators/MyIndicators/McGinleyDynamic.md @@ -1,62 +1,62 @@ -# McGinley Dynamic Indicator - -## 1. Summary (Introduction) - -The McGinley Dynamic indicator was developed in the 1990s by John R. McGinley, a Chartered Market Technician. It was designed to be a more responsive and reliable alternative to traditional moving averages. Unlike moving averages that use a fixed period, the McGinley Dynamic automatically adjusts its speed based on the speed of the market itself. - -Its primary purpose is to hug prices more closely, minimizing whipsaws and providing a smoother, more trustworthy trend line. It speeds up in down markets to protect capital and slows down in up markets to let profits run. - -## 2. Mathematical Foundations and Calculation Logic - -The core of the McGinley Dynamic is its unique, self-adjusting smoothing factor. The formula is recursive, with each new value depending on the previous one. - -### Required Components - -- **Length (N):** The base period for the indicator, similar to a moving average period. -- **Source Price (P):** The price series used for the calculation (e.g., Close). - -### Calculation Steps (Algorithm) - -1. **Initialization:** The very first value of the McGinley Dynamic line is typically the first available source price. - $\text{MD}_0 = P_0$ - -2. **Recursive Calculation:** All subsequent values are calculated using the following formula: - $\text{MD}_i = \text{MD}_{i-1} + \frac{P_i - \text{MD}_{i-1}}{N \times (\frac{P_i}{\text{MD}_{i-1}})^4}$ - Where: - - $\text{MD}_i$ is the current McGinley Dynamic value. - - $\text{MD}_{i-1}$ is the previous McGinley Dynamic value. - - $P_i$ is the current source price. - - $N$ is the Length parameter. - -The key component is the denominator: $N \times (\frac{P_i}{\text{MD}_{i-1}})^4$. The ratio $(\frac{P_i}{\text{MD}_{i-1}})$ measures the speed of the market. - -- When the price ($P_i$) is moving away from the indicator line ($\text{MD}_{i-1}$), the ratio becomes larger or smaller than 1. Raising it to the 4th power significantly amplifies this difference, making the denominator larger and the adjustment smaller, causing the indicator to "lag" less and follow the price more closely. -- When the price is moving slowly, the ratio is close to 1, and the indicator behaves more like a traditional moving average with period N. - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored for maximum stability, clarity, and efficiency. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a recursive indicator like the McGinley Dynamic, this is the most reliable method to prevent calculation errors and ensure stability, especially during timeframe changes. - -- **Robust Initialization:** The recursive calculation is carefully initialized. The first value of the indicator (`BufferMcGinley[0]`) is set directly to the first available source price (`price_source[0]`). This is a simple and highly stable method that provides a valid starting point for all subsequent recursive calculations, avoiding potential overflows or division-by-zero errors. - -- **Efficient Price Handling:** Instead of using an `iMA` handle to fetch the source price, our implementation directly accesses the `open`, `high`, `low`, and `close` arrays provided by `OnCalculate`. A `switch` block determines the correct source and copies the data into a single `price_source[]` array. This makes the indicator self-contained and more efficient, as it avoids the overhead of an external indicator call. - -- **Defensive Coding:** The calculation loop includes explicit checks to prevent division by zero, both if the previous indicator value is zero and if the calculated denominator becomes zero. This further enhances the indicator's robustness. - -- **Heikin Ashi Variant (`McGinleyDynamic_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. - - This results in an exceptionally smooth trend line, as both the input data and the indicator's formula are designed to filter out market noise. It is ideal for traders seeking to identify the primary, underlying trend with minimal distractions. - -## 4. Parameters - -- **Length (`InpLength`):** The base period for the indicator. McGinley suggested that this value should be approximately 60% of the period of a corresponding simple moving average. For example, a 14-period McGinley Dynamic is comparable in speed to a ~23-period SMA. Default is `14`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). - -## 5. Usage and Interpretation - -- **Trend Identification:** The McGinley Dynamic is primarily used as a dynamic trend line. When the price is above the line, the trend is considered bullish. When the price is below the line, the trend is considered bearish. -- **Dynamic Support and Resistance:** The line itself can act as a more reliable level of dynamic support in an uptrend or resistance in a downtrend compared to traditional moving averages, as it reacts more quickly to changes in market speed. -- **Crossovers:** While not its primary purpose, crossovers of the price and the McGinley Dynamic line can be used as trade signals, similar to a standard moving average crossover system. -- **Caution:** While it reduces whipsaws, no indicator is perfect. It is still a lagging indicator (though less so than others) and should be used in conjunction with other forms of analysis for confirmation. +# McGinley Dynamic Indicator + +## 1. Summary (Introduction) + +The McGinley Dynamic indicator was developed in the 1990s by John R. McGinley, a Chartered Market Technician. It was designed to be a more responsive and reliable alternative to traditional moving averages. Unlike moving averages that use a fixed period, the McGinley Dynamic automatically adjusts its speed based on the speed of the market itself. + +Its primary purpose is to hug prices more closely, minimizing whipsaws and providing a smoother, more trustworthy trend line. It speeds up in down markets to protect capital and slows down in up markets to let profits run. + +## 2. Mathematical Foundations and Calculation Logic + +The core of the McGinley Dynamic is its unique, self-adjusting smoothing factor. The formula is recursive, with each new value depending on the previous one. + +### Required Components + +- **Length (N):** The base period for the indicator, similar to a moving average period. +- **Source Price (P):** The price series used for the calculation (e.g., Close). + +### Calculation Steps (Algorithm) + +1. **Initialization:** The very first value of the McGinley Dynamic line is typically the first available source price. + $\text{MD}_0 = P_0$ + +2. **Recursive Calculation:** All subsequent values are calculated using the following formula: + $\text{MD}_i = \text{MD}_{i-1} + \frac{P_i - \text{MD}_{i-1}}{N \times (\frac{P_i}{\text{MD}_{i-1}})^4}$ + Where: + - $\text{MD}_i$ is the current McGinley Dynamic value. + - $\text{MD}_{i-1}$ is the previous McGinley Dynamic value. + - $P_i$ is the current source price. + - $N$ is the Length parameter. + +The key component is the denominator: $N \times (\frac{P_i}{\text{MD}_{i-1}})^4$. The ratio $(\frac{P_i}{\text{MD}_{i-1}})$ measures the speed of the market. + +- When the price ($P_i$) is moving away from the indicator line ($\text{MD}_{i-1}$), the ratio becomes larger or smaller than 1. Raising it to the 4th power significantly amplifies this difference, making the denominator larger and the adjustment smaller, causing the indicator to "lag" less and follow the price more closely. +- When the price is moving slowly, the ratio is close to 1, and the indicator behaves more like a traditional moving average with period N. + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored for maximum stability, clarity, and efficiency. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a recursive indicator like the McGinley Dynamic, this is the most reliable method to prevent calculation errors and ensure stability, especially during timeframe changes. + +- **Robust Initialization:** The recursive calculation is carefully initialized. The first value of the indicator (`BufferMcGinley[0]`) is set directly to the first available source price (`price_source[0]`). This is a simple and highly stable method that provides a valid starting point for all subsequent recursive calculations, avoiding potential overflows or division-by-zero errors. + +- **Efficient Price Handling:** Instead of using an `iMA` handle to fetch the source price, our implementation directly accesses the `open`, `high`, `low`, and `close` arrays provided by `OnCalculate`. A `switch` block determines the correct source and copies the data into a single `price_source[]` array. This makes the indicator self-contained and more efficient, as it avoids the overhead of an external indicator call. + +- **Defensive Coding:** The calculation loop includes explicit checks to prevent division by zero, both if the previous indicator value is zero and if the calculated denominator becomes zero. This further enhances the indicator's robustness. + +- **Heikin Ashi Variant (`McGinleyDynamic_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data (e.g., `ha_close`) as its input. + - This results in an exceptionally smooth trend line, as both the input data and the indicator's formula are designed to filter out market noise. It is ideal for traders seeking to identify the primary, underlying trend with minimal distractions. + +## 4. Parameters + +- **Length (`InpLength`):** The base period for the indicator. McGinley suggested that this value should be approximately 60% of the period of a corresponding simple moving average. For example, a 14-period McGinley Dynamic is comparable in speed to a ~23-period SMA. Default is `14`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). + +## 5. Usage and Interpretation + +- **Trend Identification:** The McGinley Dynamic is primarily used as a dynamic trend line. When the price is above the line, the trend is considered bullish. When the price is below the line, the trend is considered bearish. +- **Dynamic Support and Resistance:** The line itself can act as a more reliable level of dynamic support in an uptrend or resistance in a downtrend compared to traditional moving averages, as it reacts more quickly to changes in market speed. +- **Crossovers:** While not its primary purpose, crossovers of the price and the McGinley Dynamic line can be used as trade signals, similar to a standard moving average crossover system. +- **Caution:** While it reduces whipsaws, no indicator is perfect. It is still a lagging indicator (though less so than others) and should be used in conjunction with other forms of analysis for confirmation. diff --git a/Indicators/MyIndicators/McGinleyDynamic.mq5 b/Indicators/MyIndicators/McGinleyDynamic.mq5 index deacede..e83ed67 100644 --- a/Indicators/MyIndicators/McGinleyDynamic.mq5 +++ b/Indicators/MyIndicators/McGinleyDynamic.mq5 @@ -1,142 +1,142 @@ -//+------------------------------------------------------------------+ -//| McGinleyDynamic.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.01" // Corrected array handling for MQL5 syntax -#property description "McGinley Dynamic Indicator" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: McGinley Dynamic line -#property indicator_label1 "McGinley" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrCrimson -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpLength = 14; // Period -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied Price - -//--- Indicator Buffers --- -double BufferMcGinley[]; - -//--- Global Variables --- -int g_ExtLength; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input - g_ExtLength = (InpLength < 1) ? 1 : InpLength; - -//--- Map the buffer and set as non-timeseries - SetIndexBuffer(0, BufferMcGinley, INDICATOR_DATA); - ArraySetAsSeries(BufferMcGinley, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("McGinley(%d)", g_ExtLength)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| McGinley Dynamic calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < 2) - return(0); - -//--- STEP 1: Prepare the source price array - double price_source[]; - ArrayResize(price_source, rates_total); - - switch(InpAppliedPrice) - { - case PRICE_OPEN: - ArrayCopy(price_source, open, 0, 0, rates_total); - break; - case PRICE_HIGH: - ArrayCopy(price_source, high, 0, 0, rates_total); - break; - case PRICE_LOW: - ArrayCopy(price_source, low, 0, 0, rates_total); - break; - case PRICE_MEDIAN: - case PRICE_TYPICAL: - case PRICE_WEIGHTED: - for(int i=0; i - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: McGinley Dynamic line -#property indicator_label1 "HA_McGinley" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrCrimson -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Enum for selecting Heikin Ashi price source --- -enum ENUM_HA_APPLIED_PRICE - { - HA_PRICE_CLOSE, // Heikin Ashi Close - HA_PRICE_OPEN, // Heikin Ashi Open - HA_PRICE_HIGH, // Heikin Ashi High - HA_PRICE_LOW, // Heikin Ashi Low - }; - -//--- Input Parameters --- -input int InpLength = 14; -input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; - -//--- Indicator Buffers --- -double BufferHA_McGinley[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtLength; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtLength = (InpLength < 1) ? 1 : InpLength; - - SetIndexBuffer(0, BufferHA_McGinley, INDICATOR_DATA); - ArraySetAsSeries(BufferHA_McGinley, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 1); // McGinley can be drawn from the 2nd bar - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_McGinley(%d)", g_ExtLength)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| McGinley Dynamic on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < 2) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Select the source Heikin Ashi price array - double ha_price_source[]; - switch(InpAppliedPrice) - { - case HA_PRICE_OPEN: - ArrayCopy(ha_price_source, ExtHaOpenBuffer); - break; - case HA_PRICE_HIGH: - ArrayCopy(ha_price_source, ExtHaHighBuffer); - break; - case HA_PRICE_LOW: - ArrayCopy(ha_price_source, ExtHaLowBuffer); - break; - default: - ArrayCopy(ha_price_source, ExtHaCloseBuffer); - break; - } - -//--- STEP 3: Main calculation loop for McGinley Dynamic - for(int i = 0; i < rates_total; i++) - { - // --- Initialization Step --- - if(i == 0) - { - // The first McGinley value is simply the first source price - BufferHA_McGinley[i] = ha_price_source[i]; - continue; - } - - // --- Recursive Calculation Step --- - double prev_mg = BufferHA_McGinley[i-1]; - - // Prevent division by zero if the previous value was somehow zero - if(prev_mg == 0) - { - BufferHA_McGinley[i] = ha_price_source[i]; - continue; - } - - double denominator = g_ExtLength * MathPow(ha_price_source[i] / prev_mg, 4); - - // Prevent division by zero if the denominator becomes zero - if(denominator == 0) - { - BufferHA_McGinley[i] = prev_mg; - continue; - } - - BufferHA_McGinley[i] = prev_mg + (ha_price_source[i] - prev_mg) / denominator; - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| McGinleyDynamic_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "McGinley Dynamic Indicator on Heikin Ashi data" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: McGinley Dynamic line +#property indicator_label1 "HA_McGinley" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrCrimson +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Enum for selecting Heikin Ashi price source --- +enum ENUM_HA_APPLIED_PRICE + { + HA_PRICE_CLOSE, // Heikin Ashi Close + HA_PRICE_OPEN, // Heikin Ashi Open + HA_PRICE_HIGH, // Heikin Ashi High + HA_PRICE_LOW, // Heikin Ashi Low + }; + +//--- Input Parameters --- +input int InpLength = 14; +input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE; + +//--- Indicator Buffers --- +double BufferHA_McGinley[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtLength; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtLength = (InpLength < 1) ? 1 : InpLength; + + SetIndexBuffer(0, BufferHA_McGinley, INDICATOR_DATA); + ArraySetAsSeries(BufferHA_McGinley, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 1); // McGinley can be drawn from the 2nd bar + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_McGinley(%d)", g_ExtLength)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| McGinley Dynamic on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total < 2) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Select the source Heikin Ashi price array + double ha_price_source[]; + switch(InpAppliedPrice) + { + case HA_PRICE_OPEN: + ArrayCopy(ha_price_source, ExtHaOpenBuffer); + break; + case HA_PRICE_HIGH: + ArrayCopy(ha_price_source, ExtHaHighBuffer); + break; + case HA_PRICE_LOW: + ArrayCopy(ha_price_source, ExtHaLowBuffer); + break; + default: + ArrayCopy(ha_price_source, ExtHaCloseBuffer); + break; + } + +//--- STEP 3: Main calculation loop for McGinley Dynamic + for(int i = 0; i < rates_total; i++) + { + // --- Initialization Step --- + if(i == 0) + { + // The first McGinley value is simply the first source price + BufferHA_McGinley[i] = ha_price_source[i]; + continue; + } + + // --- Recursive Calculation Step --- + double prev_mg = BufferHA_McGinley[i-1]; + + // Prevent division by zero if the previous value was somehow zero + if(prev_mg == 0) + { + BufferHA_McGinley[i] = ha_price_source[i]; + continue; + } + + double denominator = g_ExtLength * MathPow(ha_price_source[i] / prev_mg, 4); + + // Prevent division by zero if the denominator becomes zero + if(denominator == 0) + { + BufferHA_McGinley[i] = prev_mg; + continue; + } + + BufferHA_McGinley[i] = prev_mg + (ha_price_source[i] - prev_mg) / denominator; + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/RSIMa.md b/Indicators/MyIndicators/RSIMa.md index f38fb72..b730dfc 100644 --- a/Indicators/MyIndicators/RSIMa.md +++ b/Indicators/MyIndicators/RSIMa.md @@ -1,63 +1,63 @@ -# Moving Average of RSI (RSIMA) - -## 1. Summary (Introduction) - -The Moving Average of RSI, often referred to as RSIMA, is a technical indicator that smooths the standard Relative Strength Index (RSI) with a moving average. While the RSI is a powerful momentum oscillator, it can often be volatile, producing sharp movements and "noisy" signals. - -By applying a moving average to the RSI line, the RSIMA filters out this short-term noise, providing a smoother line that can make the underlying momentum trend easier to identify. It essentially acts as a signal line for the RSI, similar to how the %D line acts as a signal line for the Stochastic Oscillator. - -## 2. Mathematical Foundations and Calculation Logic - -The RSIMA is a two-stage indicator. It first calculates the standard RSI and then applies a moving average to the resulting RSI values. - -### Required Components - -- **RSI (Relative Strength Index):** The underlying momentum oscillator. -- **Moving Average (MA):** The smoothing mechanism applied to the RSI line. This can be a Simple (SMA), Exponential (EMA), Smoothed (SMMA), or Linear Weighted (LWMA) moving average. - -### Calculation Steps (Algorithm) - -1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). The RSI formula is based on Wilder's smoothing of average gains and average losses. - $\text{RS}_i = \frac{\text{Wilder's MA}(\text{Up Moves}, \text{RSI Period})_i}{\text{Wilder's MA}(\text{Down Moves}, \text{RSI Period})_i}$ - $\text{RSI}_i = 100 - \frac{100}{1 + \text{RS}_i}$ - -2. **Calculate the Moving Average of RSI:** Apply the selected moving average type with its specified period to the RSI data series calculated in the first step. - $\text{RSIMA}_i = \text{MA}(\text{RSI}, \text{MA Period})_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for indicators involving recursive calculations (like EMA or SMMA) to ensure maximum stability and prevent errors during timeframe changes or history loading. - -- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series. The handle's resources are properly managed and released in the `OnDeinit` function. - -- **Robust EMA/SMMA Initialization:** This is the most critical part of the implementation. Standard library functions for recursive MAs can be unstable in a full recalculation model. To guarantee stability, we take control of the initialization process: - - - For EMA and SMMA calculations, the **first value** of the moving average is calculated using a **manual Simple Moving Average (SMA)** on the preceding RSI data. - - This provides a stable, valid starting point for all subsequent recursive calculations, completely eliminating the risk of floating-point overflows that can occur with uninitialized data. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: - - 1. **Step 1:** The complete RSI data series is retrieved into the `BufferRawRSI` calculation buffer using `CopyBuffer`. - 2. **Step 2:** A single `for` loop calculates the moving average on the `BufferRawRSI` data, utilizing our robust `switch` block to handle the different MA types and their specific initialization needs. - -- **Heikin Ashi Variant (`RSI_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_close` values as the input for the initial RSI calculation. - - This results in a doubly-smoothed oscillator, ideal for traders who want to focus only on the most significant, sustained momentum shifts. - -## 4. Parameters - -- **RSI Period (`InpPeriodRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the RSI calculation (e.g., `PRICE_CLOSE`). -- **MA Period (`InpPeriodMA`):** The lookback period for the moving average that smooths the RSI line. Default is `14`. -- **MA Method (`InpMethod`):** The type of moving average to use for smoothing (SMA, EMA, SMMA, LWMA). Default is `MODE_SMA`. - -## 5. Usage and Interpretation - -- **Trend and Momentum Confirmation:** The primary use of the RSIMA is to provide a clearer view of momentum. When the RSIMA line is rising, it confirms bullish momentum; when it's falling, it confirms bearish momentum. -- **Signal Generation via Crossovers:** - - **RSI / RSIMA Crossover:** When the raw RSI line (green) crosses above its moving average (blue), it can be seen as a bullish signal. A cross below is a bearish signal. - - **Centerline Crossover:** A crossover of the RSIMA line above the 50 level indicates that bulls are in control. A crossover below 50 indicates bears are in control. -- **Smoothed Overbought/Oversold Signals:** The RSIMA line entering the overbought (above 70/80) or oversold (below 30/20) zones provides a more filtered, less frequent signal than the raw RSI. -- **Caution:** The smoothing process introduces lag. The RSIMA will always react slower than the raw RSI. This filtering is its main advantage, but traders should be aware of the delay. +# Moving Average of RSI (RSIMA) + +## 1. Summary (Introduction) + +The Moving Average of RSI, often referred to as RSIMA, is a technical indicator that smooths the standard Relative Strength Index (RSI) with a moving average. While the RSI is a powerful momentum oscillator, it can often be volatile, producing sharp movements and "noisy" signals. + +By applying a moving average to the RSI line, the RSIMA filters out this short-term noise, providing a smoother line that can make the underlying momentum trend easier to identify. It essentially acts as a signal line for the RSI, similar to how the %D line acts as a signal line for the Stochastic Oscillator. + +## 2. Mathematical Foundations and Calculation Logic + +The RSIMA is a two-stage indicator. It first calculates the standard RSI and then applies a moving average to the resulting RSI values. + +### Required Components + +- **RSI (Relative Strength Index):** The underlying momentum oscillator. +- **Moving Average (MA):** The smoothing mechanism applied to the RSI line. This can be a Simple (SMA), Exponential (EMA), Smoothed (SMMA), or Linear Weighted (LWMA) moving average. + +### Calculation Steps (Algorithm) + +1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). The RSI formula is based on Wilder's smoothing of average gains and average losses. + $\text{RS}_i = \frac{\text{Wilder's MA}(\text{Up Moves}, \text{RSI Period})_i}{\text{Wilder's MA}(\text{Down Moves}, \text{RSI Period})_i}$ + $\text{RSI}_i = 100 - \frac{100}{1 + \text{RS}_i}$ + +2. **Calculate the Moving Average of RSI:** Apply the selected moving average type with its specified period to the RSI data series calculated in the first step. + $\text{RSIMA}_i = \text{MA}(\text{RSI}, \text{MA Period})_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for indicators involving recursive calculations (like EMA or SMMA) to ensure maximum stability and prevent errors during timeframe changes or history loading. + +- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series. The handle's resources are properly managed and released in the `OnDeinit` function. + +- **Robust EMA/SMMA Initialization:** This is the most critical part of the implementation. Standard library functions for recursive MAs can be unstable in a full recalculation model. To guarantee stability, we take control of the initialization process: + + - For EMA and SMMA calculations, the **first value** of the moving average is calculated using a **manual Simple Moving Average (SMA)** on the preceding RSI data. + - This provides a stable, valid starting point for all subsequent recursive calculations, completely eliminating the risk of floating-point overflows that can occur with uninitialized data. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into two clear, sequential steps: + + 1. **Step 1:** The complete RSI data series is retrieved into the `BufferRawRSI` calculation buffer using `CopyBuffer`. + 2. **Step 2:** A single `for` loop calculates the moving average on the `BufferRawRSI` data, utilizing our robust `switch` block to handle the different MA types and their specific initialization needs. + +- **Heikin Ashi Variant (`RSI_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_close` values as the input for the initial RSI calculation. + - This results in a doubly-smoothed oscillator, ideal for traders who want to focus only on the most significant, sustained momentum shifts. + +## 4. Parameters + +- **RSI Period (`InpPeriodRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the RSI calculation (e.g., `PRICE_CLOSE`). +- **MA Period (`InpPeriodMA`):** The lookback period for the moving average that smooths the RSI line. Default is `14`. +- **MA Method (`InpMethod`):** The type of moving average to use for smoothing (SMA, EMA, SMMA, LWMA). Default is `MODE_SMA`. + +## 5. Usage and Interpretation + +- **Trend and Momentum Confirmation:** The primary use of the RSIMA is to provide a clearer view of momentum. When the RSIMA line is rising, it confirms bullish momentum; when it's falling, it confirms bearish momentum. +- **Signal Generation via Crossovers:** + - **RSI / RSIMA Crossover:** When the raw RSI line (green) crosses above its moving average (blue), it can be seen as a bullish signal. A cross below is a bearish signal. + - **Centerline Crossover:** A crossover of the RSIMA line above the 50 level indicates that bulls are in control. A crossover below 50 indicates bears are in control. +- **Smoothed Overbought/Oversold Signals:** The RSIMA line entering the overbought (above 70/80) or oversold (below 30/20) zones provides a more filtered, less frequent signal than the raw RSI. +- **Caution:** The smoothing process introduces lag. The RSIMA will always react slower than the raw RSI. This filtering is its main advantage, but traders should be aware of the delay. diff --git a/Indicators/MyIndicators/RSIMa.mq5 b/Indicators/MyIndicators/RSIMa.mq5 index bd1babc..a408e3a 100644 --- a/Indicators/MyIndicators/RSIMa.mq5 +++ b/Indicators/MyIndicators/RSIMa.mq5 @@ -1,167 +1,167 @@ -//+------------------------------------------------------------------+ -//| RSIMA.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Oscillator based on the Moving Average of a standard RSI." - -// --- Standard Includes --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 30.0 -#property indicator_level2 50.0 -#property indicator_level3 70.0 - -//--- Buffers and Plots --- -#property indicator_buffers 2 -#property indicator_plots 2 - -//--- Plot 1: RSIMA (Smoothed RSI) -#property indicator_label1 "RSIMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: RSI (Raw RSI) -#property indicator_label2 "RSI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrGreen -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpPeriodRSI = 14; // Period for RSI -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price for RSI -input int InpPeriodMA = 14; // Period for Moving Average -input ENUM_MA_METHOD InpMethod = MODE_SMA; // Method for Moving Average - -//--- Indicator Buffers --- -double BufferRSIMA[]; // Buffer for the smoothed RSI line (Plot 1) -double BufferRawRSI[]; // Buffer for the raw RSI values (Plot 2) - -//--- Global Variables --- -int g_ExtPeriodRSI; -int g_ExtPeriodMA; -int g_handle_rsi; // Handle for the standard RSI indicator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; - g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; - -//--- Map the buffers - SetIndexBuffer(0, BufferRSIMA, INDICATOR_DATA); - SetIndexBuffer(1, BufferRawRSI, INDICATOR_DATA); - -//--- Set buffers as non-timeseries for stable calculation - ArraySetAsSeries(BufferRSIMA, false); - ArraySetAsSeries(BufferRawRSI, false); - -//--- Set indicator display properties - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("RSIMA(%d, %d)", g_ExtPeriodRSI, g_ExtPeriodMA)); - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); - PlotIndexSetString(0, PLOT_LABEL, "RSIMA"); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI - 1); - PlotIndexSetString(1, PLOT_LABEL, "RSI"); - -//--- Create a handle to the standard iRSI indicator - g_handle_rsi = iRSI(_Symbol, _Period, g_ExtPeriodRSI, InpAppliedPrice); - if(g_handle_rsi == INVALID_HANDLE) - { - PrintFormat("Failed to create iRSI handle. Error %d", GetLastError()); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Release the indicator handle - IndicatorRelease(g_handle_rsi); - } - -//+------------------------------------------------------------------+ -//| Custom indicator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough data for the calculation - int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Get all available RSI values into our buffer - if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRawRSI) < rates_total) - { - Print("Error copying RSI buffer data."); - } - -//--- STEP 2: Calculate the Moving Average on the RSI buffer - for(int i = start_pos; i < rates_total; i++) - { - switch(InpMethod) - { - case MODE_EMA: - if(i == start_pos) // Initialization with manual SMA - { - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - sum += BufferRawRSI[i - j]; - BufferRSIMA[i] = sum / g_ExtPeriodMA; - } - else // Recursive calculation - { - double pr = 2.0 / (g_ExtPeriodMA + 1.0); - BufferRSIMA[i] = BufferRawRSI[i] * pr + BufferRSIMA[i-1] * (1.0 - pr); - } - break; - case MODE_SMMA: - if(i == start_pos) // Initialization with manual SMA - { - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - sum += BufferRawRSI[i - j]; - BufferRSIMA[i] = sum / g_ExtPeriodMA; - } - else // Recursive calculation - { - BufferRSIMA[i] = (BufferRSIMA[i-1] * (g_ExtPeriodMA - 1) + BufferRawRSI[i]) / g_ExtPeriodMA; - } - break; - case MODE_LWMA: - BufferRSIMA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferRawRSI); - break; - default: // MODE_SMA - BufferRSIMA[i] = SimpleMA(i, g_ExtPeriodMA, BufferRawRSI); - break; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| RSIMA.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Oscillator based on the Moving Average of a standard RSI." + +// --- Standard Includes --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 30.0 +#property indicator_level2 50.0 +#property indicator_level3 70.0 + +//--- Buffers and Plots --- +#property indicator_buffers 2 +#property indicator_plots 2 + +//--- Plot 1: RSIMA (Smoothed RSI) +#property indicator_label1 "RSIMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: RSI (Raw RSI) +#property indicator_label2 "RSI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrGreen +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpPeriodRSI = 14; // Period for RSI +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price for RSI +input int InpPeriodMA = 14; // Period for Moving Average +input ENUM_MA_METHOD InpMethod = MODE_SMA; // Method for Moving Average + +//--- Indicator Buffers --- +double BufferRSIMA[]; // Buffer for the smoothed RSI line (Plot 1) +double BufferRawRSI[]; // Buffer for the raw RSI values (Plot 2) + +//--- Global Variables --- +int g_ExtPeriodRSI; +int g_ExtPeriodMA; +int g_handle_rsi; // Handle for the standard RSI indicator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; + g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; + +//--- Map the buffers + SetIndexBuffer(0, BufferRSIMA, INDICATOR_DATA); + SetIndexBuffer(1, BufferRawRSI, INDICATOR_DATA); + +//--- Set buffers as non-timeseries for stable calculation + ArraySetAsSeries(BufferRSIMA, false); + ArraySetAsSeries(BufferRawRSI, false); + +//--- Set indicator display properties + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("RSIMA(%d, %d)", g_ExtPeriodRSI, g_ExtPeriodMA)); + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); + PlotIndexSetString(0, PLOT_LABEL, "RSIMA"); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI - 1); + PlotIndexSetString(1, PLOT_LABEL, "RSI"); + +//--- Create a handle to the standard iRSI indicator + g_handle_rsi = iRSI(_Symbol, _Period, g_ExtPeriodRSI, InpAppliedPrice); + if(g_handle_rsi == INVALID_HANDLE) + { + PrintFormat("Failed to create iRSI handle. Error %d", GetLastError()); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Release the indicator handle + IndicatorRelease(g_handle_rsi); + } + +//+------------------------------------------------------------------+ +//| Custom indicator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough data for the calculation + int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Get all available RSI values into our buffer + if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRawRSI) < rates_total) + { + Print("Error copying RSI buffer data."); + } + +//--- STEP 2: Calculate the Moving Average on the RSI buffer + for(int i = start_pos; i < rates_total; i++) + { + switch(InpMethod) + { + case MODE_EMA: + if(i == start_pos) // Initialization with manual SMA + { + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + sum += BufferRawRSI[i - j]; + BufferRSIMA[i] = sum / g_ExtPeriodMA; + } + else // Recursive calculation + { + double pr = 2.0 / (g_ExtPeriodMA + 1.0); + BufferRSIMA[i] = BufferRawRSI[i] * pr + BufferRSIMA[i-1] * (1.0 - pr); + } + break; + case MODE_SMMA: + if(i == start_pos) // Initialization with manual SMA + { + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + sum += BufferRawRSI[i - j]; + BufferRSIMA[i] = sum / g_ExtPeriodMA; + } + else // Recursive calculation + { + BufferRSIMA[i] = (BufferRSIMA[i-1] * (g_ExtPeriodMA - 1) + BufferRawRSI[i]) / g_ExtPeriodMA; + } + break; + case MODE_LWMA: + BufferRSIMA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferRawRSI); + break; + default: // MODE_SMA + BufferRSIMA[i] = SimpleMA(i, g_ExtPeriodMA, BufferRawRSI); + break; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/RSI_HeikinAshi.mq5 b/Indicators/MyIndicators/RSI_HeikinAshi.mq5 index 8d62c82..6d0c798 100644 --- a/Indicators/MyIndicators/RSI_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/RSI_HeikinAshi.mq5 @@ -1,234 +1,234 @@ -//+------------------------------------------------------------------+ -//| RSI_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx (Based on MetaQuotes RSI) | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "4.00" // Refactored for full recalculation and stability -#property description "RSI on Heikin Ashi prices, with a Moving Average." - -// --- Standard and Custom Includes --- -#include -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_minimum 0 -#property indicator_maximum 100 -#property indicator_level1 30.0 -#property indicator_level2 50.0 -#property indicator_level3 70.0 - -//--- Buffers and Plots --- -#property indicator_buffers 4 // 2 for plotting, 2 for RSI calculations -#property indicator_plots 2 - -//--- Plot 1: RSI MA line (smoothed) -#property indicator_label1 "HA_RSIMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: RSI line (raw) -#property indicator_label2 "HA_RSI" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrGreen -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpPeriodRSI = 14; // Period for RSI calculation -input int InpPeriodMA = 14; // Period for Moving Average smoothing -input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // Method for Moving Average smoothing - -//--- Indicator Buffers --- -double BufferHARSI_MA[]; // Plotted buffer for the smoothed RSI line -double BufferHARSI[]; // Plotted buffer for the raw Heikin Ashi RSI line -double BufferPos[]; // Calculation buffer for RSI's average gain -double BufferNeg[]; // Calculation buffer for RSI's average loss - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtPeriodRSI; -int g_ExtPeriodMA; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; - g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; - -//--- Map the buffers - SetIndexBuffer(0, BufferHARSI_MA, INDICATOR_DATA); - SetIndexBuffer(1, BufferHARSI, INDICATOR_DATA); - SetIndexBuffer(2, BufferPos, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferNeg, INDICATOR_CALCULATIONS); - -//--- Set all buffers as non-timeseries - ArraySetAsSeries(BufferHARSI_MA, false); - ArraySetAsSeries(BufferHARSI, false); - ArraySetAsSeries(BufferPos, false); - ArraySetAsSeries(BufferNeg, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); - PlotIndexSetString(0, PLOT_LABEL, "HA_RSIMA"); - PlotIndexSetString(1, PLOT_LABEL, "HA_RSI"); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_RSI(%d, %d)", g_ExtPeriodRSI, g_ExtPeriodMA)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Custom indicator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtPeriodRSI) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate RSI on HA Close in a single, robust loop - for(int i = 1; i < rates_total; i++) - { - double diff = ExtHaCloseBuffer[i] - ExtHaCloseBuffer[i-1]; - double positive_change = (diff > 0) ? diff : 0; - double negative_change = (diff < 0) ? -diff : 0; - - if(i == g_ExtPeriodRSI) - { - double sum_pos=0, sum_neg=0; - for(int j=1; j<=g_ExtPeriodRSI; j++) - { - double p_diff = ExtHaCloseBuffer[j] - ExtHaCloseBuffer[j-1]; - sum_pos += (p_diff > 0) ? p_diff : 0; - sum_neg += (p_diff < 0) ? -p_diff : 0; - } - BufferPos[i] = sum_pos / g_ExtPeriodRSI; - BufferNeg[i] = sum_neg / g_ExtPeriodRSI; - } - else - if(i > g_ExtPeriodRSI) - { - BufferPos[i] = (BufferPos[i-1] * (g_ExtPeriodRSI - 1) + positive_change) / g_ExtPeriodRSI; - BufferNeg[i] = (BufferNeg[i-1] * (g_ExtPeriodRSI - 1) + negative_change) / g_ExtPeriodRSI; - } - - if(i >= g_ExtPeriodRSI) - { - if(BufferNeg[i] > 0) - { - double rs = BufferPos[i] / BufferNeg[i]; - BufferHARSI[i] = 100.0 - (100.0 / (1.0 + rs)); - } - else - { - BufferHARSI[i] = 100.0; - } - } - } - -//--- STEP 3: Calculate Moving Average on the HA RSI buffer -// --- FIX: Correct starting position for the MA calculation --- - int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; - - for(int i = ma_start_pos; i < rates_total; i++) - { - switch(InpMethodMA) - { - case MODE_EMA: - if(i == ma_start_pos) - { - // Manual SMA for initialization on non-timeseries array - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - { - sum += BufferHARSI[i - j]; - } - BufferHARSI_MA[i] = sum / g_ExtPeriodMA; - } - else - { - double pr = 2.0 / (g_ExtPeriodMA + 1.0); - BufferHARSI_MA[i] = BufferHARSI[i] * pr + BufferHARSI_MA[i-1] * (1.0 - pr); - } - break; - case MODE_SMMA: - if(i == ma_start_pos) - { - // Manual SMA for initialization on non-timeseries array - double sum = 0; - for(int j = 0; j < g_ExtPeriodMA; j++) - { - sum += BufferHARSI[i - j]; - } - BufferHARSI_MA[i] = sum / g_ExtPeriodMA; - } - else - BufferHARSI_MA[i] = (BufferHARSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferHARSI[i]) / g_ExtPeriodMA; - break; - case MODE_LWMA: - BufferHARSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferHARSI); - break; - default: // MODE_SMA - BufferHARSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferHARSI); - break; - } - } - - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| RSI_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx (Based on MetaQuotes RSI) | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "4.00" // Refactored for full recalculation and stability +#property description "RSI on Heikin Ashi prices, with a Moving Average." + +// --- Standard and Custom Includes --- +#include +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_minimum 0 +#property indicator_maximum 100 +#property indicator_level1 30.0 +#property indicator_level2 50.0 +#property indicator_level3 70.0 + +//--- Buffers and Plots --- +#property indicator_buffers 4 // 2 for plotting, 2 for RSI calculations +#property indicator_plots 2 + +//--- Plot 1: RSI MA line (smoothed) +#property indicator_label1 "HA_RSIMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: RSI line (raw) +#property indicator_label2 "HA_RSI" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrGreen +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpPeriodRSI = 14; // Period for RSI calculation +input int InpPeriodMA = 14; // Period for Moving Average smoothing +input ENUM_MA_METHOD InpMethodMA = MODE_SMA; // Method for Moving Average smoothing + +//--- Indicator Buffers --- +double BufferHARSI_MA[]; // Plotted buffer for the smoothed RSI line +double BufferHARSI[]; // Plotted buffer for the raw Heikin Ashi RSI line +double BufferPos[]; // Calculation buffer for RSI's average gain +double BufferNeg[]; // Calculation buffer for RSI's average loss + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtPeriodRSI; +int g_ExtPeriodMA; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI; + g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA; + +//--- Map the buffers + SetIndexBuffer(0, BufferHARSI_MA, INDICATOR_DATA); + SetIndexBuffer(1, BufferHARSI, INDICATOR_DATA); + SetIndexBuffer(2, BufferPos, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferNeg, INDICATOR_CALCULATIONS); + +//--- Set all buffers as non-timeseries + ArraySetAsSeries(BufferHARSI_MA, false); + ArraySetAsSeries(BufferHARSI, false); + ArraySetAsSeries(BufferPos, false); + ArraySetAsSeries(BufferNeg, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodRSI + g_ExtPeriodMA - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtPeriodRSI); + PlotIndexSetString(0, PLOT_LABEL, "HA_RSIMA"); + PlotIndexSetString(1, PLOT_LABEL, "HA_RSI"); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_RSI(%d, %d)", g_ExtPeriodRSI, g_ExtPeriodMA)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Custom indicator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtPeriodRSI) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate RSI on HA Close in a single, robust loop + for(int i = 1; i < rates_total; i++) + { + double diff = ExtHaCloseBuffer[i] - ExtHaCloseBuffer[i-1]; + double positive_change = (diff > 0) ? diff : 0; + double negative_change = (diff < 0) ? -diff : 0; + + if(i == g_ExtPeriodRSI) + { + double sum_pos=0, sum_neg=0; + for(int j=1; j<=g_ExtPeriodRSI; j++) + { + double p_diff = ExtHaCloseBuffer[j] - ExtHaCloseBuffer[j-1]; + sum_pos += (p_diff > 0) ? p_diff : 0; + sum_neg += (p_diff < 0) ? -p_diff : 0; + } + BufferPos[i] = sum_pos / g_ExtPeriodRSI; + BufferNeg[i] = sum_neg / g_ExtPeriodRSI; + } + else + if(i > g_ExtPeriodRSI) + { + BufferPos[i] = (BufferPos[i-1] * (g_ExtPeriodRSI - 1) + positive_change) / g_ExtPeriodRSI; + BufferNeg[i] = (BufferNeg[i-1] * (g_ExtPeriodRSI - 1) + negative_change) / g_ExtPeriodRSI; + } + + if(i >= g_ExtPeriodRSI) + { + if(BufferNeg[i] > 0) + { + double rs = BufferPos[i] / BufferNeg[i]; + BufferHARSI[i] = 100.0 - (100.0 / (1.0 + rs)); + } + else + { + BufferHARSI[i] = 100.0; + } + } + } + +//--- STEP 3: Calculate Moving Average on the HA RSI buffer +// --- FIX: Correct starting position for the MA calculation --- + int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; + + for(int i = ma_start_pos; i < rates_total; i++) + { + switch(InpMethodMA) + { + case MODE_EMA: + if(i == ma_start_pos) + { + // Manual SMA for initialization on non-timeseries array + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + { + sum += BufferHARSI[i - j]; + } + BufferHARSI_MA[i] = sum / g_ExtPeriodMA; + } + else + { + double pr = 2.0 / (g_ExtPeriodMA + 1.0); + BufferHARSI_MA[i] = BufferHARSI[i] * pr + BufferHARSI_MA[i-1] * (1.0 - pr); + } + break; + case MODE_SMMA: + if(i == ma_start_pos) + { + // Manual SMA for initialization on non-timeseries array + double sum = 0; + for(int j = 0; j < g_ExtPeriodMA; j++) + { + sum += BufferHARSI[i - j]; + } + BufferHARSI_MA[i] = sum / g_ExtPeriodMA; + } + else + BufferHARSI_MA[i] = (BufferHARSI_MA[i-1] * (g_ExtPeriodMA - 1) + BufferHARSI[i]) / g_ExtPeriodMA; + break; + case MODE_LWMA: + BufferHARSI_MA[i] = LinearWeightedMA(i, g_ExtPeriodMA, BufferHARSI); + break; + default: // MODE_SMA + BufferHARSI_MA[i] = SimpleMA(i, g_ExtPeriodMA, BufferHARSI); + break; + } + } + + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/SMI.md b/Indicators/MyIndicators/SMI.md index 8f76674..ffffcd2 100644 --- a/Indicators/MyIndicators/SMI.md +++ b/Indicators/MyIndicators/SMI.md @@ -1,71 +1,71 @@ -# Stochastic Momentum Index (SMI) - -## 1. Summary (Introduction) - -The Stochastic Momentum Index (SMI) was developed by William Blau. Unlike the standard Stochastic Oscillator which measures the relationship between the closing price and its high-low range, the SMI measures the relationship between the closing price and the _midpoint_ of that range. - -The result is a smoother oscillator that fluctuates around a zero line, providing clearer signals and minimizing the erratic behavior often seen in the standard Stochastic. It is designed to be a more reliable indicator of momentum, less prone to false signals from minor price volatility. - -## 2. Mathematical Foundations and Calculation Logic - -The SMI is a complex indicator involving multiple layers of smoothing, typically using Exponential Moving Averages (EMAs). - -### Required Components - -- **%K Period:** The lookback period for finding the highest high and lowest low. -- **%D Period:** The period for the double EMA smoothing. -- **Signal Period:** The period for the final EMA smoothing that creates the signal line. - -### Calculation Steps (Algorithm) - -1. **Find the Price Range:** For each bar, determine the highest high and lowest low over the `%K Period`. - $\text{Highest High}_i = \text{Max}(\text{High}, \text{\%K Period})_i$ - $\text{Lowest Low}_i = \text{Min}(\text{Low}, \text{\%K Period})_i$ - -2. **Calculate the Relative Distance:** Determine the distance of the current close from the midpoint of the high-low range. - $\text{Range}_i = \text{Highest High}_i - \text{Lowest Low}_i$ - $\text{Relative Distance}_i = \text{Close}_i - \frac{\text{Highest High}_i + \text{Lowest Low}_i}{2}$ - -3. **First EMA Smoothing:** Apply an EMA with the `%D Period` to both the `Relative Distance` and the `Range`. - $\text{EMA(Relative)}_i = \text{EMA}(\text{Relative Distance}, \text{\%D Period})_i$ - $\text{EMA(Range)}_i = \text{EMA}(\text{Range}, \text{\%D Period})_i$ - -4. **Second EMA Smoothing:** Apply another EMA with the `%D Period` to the results of the first smoothing. This double-smoothing is a key feature of the SMI. - $\text{EMA2(Relative)}_i = \text{EMA}(\text{EMA(Relative)}, \text{\%D Period})_i$ - $\text{EMA2(Range)}_i = \text{EMA}(\text{EMA(Range)}, \text{\%D Period})_i$ - -5. **Calculate the SMI Value:** The final SMI is calculated as a percentage. The division by `Range / 2` scales the result to oscillate primarily between +100 and -100. - $\text{SMI}_i = 100 \times \frac{\text{EMA2(Relative)}_i}{\text{EMA2(Range)}_i / 2}$ - -6. **Calculate the Signal Line:** The signal line is an EMA of the SMI line itself, using the `Signal Period`. - $\text{Signal}_i = \text{EMA}(\text{SMI}, \text{Signal Period})_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored to be highly robust, clear, and efficient, especially considering the multiple layers of recursive EMA calculations. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a complex, multi-stage indicator like the SMI, this is the most reliable method to prevent calculation errors and ensure stability. - -- **Robust EMA Initialization:** Each recursive EMA calculation step is carefully initialized to prevent floating-point overflows. For the second EMA pass and the final signal line, the first value is calculated using a **manual Simple Moving Average (SMA)** on the preceding data. This provides a stable starting point for the subsequent recursive calculations. - -- **Optimized Calculation Flow:** The `OnCalculate` function is structured into clear, sequential steps. After an initial loop to calculate the raw price ranges, a single, efficient `for` loop handles all subsequent smoothing and final calculations. This integrated approach is more efficient than using multiple separate loops that would iterate over the entire dataset repeatedly. - -- **Heikin Ashi Variant (`SMI_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. - - This results in an even smoother oscillator, as the input data itself is already filtered. This version is ideal for traders who want to focus on the most significant momentum shifts and filter out market noise. - -## 4. Parameters - -- **%K Length (`InpLengthK`):** The lookback period for finding the highest high and lowest low. Default is `10`. -- **%D Length (`InpLengthD`):** The period used for the double EMA smoothing of the price ranges. Default is `3`. -- **EMA Length (`InpLengthEMA`):** The smoothing period for the final signal line. Default is `3`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The SMI typically uses +40 as the overbought level and -40 as the oversold level. A move above +40 suggests strong bullish momentum that may be nearing exhaustion, while a move below -40 suggests strong bearish momentum. -- **Crossovers:** - - **SMI / Signal Line Crossover:** When the SMI line (blue) crosses above its signal line (orange), it can be considered a bullish signal. When it crosses below, it's a bearish signal. - - **Zero Line Crossover:** A crossover of the SMI line above the zero line indicates that bullish momentum is taking control. A crossover below zero indicates bearish momentum is dominant. -- **Divergence:** Look for divergences between the SMI and the price. A bearish divergence (higher price highs, lower SMI highs) can signal a potential top, while a bullish divergence (lower price lows, higher SMI lows) can signal a potential bottom. -- **Caution:** While smoother than a standard Stochastic, the SMI is still a momentum oscillator and can give false signals in choppy markets. It is best used for confirmation with other forms of analysis. +# Stochastic Momentum Index (SMI) + +## 1. Summary (Introduction) + +The Stochastic Momentum Index (SMI) was developed by William Blau. Unlike the standard Stochastic Oscillator which measures the relationship between the closing price and its high-low range, the SMI measures the relationship between the closing price and the _midpoint_ of that range. + +The result is a smoother oscillator that fluctuates around a zero line, providing clearer signals and minimizing the erratic behavior often seen in the standard Stochastic. It is designed to be a more reliable indicator of momentum, less prone to false signals from minor price volatility. + +## 2. Mathematical Foundations and Calculation Logic + +The SMI is a complex indicator involving multiple layers of smoothing, typically using Exponential Moving Averages (EMAs). + +### Required Components + +- **%K Period:** The lookback period for finding the highest high and lowest low. +- **%D Period:** The period for the double EMA smoothing. +- **Signal Period:** The period for the final EMA smoothing that creates the signal line. + +### Calculation Steps (Algorithm) + +1. **Find the Price Range:** For each bar, determine the highest high and lowest low over the `%K Period`. + $\text{Highest High}_i = \text{Max}(\text{High}, \text{\%K Period})_i$ + $\text{Lowest Low}_i = \text{Min}(\text{Low}, \text{\%K Period})_i$ + +2. **Calculate the Relative Distance:** Determine the distance of the current close from the midpoint of the high-low range. + $\text{Range}_i = \text{Highest High}_i - \text{Lowest Low}_i$ + $\text{Relative Distance}_i = \text{Close}_i - \frac{\text{Highest High}_i + \text{Lowest Low}_i}{2}$ + +3. **First EMA Smoothing:** Apply an EMA with the `%D Period` to both the `Relative Distance` and the `Range`. + $\text{EMA(Relative)}_i = \text{EMA}(\text{Relative Distance}, \text{\%D Period})_i$ + $\text{EMA(Range)}_i = \text{EMA}(\text{Range}, \text{\%D Period})_i$ + +4. **Second EMA Smoothing:** Apply another EMA with the `%D Period` to the results of the first smoothing. This double-smoothing is a key feature of the SMI. + $\text{EMA2(Relative)}_i = \text{EMA}(\text{EMA(Relative)}, \text{\%D Period})_i$ + $\text{EMA2(Range)}_i = \text{EMA}(\text{EMA(Range)}, \text{\%D Period})_i$ + +5. **Calculate the SMI Value:** The final SMI is calculated as a percentage. The division by `Range / 2` scales the result to oscillate primarily between +100 and -100. + $\text{SMI}_i = 100 \times \frac{\text{EMA2(Relative)}_i}{\text{EMA2(Range)}_i / 2}$ + +6. **Calculate the Signal Line:** The signal line is an EMA of the SMI line itself, using the `Signal Period`. + $\text{Signal}_i = \text{EMA}(\text{SMI}, \text{Signal Period})_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored to be highly robust, clear, and efficient, especially considering the multiple layers of recursive EMA calculations. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a complex, multi-stage indicator like the SMI, this is the most reliable method to prevent calculation errors and ensure stability. + +- **Robust EMA Initialization:** Each recursive EMA calculation step is carefully initialized to prevent floating-point overflows. For the second EMA pass and the final signal line, the first value is calculated using a **manual Simple Moving Average (SMA)** on the preceding data. This provides a stable starting point for the subsequent recursive calculations. + +- **Optimized Calculation Flow:** The `OnCalculate` function is structured into clear, sequential steps. After an initial loop to calculate the raw price ranges, a single, efficient `for` loop handles all subsequent smoothing and final calculations. This integrated approach is more efficient than using multiple separate loops that would iterate over the entire dataset repeatedly. + +- **Heikin Ashi Variant (`SMI_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. + - This results in an even smoother oscillator, as the input data itself is already filtered. This version is ideal for traders who want to focus on the most significant momentum shifts and filter out market noise. + +## 4. Parameters + +- **%K Length (`InpLengthK`):** The lookback period for finding the highest high and lowest low. Default is `10`. +- **%D Length (`InpLengthD`):** The period used for the double EMA smoothing of the price ranges. Default is `3`. +- **EMA Length (`InpLengthEMA`):** The smoothing period for the final signal line. Default is `3`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation (e.g., `PRICE_CLOSE`). + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The SMI typically uses +40 as the overbought level and -40 as the oversold level. A move above +40 suggests strong bullish momentum that may be nearing exhaustion, while a move below -40 suggests strong bearish momentum. +- **Crossovers:** + - **SMI / Signal Line Crossover:** When the SMI line (blue) crosses above its signal line (orange), it can be considered a bullish signal. When it crosses below, it's a bearish signal. + - **Zero Line Crossover:** A crossover of the SMI line above the zero line indicates that bullish momentum is taking control. A crossover below zero indicates bearish momentum is dominant. +- **Divergence:** Look for divergences between the SMI and the price. A bearish divergence (higher price highs, lower SMI highs) can signal a potential top, while a bullish divergence (lower price lows, higher SMI lows) can signal a potential bottom. +- **Caution:** While smoother than a standard Stochastic, the SMI is still a momentum oscillator and can give false signals in choppy markets. It is best used for confirmation with other forms of analysis. diff --git a/Indicators/MyIndicators/SMI.mq5 b/Indicators/MyIndicators/SMI.mq5 index c6b866c..d41a60b 100644 --- a/Indicators/MyIndicators/SMI.mq5 +++ b/Indicators/MyIndicators/SMI.mq5 @@ -1,226 +1,226 @@ -//+------------------------------------------------------------------+ -//| SMI.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Stochastic Momentum Index (SMI)" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 8 // SMI, Signal, and 6 calculation buffers -#property indicator_plots 2 -#property indicator_level1 40.0 -#property indicator_level2 0.0 -#property indicator_level3 -40.0 -#property indicator_levelstyle STYLE_DOT - -//--- Plot 1: SMI line -#property indicator_label1 "SMI" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: Signal line (EMA of SMI) -#property indicator_label2 "Signal" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthK = 10; // %K Length -input int InpLengthD = 3; // %D Length (for double smoothing) -input int InpLengthEMA = 3; // EMA Length (for signal line) -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied Price - -//--- Indicator Buffers --- -double BufferSMI[]; -double BufferSignal[]; -double BufferHighestLowestRange[]; -double BufferRelativeRange[]; -double BufferEma_Relative[]; -double BufferEma_Range[]; -double BufferEmaEma_Relative[]; -double BufferEmaEma_Range[]; - -//--- Global Variables --- -int g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store inputs - g_ExtLengthK = (InpLengthK < 1) ? 1 : InpLengthK; - g_ExtLengthD = (InpLengthD < 1) ? 1 : InpLengthD; - g_ExtLengthEMA = (InpLengthEMA < 1) ? 1 : InpLengthEMA; - -//--- Map the buffers - SetIndexBuffer(0, BufferSMI, INDICATOR_DATA); - SetIndexBuffer(1, BufferSignal, INDICATOR_DATA); - SetIndexBuffer(2, BufferHighestLowestRange, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRelativeRange, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferEma_Relative, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferEma_Range, INDICATOR_CALCULATIONS); - SetIndexBuffer(6, BufferEmaEma_Relative, INDICATOR_CALCULATIONS); - SetIndexBuffer(7, BufferEmaEma_Range, INDICATOR_CALCULATIONS); - -//--- Set all buffers to non-timeseries - ArraySetAsSeries(BufferSMI, false); - ArraySetAsSeries(BufferSignal, false); - ArraySetAsSeries(BufferHighestLowestRange, false); - ArraySetAsSeries(BufferRelativeRange, false); - ArraySetAsSeries(BufferEma_Relative, false); - ArraySetAsSeries(BufferEma_Range, false); - ArraySetAsSeries(BufferEmaEma_Relative, false); - ArraySetAsSeries(BufferEmaEma_Range, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - int smi_draw_begin = g_ExtLengthK + g_ExtLengthD + g_ExtLengthD - 3; // K + D + (D-1) for 2nd EMA - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, smi_draw_begin); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, smi_draw_begin + g_ExtLengthEMA - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SMI(%d,%d,%d)", g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Stochastic Momentum Index calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthK + g_ExtLengthD + g_ExtLengthD + g_ExtLengthEMA - 4; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Calculate Highest, Lowest, and Ranges - for(int i = g_ExtLengthK - 1; i < rates_total; i++) - { - double highest_high = Highest(high, g_ExtLengthK, i); - double lowest_low = Lowest(low, g_ExtLengthK, i); - BufferHighestLowestRange[i] = highest_high - lowest_low; - BufferRelativeRange[i] = close[i] - (highest_high + lowest_low) / 2.0; - } - -//--- STEP 2-6: Calculate all smoothed values and final SMI in a single loop - double pr_d = 2.0 / (g_ExtLengthD + 1.0); - double pr_ema = 2.0 / (g_ExtLengthEMA + 1.0); - - int ema1_start = g_ExtLengthK + g_ExtLengthD - 2; - int ema2_start = ema1_start + g_ExtLengthD - 1; - int signal_start = ema2_start + g_ExtLengthEMA - 1; - - for(int i = g_ExtLengthK - 1; i < rates_total; i++) - { - // --- 1st EMA Smoothing --- - if(i == g_ExtLengthK - 1) // Initialization - { - BufferEma_Relative[i] = BufferRelativeRange[i]; - BufferEma_Range[i] = BufferHighestLowestRange[i]; - } - else // Recursive - { - BufferEma_Relative[i] = BufferRelativeRange[i] * pr_d + BufferEma_Relative[i-1] * (1.0 - pr_d); - BufferEma_Range[i] = BufferHighestLowestRange[i] * pr_d + BufferEma_Range[i-1] * (1.0 - pr_d); - } - - // --- 2nd EMA Smoothing --- - if(i == ema2_start) // Initialization with manual SMA - { - double sum_rel=0, sum_ran=0; - for(int j=0; j ema2_start) // Recursive - { - BufferEmaEma_Relative[i] = BufferEma_Relative[i] * pr_d + BufferEmaEma_Relative[i-1] * (1.0 - pr_d); - BufferEmaEma_Range[i] = BufferEma_Range[i] * pr_d + BufferEmaEma_Range[i-1] * (1.0 - pr_d); - } - - // --- Final SMI Value --- - if(i >= ema2_start) - { - if(BufferEmaEma_Range[i] != 0) - BufferSMI[i] = 100 * (BufferEmaEma_Relative[i] / (BufferEmaEma_Range[i] / 2.0)); - else - BufferSMI[i] = 0; - } - - // --- Signal Line --- - if(i == signal_start) // Initialization with manual SMA - { - double sum_smi=0; - for(int j=0; j signal_start) // Recursive - { - BufferSignal[i] = BufferSMI[i] * pr_ema + BufferSignal[i-1] * (1.0 - pr_ema); - } - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| SMI.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Stochastic Momentum Index (SMI)" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 8 // SMI, Signal, and 6 calculation buffers +#property indicator_plots 2 +#property indicator_level1 40.0 +#property indicator_level2 0.0 +#property indicator_level3 -40.0 +#property indicator_levelstyle STYLE_DOT + +//--- Plot 1: SMI line +#property indicator_label1 "SMI" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: Signal line (EMA of SMI) +#property indicator_label2 "Signal" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthK = 10; // %K Length +input int InpLengthD = 3; // %D Length (for double smoothing) +input int InpLengthEMA = 3; // EMA Length (for signal line) +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied Price + +//--- Indicator Buffers --- +double BufferSMI[]; +double BufferSignal[]; +double BufferHighestLowestRange[]; +double BufferRelativeRange[]; +double BufferEma_Relative[]; +double BufferEma_Range[]; +double BufferEmaEma_Relative[]; +double BufferEmaEma_Range[]; + +//--- Global Variables --- +int g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store inputs + g_ExtLengthK = (InpLengthK < 1) ? 1 : InpLengthK; + g_ExtLengthD = (InpLengthD < 1) ? 1 : InpLengthD; + g_ExtLengthEMA = (InpLengthEMA < 1) ? 1 : InpLengthEMA; + +//--- Map the buffers + SetIndexBuffer(0, BufferSMI, INDICATOR_DATA); + SetIndexBuffer(1, BufferSignal, INDICATOR_DATA); + SetIndexBuffer(2, BufferHighestLowestRange, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRelativeRange, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferEma_Relative, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferEma_Range, INDICATOR_CALCULATIONS); + SetIndexBuffer(6, BufferEmaEma_Relative, INDICATOR_CALCULATIONS); + SetIndexBuffer(7, BufferEmaEma_Range, INDICATOR_CALCULATIONS); + +//--- Set all buffers to non-timeseries + ArraySetAsSeries(BufferSMI, false); + ArraySetAsSeries(BufferSignal, false); + ArraySetAsSeries(BufferHighestLowestRange, false); + ArraySetAsSeries(BufferRelativeRange, false); + ArraySetAsSeries(BufferEma_Relative, false); + ArraySetAsSeries(BufferEma_Range, false); + ArraySetAsSeries(BufferEmaEma_Relative, false); + ArraySetAsSeries(BufferEmaEma_Range, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + int smi_draw_begin = g_ExtLengthK + g_ExtLengthD + g_ExtLengthD - 3; // K + D + (D-1) for 2nd EMA + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, smi_draw_begin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, smi_draw_begin + g_ExtLengthEMA - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SMI(%d,%d,%d)", g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Stochastic Momentum Index calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthK + g_ExtLengthD + g_ExtLengthD + g_ExtLengthEMA - 4; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Calculate Highest, Lowest, and Ranges + for(int i = g_ExtLengthK - 1; i < rates_total; i++) + { + double highest_high = Highest(high, g_ExtLengthK, i); + double lowest_low = Lowest(low, g_ExtLengthK, i); + BufferHighestLowestRange[i] = highest_high - lowest_low; + BufferRelativeRange[i] = close[i] - (highest_high + lowest_low) / 2.0; + } + +//--- STEP 2-6: Calculate all smoothed values and final SMI in a single loop + double pr_d = 2.0 / (g_ExtLengthD + 1.0); + double pr_ema = 2.0 / (g_ExtLengthEMA + 1.0); + + int ema1_start = g_ExtLengthK + g_ExtLengthD - 2; + int ema2_start = ema1_start + g_ExtLengthD - 1; + int signal_start = ema2_start + g_ExtLengthEMA - 1; + + for(int i = g_ExtLengthK - 1; i < rates_total; i++) + { + // --- 1st EMA Smoothing --- + if(i == g_ExtLengthK - 1) // Initialization + { + BufferEma_Relative[i] = BufferRelativeRange[i]; + BufferEma_Range[i] = BufferHighestLowestRange[i]; + } + else // Recursive + { + BufferEma_Relative[i] = BufferRelativeRange[i] * pr_d + BufferEma_Relative[i-1] * (1.0 - pr_d); + BufferEma_Range[i] = BufferHighestLowestRange[i] * pr_d + BufferEma_Range[i-1] * (1.0 - pr_d); + } + + // --- 2nd EMA Smoothing --- + if(i == ema2_start) // Initialization with manual SMA + { + double sum_rel=0, sum_ran=0; + for(int j=0; j ema2_start) // Recursive + { + BufferEmaEma_Relative[i] = BufferEma_Relative[i] * pr_d + BufferEmaEma_Relative[i-1] * (1.0 - pr_d); + BufferEmaEma_Range[i] = BufferEma_Range[i] * pr_d + BufferEmaEma_Range[i-1] * (1.0 - pr_d); + } + + // --- Final SMI Value --- + if(i >= ema2_start) + { + if(BufferEmaEma_Range[i] != 0) + BufferSMI[i] = 100 * (BufferEmaEma_Relative[i] / (BufferEmaEma_Range[i] / 2.0)); + else + BufferSMI[i] = 0; + } + + // --- Signal Line --- + if(i == signal_start) // Initialization with manual SMA + { + double sum_smi=0; + for(int j=0; j signal_start) // Recursive + { + BufferSignal[i] = BufferSMI[i] * pr_ema + BufferSignal[i-1] * (1.0 - pr_ema); + } + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/SMI_HeikinAshi.mq5 b/Indicators/MyIndicators/SMI_HeikinAshi.mq5 index a8296ab..f65684d 100644 --- a/Indicators/MyIndicators/SMI_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/SMI_HeikinAshi.mq5 @@ -1,268 +1,268 @@ -//+------------------------------------------------------------------+ -//| SMI_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Stochastic Momentum Index (SMI) on Heikin Ashi data" - -// --- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 40.0 -#property indicator_level2 0.0 -#property indicator_level3 -40.0 -#property indicator_levelstyle STYLE_DOT - -//--- Buffers and Plots --- -#property indicator_buffers 8 // SMI, Signal, and 6 calculation buffers -#property indicator_plots 2 - -//--- Plot 1: SMI line -#property indicator_label1 "HA_SMI" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: Signal line (EMA of SMI) -#property indicator_label2 "HA_Signal" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthK = 10; // %K Length -input int InpLengthD = 3; // %D Length (for double smoothing) -input int InpLengthEMA = 3; // EMA Length (for signal line) - -//--- Indicator Buffers --- -double BufferSMI[]; -double BufferSignal[]; -double BufferHighestLowestRange[]; -double BufferRelativeRange[]; -double BufferEma_Relative[]; -double BufferEma_Range[]; -double BufferEmaEma_Relative[]; -double BufferEmaEma_Range[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store inputs - g_ExtLengthK = (InpLengthK < 1) ? 1 : InpLengthK; - g_ExtLengthD = (InpLengthD < 1) ? 1 : InpLengthD; - g_ExtLengthEMA = (InpLengthEMA < 1) ? 1 : InpLengthEMA; - -//--- Map the buffers - SetIndexBuffer(0, BufferSMI, INDICATOR_DATA); - SetIndexBuffer(1, BufferSignal, INDICATOR_DATA); - SetIndexBuffer(2, BufferHighestLowestRange, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRelativeRange, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferEma_Relative, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferEma_Range, INDICATOR_CALCULATIONS); - SetIndexBuffer(6, BufferEmaEma_Relative, INDICATOR_CALCULATIONS); - SetIndexBuffer(7, BufferEmaEma_Range, INDICATOR_CALCULATIONS); - -//--- Set all buffers to non-timeseries - ArraySetAsSeries(BufferSMI, false); - ArraySetAsSeries(BufferSignal, false); - ArraySetAsSeries(BufferHighestLowestRange, false); - ArraySetAsSeries(BufferRelativeRange, false); - ArraySetAsSeries(BufferEma_Relative, false); - ArraySetAsSeries(BufferEma_Range, false); - ArraySetAsSeries(BufferEmaEma_Relative, false); - ArraySetAsSeries(BufferEmaEma_Range, false); - -//--- Set indicator properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - int smi_draw_begin = g_ExtLengthK + g_ExtLengthD - 2; - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, smi_draw_begin); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, smi_draw_begin + g_ExtLengthEMA - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_SMI(%d,%d,%d)", g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Stochastic Momentum Index calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthK + g_ExtLengthD + g_ExtLengthEMA - 2; - if(rates_total <= start_pos) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate Highest, Lowest, and Ranges - for(int i = g_ExtLengthK - 1; i < rates_total; i++) - { - double highest_high = Highest(ExtHaHighBuffer, g_ExtLengthK, i); - double lowest_low = Lowest(ExtHaLowBuffer, g_ExtLengthK, i); - BufferHighestLowestRange[i] = highest_high - lowest_low; - BufferRelativeRange[i] = ExtHaCloseBuffer[i] - (highest_high + lowest_low) / 2.0; - } - -//--- STEP 3-7: Calculate all smoothed values and final SMI in a single loop - double pr_d = 2.0 / (g_ExtLengthD + 1.0); - double pr_ema = 2.0 / (g_ExtLengthEMA + 1.0); - - int ema1_start = g_ExtLengthK + g_ExtLengthD - 2; - int ema2_start = ema1_start + g_ExtLengthD - 1; - int signal_start = ema2_start + g_ExtLengthEMA - 1; - - for(int i = g_ExtLengthK - 1; i < rates_total; i++) - { - // --- 1st EMA Smoothing --- - if(i == g_ExtLengthK - 1) // Initialization - { - BufferEma_Relative[i] = BufferRelativeRange[i]; - BufferEma_Range[i] = BufferHighestLowestRange[i]; - } - else // Recursive - { - BufferEma_Relative[i] = BufferRelativeRange[i] * pr_d + BufferEma_Relative[i-1] * (1.0 - pr_d); - BufferEma_Range[i] = BufferHighestLowestRange[i] * pr_d + BufferEma_Range[i-1] * (1.0 - pr_d); - } - - // --- 2nd EMA Smoothing --- - if(i == ema1_start) // Initialization with manual SMA - { - double sum_rel=0, sum_ran=0; - for(int j=0; j ema1_start) // Recursive - { - BufferEmaEma_Relative[i] = BufferEma_Relative[i] * pr_d + BufferEmaEma_Relative[i-1] * (1.0 - pr_d); - BufferEmaEma_Range[i] = BufferEma_Range[i] * pr_d + BufferEmaEma_Range[i-1] * (1.0 - pr_d); - } - - // --- Final SMI Value --- - if(i >= ema1_start) - { - if(BufferEmaEma_Range[i] != 0) - BufferSMI[i] = 100 * (BufferEmaEma_Relative[i] / (BufferEmaEma_Range[i] / 2.0)); - else - BufferSMI[i] = 0; - } - - // --- Signal Line --- - if(i == signal_start) // Initialization with manual SMA - { - double sum_smi=0; - for(int j=0; j signal_start) // Recursive - { - BufferSignal[i] = BufferSMI[i] * pr_ema + BufferSignal[i-1] * (1.0 - pr_ema); - } - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| SMI_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Stochastic Momentum Index (SMI) on Heikin Ashi data" + +// --- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 40.0 +#property indicator_level2 0.0 +#property indicator_level3 -40.0 +#property indicator_levelstyle STYLE_DOT + +//--- Buffers and Plots --- +#property indicator_buffers 8 // SMI, Signal, and 6 calculation buffers +#property indicator_plots 2 + +//--- Plot 1: SMI line +#property indicator_label1 "HA_SMI" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: Signal line (EMA of SMI) +#property indicator_label2 "HA_Signal" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthK = 10; // %K Length +input int InpLengthD = 3; // %D Length (for double smoothing) +input int InpLengthEMA = 3; // EMA Length (for signal line) + +//--- Indicator Buffers --- +double BufferSMI[]; +double BufferSignal[]; +double BufferHighestLowestRange[]; +double BufferRelativeRange[]; +double BufferEma_Relative[]; +double BufferEma_Range[]; +double BufferEmaEma_Relative[]; +double BufferEmaEma_Range[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store inputs + g_ExtLengthK = (InpLengthK < 1) ? 1 : InpLengthK; + g_ExtLengthD = (InpLengthD < 1) ? 1 : InpLengthD; + g_ExtLengthEMA = (InpLengthEMA < 1) ? 1 : InpLengthEMA; + +//--- Map the buffers + SetIndexBuffer(0, BufferSMI, INDICATOR_DATA); + SetIndexBuffer(1, BufferSignal, INDICATOR_DATA); + SetIndexBuffer(2, BufferHighestLowestRange, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRelativeRange, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferEma_Relative, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferEma_Range, INDICATOR_CALCULATIONS); + SetIndexBuffer(6, BufferEmaEma_Relative, INDICATOR_CALCULATIONS); + SetIndexBuffer(7, BufferEmaEma_Range, INDICATOR_CALCULATIONS); + +//--- Set all buffers to non-timeseries + ArraySetAsSeries(BufferSMI, false); + ArraySetAsSeries(BufferSignal, false); + ArraySetAsSeries(BufferHighestLowestRange, false); + ArraySetAsSeries(BufferRelativeRange, false); + ArraySetAsSeries(BufferEma_Relative, false); + ArraySetAsSeries(BufferEma_Range, false); + ArraySetAsSeries(BufferEmaEma_Relative, false); + ArraySetAsSeries(BufferEmaEma_Range, false); + +//--- Set indicator properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + int smi_draw_begin = g_ExtLengthK + g_ExtLengthD - 2; + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, smi_draw_begin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, smi_draw_begin + g_ExtLengthEMA - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_SMI(%d,%d,%d)", g_ExtLengthK, g_ExtLengthD, g_ExtLengthEMA)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Stochastic Momentum Index calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthK + g_ExtLengthD + g_ExtLengthEMA - 2; + if(rates_total <= start_pos) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate Highest, Lowest, and Ranges + for(int i = g_ExtLengthK - 1; i < rates_total; i++) + { + double highest_high = Highest(ExtHaHighBuffer, g_ExtLengthK, i); + double lowest_low = Lowest(ExtHaLowBuffer, g_ExtLengthK, i); + BufferHighestLowestRange[i] = highest_high - lowest_low; + BufferRelativeRange[i] = ExtHaCloseBuffer[i] - (highest_high + lowest_low) / 2.0; + } + +//--- STEP 3-7: Calculate all smoothed values and final SMI in a single loop + double pr_d = 2.0 / (g_ExtLengthD + 1.0); + double pr_ema = 2.0 / (g_ExtLengthEMA + 1.0); + + int ema1_start = g_ExtLengthK + g_ExtLengthD - 2; + int ema2_start = ema1_start + g_ExtLengthD - 1; + int signal_start = ema2_start + g_ExtLengthEMA - 1; + + for(int i = g_ExtLengthK - 1; i < rates_total; i++) + { + // --- 1st EMA Smoothing --- + if(i == g_ExtLengthK - 1) // Initialization + { + BufferEma_Relative[i] = BufferRelativeRange[i]; + BufferEma_Range[i] = BufferHighestLowestRange[i]; + } + else // Recursive + { + BufferEma_Relative[i] = BufferRelativeRange[i] * pr_d + BufferEma_Relative[i-1] * (1.0 - pr_d); + BufferEma_Range[i] = BufferHighestLowestRange[i] * pr_d + BufferEma_Range[i-1] * (1.0 - pr_d); + } + + // --- 2nd EMA Smoothing --- + if(i == ema1_start) // Initialization with manual SMA + { + double sum_rel=0, sum_ran=0; + for(int j=0; j ema1_start) // Recursive + { + BufferEmaEma_Relative[i] = BufferEma_Relative[i] * pr_d + BufferEmaEma_Relative[i-1] * (1.0 - pr_d); + BufferEmaEma_Range[i] = BufferEma_Range[i] * pr_d + BufferEmaEma_Range[i-1] * (1.0 - pr_d); + } + + // --- Final SMI Value --- + if(i >= ema1_start) + { + if(BufferEmaEma_Range[i] != 0) + BufferSMI[i] = 100 * (BufferEmaEma_Relative[i] / (BufferEmaEma_Range[i] / 2.0)); + else + BufferSMI[i] = 0; + } + + // --- Signal Line --- + if(i == signal_start) // Initialization with manual SMA + { + double sum_smi=0; + for(int j=0; j signal_start) // Recursive + { + BufferSignal[i] = BufferSMI[i] * pr_ema + BufferSignal[i-1] * (1.0 - pr_ema); + } + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochRSI_Fast.md b/Indicators/MyIndicators/StochRSI_Fast.md index 2643d15..33b77ff 100644 --- a/Indicators/MyIndicators/StochRSI_Fast.md +++ b/Indicators/MyIndicators/StochRSI_Fast.md @@ -1,63 +1,63 @@ -# Fast Stochastic RSI (StochRSI) Indicator - -## 1. Summary (Introduction) - -The Stochastic RSI, or StochRSI, is a technical analysis indicator developed by Tushar Chande and Stanley Kroll. It is an "indicator of an indicator," applying the Stochastic Oscillator formula to a set of Relative Strength Index (RSI) values instead of standard price data. The primary goal of the StochRSI is to identify overbought and oversold conditions with greater sensitivity and speed than the RSI alone. - -The "Fast" version of the StochRSI represents the raw, unsmoothed calculation of the Stochastic on the RSI. It is more responsive to momentum changes than its "Slow" counterpart but can also be more prone to generating false signals. - -## 2. Mathematical Foundations and Calculation Logic - -The Fast StochRSI calculation is a direct application of the Stochastic formula to an RSI data series. - -### Required Components - -- **RSI (Relative Strength Index):** The underlying data series for the calculation. -- **Stochastic %K Period:** The lookback period used to find the highest and lowest RSI values. -- **%D Period:** The period for the smoothing of the %K line to create the signal line. - -### Calculation Steps (Algorithm) - -1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). Let's denote this as $\text{RSI}_i$. - -2. **Calculate the Fast %K:** Apply the Stochastic formula to the RSI data series. This finds where the current RSI value lies in relation to its highest and lowest values over the Stochastic period. This line is the main line of the Fast StochRSI. - $\text{Fast \%K}_i = \frac{\text{RSI}_i - \text{Lowest Low RSI}_{\text{Stoch Period}}}{\text{Highest High RSI}_{\text{Stoch Period}} - \text{Lowest Low RSI}_{\text{Stoch Period}}} \times 100$ - Where: - - - $\text{Lowest Low RSI}$ is the minimum RSI value over the Stochastic lookback period. - - $\text{Highest High RSI}$ is the maximum RSI value over the Stochastic lookback period. - -3. **Calculate the %D (Signal Line):** The signal line is a moving average (typically a Simple Moving Average) of the Fast %K line. - $\text{\%D}_i = \text{SMA}(\text{Fast \%K}, \text{\%D Period})_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> RSI -> %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. - -- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series. The handle's resources are properly managed and released in the `OnDeinit` function. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: - - 1. **Step 1:** RSI data is retrieved into the `BufferRSI` calculation buffer. - 2. **Step 2:** The Fast %K line is calculated from `BufferRSI` and stored in the `BufferK` plot buffer. - 3. **Step 3:** The %D signal line is calculated by applying a simple moving average to `BufferK` and storing the result in the `BufferD` plot buffer. - -- **Heikin Ashi Variant (`StochRSI_Fast_HeikinAshi.mq5`):** - - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. Instead of using the standard `iRSI`, it utilizes our custom `CHeikinAshi_RSI_Calculator` from the `HeikinAshi_Tools.mqh` library. - - This ensures that the entire calculation, from the very first RSI value to the final %D line, is based on the smoothed Heikin Ashi price data, providing a consistent, filtered view of momentum. - -## 4. Parameters - -- **RSI Length (`InpLengthRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. -- **Stochastic Length (`InpLengthStoch`):** The lookback period for finding the highest and lowest RSI values (%K calculation). Default is `14`. -- **%D Smoothing (`InpSmoothD`):** The smoothing period for the signal line (%D). Default is `3`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the initial RSI calculation (e.g., `PRICE_CLOSE`). - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The primary use of StochRSI is to identify overbought (typically above 80) and oversold (typically below 20) conditions. The Fast version will reach these levels more quickly and frequently than the Slow version or the standard RSI. -- **Crossovers:** The crossover of the %K line and the %D signal line can be used to generate trade signals. A crossover of %K above %D can be a bullish signal, especially when occurring in oversold territory. A crossover of %K below %D can be a bearish signal, especially in overbought territory. -- **Divergence:** Look for divergences between the StochRSI and the price action. For example, if the price is making a new high but the StochRSI is failing to do so (bearish divergence), it could signal a potential trend reversal. -- **Caution:** The Fast StochRSI is extremely sensitive and can produce a significant number of false signals ("whipsaws"), especially in choppy or strongly trending markets. It is often used as a component in a larger trading system or confirmed with other, less sensitive indicators. +# Fast Stochastic RSI (StochRSI) Indicator + +## 1. Summary (Introduction) + +The Stochastic RSI, or StochRSI, is a technical analysis indicator developed by Tushar Chande and Stanley Kroll. It is an "indicator of an indicator," applying the Stochastic Oscillator formula to a set of Relative Strength Index (RSI) values instead of standard price data. The primary goal of the StochRSI is to identify overbought and oversold conditions with greater sensitivity and speed than the RSI alone. + +The "Fast" version of the StochRSI represents the raw, unsmoothed calculation of the Stochastic on the RSI. It is more responsive to momentum changes than its "Slow" counterpart but can also be more prone to generating false signals. + +## 2. Mathematical Foundations and Calculation Logic + +The Fast StochRSI calculation is a direct application of the Stochastic formula to an RSI data series. + +### Required Components + +- **RSI (Relative Strength Index):** The underlying data series for the calculation. +- **Stochastic %K Period:** The lookback period used to find the highest and lowest RSI values. +- **%D Period:** The period for the smoothing of the %K line to create the signal line. + +### Calculation Steps (Algorithm) + +1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). Let's denote this as $\text{RSI}_i$. + +2. **Calculate the Fast %K:** Apply the Stochastic formula to the RSI data series. This finds where the current RSI value lies in relation to its highest and lowest values over the Stochastic period. This line is the main line of the Fast StochRSI. + $\text{Fast \%K}_i = \frac{\text{RSI}_i - \text{Lowest Low RSI}_{\text{Stoch Period}}}{\text{Highest High RSI}_{\text{Stoch Period}} - \text{Lowest Low RSI}_{\text{Stoch Period}}} \times 100$ + Where: + + - $\text{Lowest Low RSI}$ is the minimum RSI value over the Stochastic lookback period. + - $\text{Highest High RSI}$ is the maximum RSI value over the Stochastic lookback period. + +3. **Calculate the %D (Signal Line):** The signal line is a moving average (typically a Simple Moving Average) of the Fast %K line. + $\text{\%D}_i = \text{SMA}(\text{Fast \%K}, \text{\%D Period})_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> RSI -> %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. + +- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series. The handle's resources are properly managed and released in the `OnDeinit` function. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: + + 1. **Step 1:** RSI data is retrieved into the `BufferRSI` calculation buffer. + 2. **Step 2:** The Fast %K line is calculated from `BufferRSI` and stored in the `BufferK` plot buffer. + 3. **Step 3:** The %D signal line is calculated by applying a simple moving average to `BufferK` and storing the result in the `BufferD` plot buffer. + +- **Heikin Ashi Variant (`StochRSI_Fast_HeikinAshi.mq5`):** + - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. Instead of using the standard `iRSI`, it utilizes our custom `CHeikinAshi_RSI_Calculator` from the `HeikinAshi_Tools.mqh` library. + - This ensures that the entire calculation, from the very first RSI value to the final %D line, is based on the smoothed Heikin Ashi price data, providing a consistent, filtered view of momentum. + +## 4. Parameters + +- **RSI Length (`InpLengthRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. +- **Stochastic Length (`InpLengthStoch`):** The lookback period for finding the highest and lowest RSI values (%K calculation). Default is `14`. +- **%D Smoothing (`InpSmoothD`):** The smoothing period for the signal line (%D). Default is `3`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the initial RSI calculation (e.g., `PRICE_CLOSE`). + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The primary use of StochRSI is to identify overbought (typically above 80) and oversold (typically below 20) conditions. The Fast version will reach these levels more quickly and frequently than the Slow version or the standard RSI. +- **Crossovers:** The crossover of the %K line and the %D signal line can be used to generate trade signals. A crossover of %K above %D can be a bullish signal, especially when occurring in oversold territory. A crossover of %K below %D can be a bearish signal, especially in overbought territory. +- **Divergence:** Look for divergences between the StochRSI and the price action. For example, if the price is making a new high but the StochRSI is failing to do so (bearish divergence), it could signal a potential trend reversal. +- **Caution:** The Fast StochRSI is extremely sensitive and can produce a significant number of false signals ("whipsaws"), especially in choppy or strongly trending markets. It is often used as a component in a larger trading system or confirmed with other, less sensitive indicators. diff --git a/Indicators/MyIndicators/StochRSI_Fast.mq5 b/Indicators/MyIndicators/StochRSI_Fast.mq5 index 12b9cfd..f82352e 100644 --- a/Indicators/MyIndicators/StochRSI_Fast.mq5 +++ b/Indicators/MyIndicators/StochRSI_Fast.mq5 @@ -1,181 +1,181 @@ -//+------------------------------------------------------------------+ -//| StochRSI_Fast.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Fast Stochastic RSI Oscillator" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 3 // %K, %D, and RSI calculation buffer -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum -10.0 -#property indicator_maximum 110.0 - -//--- Plot 1: %K line -#property indicator_label1 "%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line -#property indicator_label2 "%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthRSI = 14; // RSI Length -input int InpLengthStoch = 14; // Stochastic Length (%K Period) -input int InpSmoothD = 3; // %D Smoothing (Signal Line) -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // RSI Source Price - -//--- Indicator Buffers --- -double BufferK[]; -double BufferD[]; -double BufferRSI[]; - -//--- Global Variables --- -int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD; -int g_handle_rsi; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; - g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; - g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; - - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - SetIndexBuffer(2, BufferRSI, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - ArraySetAsSeries(BufferRSI, false); - - g_handle_rsi = iRSI(_Symbol, _Period, g_ExtLengthRSI, InpAppliedPrice); - if(g_handle_rsi == INVALID_HANDLE) - { - Print("Error creating iRSI handle."); - return(INIT_FAILED); - } - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch - 2); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fast StochRSI(%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Release the indicator handle - IndicatorRelease(g_handle_rsi); - } - -//+------------------------------------------------------------------+ -//| Fast Stochastic RSI calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 2; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Get RSI values from the standard indicator - if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRSI) < rates_total) - { - Print("Error copying iRSI buffer data."); - } - -//--- STEP 2: Calculate Fast %K on the RSI buffer - int k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; - for(int i = k_start_pos; i < rates_total; i++) - { - double highest_rsi = Highest(BufferRSI, g_ExtLengthStoch, i); - double lowest_rsi = Lowest(BufferRSI, g_ExtLengthStoch, i); - - double range = highest_rsi - lowest_rsi; - if(range > 0.00001) - BufferK[i] = (BufferRSI[i] - lowest_rsi) / range * 100.0; - else - BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; - } - -//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K - int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSmoothD; j++) - { - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtSmoothD; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochRSI_Fast.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Fast Stochastic RSI Oscillator" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 3 // %K, %D, and RSI calculation buffer +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum -10.0 +#property indicator_maximum 110.0 + +//--- Plot 1: %K line +#property indicator_label1 "%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line +#property indicator_label2 "%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthRSI = 14; // RSI Length +input int InpLengthStoch = 14; // Stochastic Length (%K Period) +input int InpSmoothD = 3; // %D Smoothing (Signal Line) +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // RSI Source Price + +//--- Indicator Buffers --- +double BufferK[]; +double BufferD[]; +double BufferRSI[]; + +//--- Global Variables --- +int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD; +int g_handle_rsi; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; + g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; + g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; + + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + SetIndexBuffer(2, BufferRSI, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + ArraySetAsSeries(BufferRSI, false); + + g_handle_rsi = iRSI(_Symbol, _Period, g_ExtLengthRSI, InpAppliedPrice); + if(g_handle_rsi == INVALID_HANDLE) + { + Print("Error creating iRSI handle."); + return(INIT_FAILED); + } + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch - 2); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fast StochRSI(%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Release the indicator handle + IndicatorRelease(g_handle_rsi); + } + +//+------------------------------------------------------------------+ +//| Fast Stochastic RSI calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 2; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Get RSI values from the standard indicator + if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRSI) < rates_total) + { + Print("Error copying iRSI buffer data."); + } + +//--- STEP 2: Calculate Fast %K on the RSI buffer + int k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; + for(int i = k_start_pos; i < rates_total; i++) + { + double highest_rsi = Highest(BufferRSI, g_ExtLengthStoch, i); + double lowest_rsi = Lowest(BufferRSI, g_ExtLengthStoch, i); + + double range = highest_rsi - lowest_rsi; + if(range > 0.00001) + BufferK[i] = (BufferRSI[i] - lowest_rsi) / range * 100.0; + else + BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; + } + +//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K + int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSmoothD; j++) + { + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtSmoothD; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochRSI_Fast_HeikinAshi.mq5 b/Indicators/MyIndicators/StochRSI_Fast_HeikinAshi.mq5 index 0134efa..d52bac9 100644 --- a/Indicators/MyIndicators/StochRSI_Fast_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/StochRSI_Fast_HeikinAshi.mq5 @@ -1,189 +1,189 @@ -//+------------------------------------------------------------------+ -//| StochRSI_Fast_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored to be self-contained, no iCustom -#property description "Fast Stochastic on a Heikin Ashi based RSI" - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 3 // %K, %D, and HA_RSI for calculation -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum -10.0 -#property indicator_maximum 110.0 - -//--- Plot 1: %K line -#property indicator_label1 "HA_%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrBlue -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line -#property indicator_label2 "HA_%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrOrange -#property indicator_style2 STYLE_SOLID -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthRSI = 14; // RSI Length -input int InpLengthStoch = 14; // Stochastic Length (%K Period) -input int InpSmoothD = 3; // %D Smoothing (Signal Line) - -//--- Indicator Buffers --- -double BufferK[]; -double BufferD[]; -double BufferHA_RSI[]; // Buffer to store the Heikin Ashi RSI values - -//--- Global Objects and Variables --- -int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD; -CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator; // Pointer to our new calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; - g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; - g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; - - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - SetIndexBuffer(2, BufferHA_RSI, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - ArraySetAsSeries(BufferHA_RSI, false); - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch - 2); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_StochRSI_Fast(%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD)); - -//--- Create the calculator instance - g_ha_rsi_calculator = new CHeikinAshi_RSI_Calculator(); - if(CheckPointer(g_ha_rsi_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_RSI_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID) - { - delete g_ha_rsi_calculator; - g_ha_rsi_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Fast StochRSI on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 2; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Calculate Heikin Ashi RSI values using our new toolkit - if(!g_ha_rsi_calculator.Calculate(rates_total, g_ExtLengthRSI, open, high, low, close, BufferHA_RSI)) - { - Print("Heikin Ashi RSI calculation failed."); - return(0); - } - -//--- STEP 2: Calculate Fast %K on the HA_RSI buffer - int k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; - for(int i = k_start_pos; i < rates_total; i++) - { - double highest_ha_rsi = Highest(BufferHA_RSI, g_ExtLengthStoch, i); - double lowest_ha_rsi = Lowest(BufferHA_RSI, g_ExtLengthStoch, i); - - double range = highest_ha_rsi - lowest_ha_rsi; - if(range > 0.00001) - BufferK[i] = (BufferHA_RSI[i] - lowest_ha_rsi) / range * 100.0; - else - BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; - } - -//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K - int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSmoothD; j++) - { - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtSmoothD; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochRSI_Fast_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored to be self-contained, no iCustom +#property description "Fast Stochastic on a Heikin Ashi based RSI" + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 3 // %K, %D, and HA_RSI for calculation +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum -10.0 +#property indicator_maximum 110.0 + +//--- Plot 1: %K line +#property indicator_label1 "HA_%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrBlue +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line +#property indicator_label2 "HA_%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrOrange +#property indicator_style2 STYLE_SOLID +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthRSI = 14; // RSI Length +input int InpLengthStoch = 14; // Stochastic Length (%K Period) +input int InpSmoothD = 3; // %D Smoothing (Signal Line) + +//--- Indicator Buffers --- +double BufferK[]; +double BufferD[]; +double BufferHA_RSI[]; // Buffer to store the Heikin Ashi RSI values + +//--- Global Objects and Variables --- +int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD; +CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator; // Pointer to our new calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; + g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; + g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; + + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + SetIndexBuffer(2, BufferHA_RSI, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + ArraySetAsSeries(BufferHA_RSI, false); + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch - 2); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_StochRSI_Fast(%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSmoothD)); + +//--- Create the calculator instance + g_ha_rsi_calculator = new CHeikinAshi_RSI_Calculator(); + if(CheckPointer(g_ha_rsi_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_RSI_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID) + { + delete g_ha_rsi_calculator; + g_ha_rsi_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Fast StochRSI on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 2; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Calculate Heikin Ashi RSI values using our new toolkit + if(!g_ha_rsi_calculator.Calculate(rates_total, g_ExtLengthRSI, open, high, low, close, BufferHA_RSI)) + { + Print("Heikin Ashi RSI calculation failed."); + return(0); + } + +//--- STEP 2: Calculate Fast %K on the HA_RSI buffer + int k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; + for(int i = k_start_pos; i < rates_total; i++) + { + double highest_ha_rsi = Highest(BufferHA_RSI, g_ExtLengthStoch, i); + double lowest_ha_rsi = Lowest(BufferHA_RSI, g_ExtLengthStoch, i); + + double range = highest_ha_rsi - lowest_ha_rsi; + if(range > 0.00001) + BufferK[i] = (BufferHA_RSI[i] - lowest_ha_rsi) / range * 100.0; + else + BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; + } + +//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K + int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSmoothD - 3; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSmoothD; j++) + { + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtSmoothD; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochRSI_Slow.md b/Indicators/MyIndicators/StochRSI_Slow.md index 7928b77..ff00de3 100644 --- a/Indicators/MyIndicators/StochRSI_Slow.md +++ b/Indicators/MyIndicators/StochRSI_Slow.md @@ -1,69 +1,69 @@ -# Slow Stochastic RSI (StochRSI) Indicator - -## 1. Summary (Introduction) - -The Stochastic RSI, or StochRSI, is a technical analysis indicator developed by Tushar Chande and Stanley Kroll. It is essentially an "indicator of an indicator," applying the Stochastic Oscillator formula to a set of Relative Strength Index (RSI) values instead of standard price data. The primary goal of the StochRSI is to identify overbought and oversold conditions with greater sensitivity and speed than the RSI alone. - -The "Slow" version of the StochRSI adds an extra layer of smoothing to its main line, making it less erratic than the "Fast" version and helping to filter out minor, insignificant fluctuations. - -## 2. Mathematical Foundations and Calculation Logic - -The StochRSI calculation is a multi-step process that builds upon the standard RSI and Stochastic formulas. - -### Required Components - -- **RSI (Relative Strength Index):** The underlying data series for the calculation. -- **Stochastic %K Period:** The lookback period used to find the highest and lowest RSI values. -- **Slowing Period:** The period for the first smoothing step, which transforms the "Fast %K" into the "Slow %K". -- **%D Period:** The period for the second smoothing step, which creates the signal line (%D) from the Slow %K. - -### Calculation Steps (Algorithm) - -1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). Let's denote this as $\text{RSI}_i$. - -2. **Calculate the Raw %K (Fast StochRSI):** Apply the Stochastic formula to the RSI data series. This finds where the current RSI value lies in relation to its highest and lowest values over a given period. - $\text{Raw \%K}_i = \frac{\text{RSI}_i - \text{Lowest Low RSI}_{\text{Stoch Period}}}{\text{Highest High RSI}_{\text{Stoch Period}} - \text{Lowest Low RSI}_{\text{Stoch Period}}} \times 100$ - Where: - - - $\text{Lowest Low RSI}$ is the minimum RSI value over the Stochastic lookback period. - - $\text{Highest High RSI}$ is the maximum RSI value over the Stochastic lookback period. - -3. **Calculate the Slow %K (Main Line):** This is the key step that differentiates the "Slow" from the "Fast" StochRSI. The Raw %K line is smoothed, typically with a Simple Moving Average (SMA), to create the final %K line. - $\text{Slow \%K}_i = \text{SMA}(\text{Raw \%K}, \text{Slowing Period})_i$ - -4. **Calculate the %D (Signal Line):** The signal line is a moving average of the Slow %K line, providing an additional layer of smoothing. - $\text{Slow \%D}_i = \text{SMA}(\text{Slow \%K}, \text{\%D Period})_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> RSI -> Raw %K -> Slow %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. - -- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series without re-implementing the logic ourselves. The handle's resources are properly managed and released in the `OnDeinit` function. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: - - 1. **Step 1:** RSI data is retrieved into `BufferRSI`. - 2. **Step 2:** Raw %K is calculated from `BufferRSI` and stored in `BufferRawStochK`. - 3. **Step 3:** Slow %K (the main plot line) is calculated by smoothing `BufferRawStochK` and stored in `BufferK`. - 4. **Step 4:** The %D signal line is calculated by smoothing `BufferK` and stored in `BufferD`. - -- **Heikin Ashi Variant (`StochRSI_Slow_HeikinAshi.mq5`):** - - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. Instead of using the standard `iRSI`, it utilizes our custom `CHeikinAshi_RSI_Calculator` from the `HeikinAshi_Tools.mqh` library. - - This ensures that the entire calculation, from the very first RSI value to the final %D line, is based on the smoothed Heikin Ashi price data, providing a consistent, filtered view of momentum. - -## 4. Parameters - -- **RSI Length (`InpLengthRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. -- **Stochastic Length (`InpLengthStoch`):** The lookback period for finding the highest and lowest RSI values (%K calculation). Default is `14`. -- **Slowing (`InpSlowing`):** The smoothing period applied to the Raw %K to create the final Slow %K line. A value of `1` results in a Fast StochRSI. Default is `3`. -- **%D Smoothing (`InpSmoothD`):** The smoothing period for the signal line (%D). Default is `3`. -- **Applied Price (`InpAppliedPrice`):** The source price used for the initial RSI calculation (e.g., `PRICE_CLOSE`). - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The primary use of StochRSI is to identify overbought (typically above 80) and oversold (typically below 20) conditions. Because it is more sensitive than RSI, it tends to reach these levels more frequently. -- **Crossovers:** The crossover of the %K line and the %D signal line can be used to generate trade signals. A crossover of %K above %D can be a bullish signal, especially when occurring in oversold territory. A crossover of %K below %D can be a bearish signal, especially in overbought territory. -- **Divergence:** Look for divergences between the StochRSI and the price action. For example, if the price is making a new high but the StochRSI is failing to do so (bearish divergence), it could signal a potential trend reversal. -- **Caution:** Due to its sensitivity, StochRSI can produce many false signals, especially in strongly trending markets where it may remain in overbought/oversold territory for extended periods. It is best used for confirmation alongside other trend-following indicators. +# Slow Stochastic RSI (StochRSI) Indicator + +## 1. Summary (Introduction) + +The Stochastic RSI, or StochRSI, is a technical analysis indicator developed by Tushar Chande and Stanley Kroll. It is essentially an "indicator of an indicator," applying the Stochastic Oscillator formula to a set of Relative Strength Index (RSI) values instead of standard price data. The primary goal of the StochRSI is to identify overbought and oversold conditions with greater sensitivity and speed than the RSI alone. + +The "Slow" version of the StochRSI adds an extra layer of smoothing to its main line, making it less erratic than the "Fast" version and helping to filter out minor, insignificant fluctuations. + +## 2. Mathematical Foundations and Calculation Logic + +The StochRSI calculation is a multi-step process that builds upon the standard RSI and Stochastic formulas. + +### Required Components + +- **RSI (Relative Strength Index):** The underlying data series for the calculation. +- **Stochastic %K Period:** The lookback period used to find the highest and lowest RSI values. +- **Slowing Period:** The period for the first smoothing step, which transforms the "Fast %K" into the "Slow %K". +- **%D Period:** The period for the second smoothing step, which creates the signal line (%D) from the Slow %K. + +### Calculation Steps (Algorithm) + +1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price (e.g., Close). Let's denote this as $\text{RSI}_i$. + +2. **Calculate the Raw %K (Fast StochRSI):** Apply the Stochastic formula to the RSI data series. This finds where the current RSI value lies in relation to its highest and lowest values over a given period. + $\text{Raw \%K}_i = \frac{\text{RSI}_i - \text{Lowest Low RSI}_{\text{Stoch Period}}}{\text{Highest High RSI}_{\text{Stoch Period}} - \text{Lowest Low RSI}_{\text{Stoch Period}}} \times 100$ + Where: + + - $\text{Lowest Low RSI}$ is the minimum RSI value over the Stochastic lookback period. + - $\text{Highest High RSI}$ is the maximum RSI value over the Stochastic lookback period. + +3. **Calculate the Slow %K (Main Line):** This is the key step that differentiates the "Slow" from the "Fast" StochRSI. The Raw %K line is smoothed, typically with a Simple Moving Average (SMA), to create the final %K line. + $\text{Slow \%K}_i = \text{SMA}(\text{Raw \%K}, \text{Slowing Period})_i$ + +4. **Calculate the %D (Signal Line):** The signal line is a moving average of the Slow %K line, providing an additional layer of smoothing. + $\text{Slow \%D}_i = \text{SMA}(\text{Slow \%K}, \text{\%D Period})_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> RSI -> Raw %K -> Slow %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. + +- **Leveraging Standard Indicators:** For the initial RSI calculation, we use a handle to MQL5's built-in `iRSI` indicator. This is a robust and efficient method for obtaining the underlying RSI data series without re-implementing the logic ourselves. The handle's resources are properly managed and released in the `OnDeinit` function. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: + + 1. **Step 1:** RSI data is retrieved into `BufferRSI`. + 2. **Step 2:** Raw %K is calculated from `BufferRSI` and stored in `BufferRawStochK`. + 3. **Step 3:** Slow %K (the main plot line) is calculated by smoothing `BufferRawStochK` and stored in `BufferK`. + 4. **Step 4:** The %D signal line is calculated by smoothing `BufferK` and stored in `BufferD`. + +- **Heikin Ashi Variant (`StochRSI_Slow_HeikinAshi.mq5`):** + - Our toolkit also includes a "pure" Heikin Ashi version of this indicator. Instead of using the standard `iRSI`, it utilizes our custom `CHeikinAshi_RSI_Calculator` from the `HeikinAshi_Tools.mqh` library. + - This ensures that the entire calculation, from the very first RSI value to the final %D line, is based on the smoothed Heikin Ashi price data, providing a consistent, filtered view of momentum. + +## 4. Parameters + +- **RSI Length (`InpLengthRSI`):** The lookback period for the underlying RSI calculation. Default is `14`. +- **Stochastic Length (`InpLengthStoch`):** The lookback period for finding the highest and lowest RSI values (%K calculation). Default is `14`. +- **Slowing (`InpSlowing`):** The smoothing period applied to the Raw %K to create the final Slow %K line. A value of `1` results in a Fast StochRSI. Default is `3`. +- **%D Smoothing (`InpSmoothD`):** The smoothing period for the signal line (%D). Default is `3`. +- **Applied Price (`InpAppliedPrice`):** The source price used for the initial RSI calculation (e.g., `PRICE_CLOSE`). + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The primary use of StochRSI is to identify overbought (typically above 80) and oversold (typically below 20) conditions. Because it is more sensitive than RSI, it tends to reach these levels more frequently. +- **Crossovers:** The crossover of the %K line and the %D signal line can be used to generate trade signals. A crossover of %K above %D can be a bullish signal, especially when occurring in oversold territory. A crossover of %K below %D can be a bearish signal, especially in overbought territory. +- **Divergence:** Look for divergences between the StochRSI and the price action. For example, if the price is making a new high but the StochRSI is failing to do so (bearish divergence), it could signal a potential trend reversal. +- **Caution:** Due to its sensitivity, StochRSI can produce many false signals, especially in strongly trending markets where it may remain in overbought/oversold territory for extended periods. It is best used for confirmation alongside other trend-following indicators. diff --git a/Indicators/MyIndicators/StochRSI_Slow.mq5 b/Indicators/MyIndicators/StochRSI_Slow.mq5 index 1a8f5fb..8ec89fd 100644 --- a/Indicators/MyIndicators/StochRSI_Slow.mq5 +++ b/Indicators/MyIndicators/StochRSI_Slow.mq5 @@ -1,199 +1,199 @@ -//+------------------------------------------------------------------+ -//| StochRSI_Slow.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Slow Stochastic RSI Oscillator" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 4 // %K, %D, RawK, and RSI buffer -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum -10.0 -#property indicator_maximum 110.0 - -//--- Plot 1: %K line (Slow) -#property indicator_label1 "%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthRSI = 14; -input int InpLengthStoch = 14; -input int InpSlowing = 3; -input int InpSmoothD = 3; -input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; - -//--- Indicator Buffers --- -double BufferK[]; -double BufferD[]; -double BufferRSI[]; -double BufferRawStochK[]; - -//--- Global Variables --- -int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD; -int g_handle_rsi; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; - g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; - g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; - g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; - - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - SetIndexBuffer(2, BufferRSI, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRawStochK, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - ArraySetAsSeries(BufferRSI, false); - ArraySetAsSeries(BufferRawStochK, false); - - g_handle_rsi = iRSI(_Symbol, _Period, g_ExtLengthRSI, InpAppliedPrice); - if(g_handle_rsi == INVALID_HANDLE) - { - Print("Error creating iRSI handle."); - return(INIT_FAILED); - } - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Slow StochRSI(%d,%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Release the indicator handle - IndicatorRelease(g_handle_rsi); - } - -//+------------------------------------------------------------------+ -//| Slow Stochastic RSI calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 3; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Get RSI values from the standard indicator - if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRSI) < rates_total) - { - Print("Error copying iRSI buffer data."); - } - -//--- STEP 2: Calculate Raw Stochastic %K on the RSI buffer - int raw_k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; - for(int i = raw_k_start_pos; i < rates_total; i++) - { - double highest_rsi = Highest(BufferRSI, g_ExtLengthStoch, i); - double lowest_rsi = Lowest(BufferRSI, g_ExtLengthStoch, i); - - double range = highest_rsi - lowest_rsi; - if(range > 0.00001) - BufferRawStochK[i] = (BufferRSI[i] - lowest_rsi) / range * 100.0; - else - BufferRawStochK[i] = (i > 0) ? BufferRawStochK[i-1] : 50.0; - } - -//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K - int k_slow_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3; - for(int i = k_slow_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSlowing; j++) - { - sum += BufferRawStochK[i-j]; - } - BufferK[i] = sum / g_ExtSlowing; - } - -//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K - int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSmoothD; j++) - { - // --- FIX: Source for %D is the %K buffer, not itself --- - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtSmoothD; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochRSI_Slow.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Slow Stochastic RSI Oscillator" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 4 // %K, %D, RawK, and RSI buffer +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum -10.0 +#property indicator_maximum 110.0 + +//--- Plot 1: %K line (Slow) +#property indicator_label1 "%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthRSI = 14; +input int InpLengthStoch = 14; +input int InpSlowing = 3; +input int InpSmoothD = 3; +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; + +//--- Indicator Buffers --- +double BufferK[]; +double BufferD[]; +double BufferRSI[]; +double BufferRawStochK[]; + +//--- Global Variables --- +int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD; +int g_handle_rsi; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; + g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; + g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; + g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; + + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + SetIndexBuffer(2, BufferRSI, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRawStochK, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + ArraySetAsSeries(BufferRSI, false); + ArraySetAsSeries(BufferRawStochK, false); + + g_handle_rsi = iRSI(_Symbol, _Period, g_ExtLengthRSI, InpAppliedPrice); + if(g_handle_rsi == INVALID_HANDLE) + { + Print("Error creating iRSI handle."); + return(INIT_FAILED); + } + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Slow StochRSI(%d,%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Release the indicator handle + IndicatorRelease(g_handle_rsi); + } + +//+------------------------------------------------------------------+ +//| Slow Stochastic RSI calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 3; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Get RSI values from the standard indicator + if(CopyBuffer(g_handle_rsi, 0, 0, rates_total, BufferRSI) < rates_total) + { + Print("Error copying iRSI buffer data."); + } + +//--- STEP 2: Calculate Raw Stochastic %K on the RSI buffer + int raw_k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; + for(int i = raw_k_start_pos; i < rates_total; i++) + { + double highest_rsi = Highest(BufferRSI, g_ExtLengthStoch, i); + double lowest_rsi = Lowest(BufferRSI, g_ExtLengthStoch, i); + + double range = highest_rsi - lowest_rsi; + if(range > 0.00001) + BufferRawStochK[i] = (BufferRSI[i] - lowest_rsi) / range * 100.0; + else + BufferRawStochK[i] = (i > 0) ? BufferRawStochK[i-1] : 50.0; + } + +//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K + int k_slow_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3; + for(int i = k_slow_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSlowing; j++) + { + sum += BufferRawStochK[i-j]; + } + BufferK[i] = sum / g_ExtSlowing; + } + +//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K + int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSmoothD; j++) + { + // --- FIX: Source for %D is the %K buffer, not itself --- + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtSmoothD; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochRSI_Slow_HeikinAshi.mq5 b/Indicators/MyIndicators/StochRSI_Slow_HeikinAshi.mq5 index 3553819..4c14588 100644 --- a/Indicators/MyIndicators/StochRSI_Slow_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/StochRSI_Slow_HeikinAshi.mq5 @@ -1,206 +1,206 @@ -//+------------------------------------------------------------------+ -//| StochRSI_Slow_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored to be self-contained, no iCustom -#property description "Slow Stochastic on a Heikin Ashi based RSI" - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 4 // %K, %D, RawK, and HA_RSI buffer -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum -10.0 -#property indicator_maximum 110.0 - -//--- Plot 1: %K line (Slow) -#property indicator_label1 "HA_%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "HA_%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpLengthRSI = 14; // RSI Length -input int InpLengthStoch = 14; // Stochastic %K Period -input int InpSlowing = 3; // Slowing Period -input int InpSmoothD = 3; // %D Smoothing Period - -//--- Indicator Buffers --- -double BufferK[]; -double BufferD[]; -double BufferHA_RSI[]; -double BufferRawStochK[]; - -//--- Global Objects and Variables --- -int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD; -CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator; // Pointer to our HA_RSI calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; - g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; - g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; - g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; - - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - SetIndexBuffer(2, BufferHA_RSI, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferRawStochK, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - ArraySetAsSeries(BufferHA_RSI, false); - ArraySetAsSeries(BufferRawStochK, false); - - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_StochRSI_Slow(%d,%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD)); - -//--- Create the calculator instance - g_ha_rsi_calculator = new CHeikinAshi_RSI_Calculator(); - if(CheckPointer(g_ha_rsi_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_RSI_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID) - { - delete g_ha_rsi_calculator; - g_ha_rsi_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Slow StochRSI on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 3; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Calculate Heikin Ashi RSI values using our toolkit - if(!g_ha_rsi_calculator.Calculate(rates_total, g_ExtLengthRSI, open, high, low, close, BufferHA_RSI)) - { - Print("Heikin Ashi RSI calculation failed."); - return(0); - } - -//--- STEP 2: Calculate Raw Stochastic %K on the HA_RSI buffer - int raw_k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; - for(int i = raw_k_start_pos; i < rates_total; i++) - { - double highest_ha_rsi = Highest(BufferHA_RSI, g_ExtLengthStoch, i); - double lowest_ha_rsi = Lowest(BufferHA_RSI, g_ExtLengthStoch, i); - - double range = highest_ha_rsi - lowest_ha_rsi; - if(range > 0.00001) - BufferRawStochK[i] = (BufferHA_RSI[i] - lowest_ha_rsi) / range * 100.0; - else - BufferRawStochK[i] = (i > 0) ? BufferRawStochK[i-1] : 50.0; - } - -//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K - int k_slow_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3; - for(int i = k_slow_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSlowing; j++) - { - sum += BufferRawStochK[i-j]; - } - BufferK[i] = sum / g_ExtSlowing; - } - -//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K - int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSmoothD; j++) - { - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtSmoothD; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochRSI_Slow_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored to be self-contained, no iCustom +#property description "Slow Stochastic on a Heikin Ashi based RSI" + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 4 // %K, %D, RawK, and HA_RSI buffer +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum -10.0 +#property indicator_maximum 110.0 + +//--- Plot 1: %K line (Slow) +#property indicator_label1 "HA_%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "HA_%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpLengthRSI = 14; // RSI Length +input int InpLengthStoch = 14; // Stochastic %K Period +input int InpSlowing = 3; // Slowing Period +input int InpSmoothD = 3; // %D Smoothing Period + +//--- Indicator Buffers --- +double BufferK[]; +double BufferD[]; +double BufferHA_RSI[]; +double BufferRawStochK[]; + +//--- Global Objects and Variables --- +int g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD; +CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator; // Pointer to our HA_RSI calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtLengthRSI = (InpLengthRSI < 1) ? 1 : InpLengthRSI; + g_ExtLengthStoch = (InpLengthStoch < 1) ? 1 : InpLengthStoch; + g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; + g_ExtSmoothD = (InpSmoothD < 1) ? 1 : InpSmoothD; + + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + SetIndexBuffer(2, BufferHA_RSI, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferRawStochK, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + ArraySetAsSeries(BufferHA_RSI, false); + ArraySetAsSeries(BufferRawStochK, false); + + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_StochRSI_Slow(%d,%d,%d,%d)", g_ExtLengthRSI, g_ExtLengthStoch, g_ExtSlowing, g_ExtSmoothD)); + +//--- Create the calculator instance + g_ha_rsi_calculator = new CHeikinAshi_RSI_Calculator(); + if(CheckPointer(g_ha_rsi_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_RSI_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID) + { + delete g_ha_rsi_calculator; + g_ha_rsi_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Slow StochRSI on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + int start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 3; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Calculate Heikin Ashi RSI values using our toolkit + if(!g_ha_rsi_calculator.Calculate(rates_total, g_ExtLengthRSI, open, high, low, close, BufferHA_RSI)) + { + Print("Heikin Ashi RSI calculation failed."); + return(0); + } + +//--- STEP 2: Calculate Raw Stochastic %K on the HA_RSI buffer + int raw_k_start_pos = g_ExtLengthRSI + g_ExtLengthStoch - 2; + for(int i = raw_k_start_pos; i < rates_total; i++) + { + double highest_ha_rsi = Highest(BufferHA_RSI, g_ExtLengthStoch, i); + double lowest_ha_rsi = Lowest(BufferHA_RSI, g_ExtLengthStoch, i); + + double range = highest_ha_rsi - lowest_ha_rsi; + if(range > 0.00001) + BufferRawStochK[i] = (BufferHA_RSI[i] - lowest_ha_rsi) / range * 100.0; + else + BufferRawStochK[i] = (i > 0) ? BufferRawStochK[i-1] : 50.0; + } + +//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K + int k_slow_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing - 3; + for(int i = k_slow_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSlowing; j++) + { + sum += BufferRawStochK[i-j]; + } + BufferK[i] = sum / g_ExtSlowing; + } + +//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K + int d_start_pos = g_ExtLengthRSI + g_ExtLengthStoch + g_ExtSlowing + g_ExtSmoothD - 4; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSmoothD; j++) + { + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtSmoothD; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochasticFast.md b/Indicators/MyIndicators/StochasticFast.md index c1a318d..78389e4 100644 --- a/Indicators/MyIndicators/StochasticFast.md +++ b/Indicators/MyIndicators/StochasticFast.md @@ -1,57 +1,57 @@ -# Fast Stochastic Oscillator - -## 1. Summary (Introduction) - -The Stochastic Oscillator, developed by George C. Lane in the late 1950s, is a momentum indicator that compares a particular closing price of a security to a range of its prices over a certain period of time. The "Fast" version is the original, unsmoothed calculation of the oscillator. - -It is designed to identify overbought and oversold conditions by measuring the speed and momentum of price changes. Due to its high sensitivity, it reacts very quickly to price movements, making it a tool for traders who need to identify short-term momentum shifts. - -## 2. Mathematical Foundations and Calculation Logic - -The Fast Stochastic is the foundational calculation from which the "Slow" and "Full" versions are derived. - -### Required Components - -- **%K Period:** The main lookback period for the Stochastic calculation. -- **%D Period:** The period for the smoothing of the %K line to create the signal line. - -### Calculation Steps (Algorithm) - -1. **Calculate the Fast %K (Main Line):** This is the core of the Stochastic calculation. It measures where the current close is relative to the highest high and lowest low over the `%K Period`. This raw calculation produces the main line of the Fast Stochastic. - $\text{Fast \%K}_i = \frac{\text{Close}_i - \text{Lowest Low}_{\%K \text{ Period}}}{\text{Highest High}_{\%K \text{ Period}} - \text{Lowest Low}_{\%K \text{ Period}}} \times 100$ - Where: - - - $\text{Lowest Low}$ is the minimum low price over the `%K Period`. - - $\text{Highest High}$ is the maximum high price over the `%K Period`. - -2. **Calculate the %D (Signal Line):** The signal line is a moving average (typically a Simple Moving Average) of the Fast %K line. - $\text{\%D}_i = \text{SMA}(\text{Fast \%K}, \text{\%D Period})_i$ - -_Note: In the "Slow" Stochastic, the Fast %K line is smoothed an additional time before the %D line is calculated. In the "Fast" Stochastic, this intermediate smoothing step is omitted._ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the two-stage calculation (Price -> %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: - - 1. **Step 1:** The Fast %K line is calculated from the standard `high`, `low`, and `close` price arrays and stored in the `BufferK` plot buffer. - 2. **Step 2:** The %D signal line is calculated by applying a simple moving average with the `%D Period` to the `BufferK`. The result is stored in the `BufferD` plot buffer. - -- **Heikin Ashi Variant (`StochasticFast_HeikinAshi.mqmq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input instead of the standard price data. - - This results in a smoother oscillator, as the input data itself is already filtered, effectively creating a hybrid between the Fast and Slow Stochastics. - -## 4. Parameters - -- **%K Period (`InpKPeriod`):** The lookback period for the Stochastic calculation (finding the highest high and lowest low). Default is `14`. -- **%D Period (`InpDPeriod`):** The smoothing period for the signal line (%D). Default is `3`. - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The primary use of the Stochastic is to identify overbought (typically above 80) and oversold (typically below 20) conditions. The Fast version will enter and exit these zones very rapidly. -- **Crossovers:** The crossover of the %K line and the %D signal line is a common trade signal. A crossover of %K above %D is considered bullish, especially in oversold territory. A crossover of %K below %D is considered bearish, especially in overbought territory. -- **Divergence:** Look for divergences between the Stochastic and the price action. If the price is making a new high but the Stochastic is failing to do so (bearish divergence), it could signal weakening momentum and a potential reversal. -- **Caution:** The Fast Stochastic is highly sensitive and can produce many false signals ("whipsaws"), especially in choppy markets. It is often used by short-term traders or as a fast-reacting component in a larger trading system. For most applications, the "Slow" Stochastic is preferred due to its superior filtering of market noise. +# Fast Stochastic Oscillator + +## 1. Summary (Introduction) + +The Stochastic Oscillator, developed by George C. Lane in the late 1950s, is a momentum indicator that compares a particular closing price of a security to a range of its prices over a certain period of time. The "Fast" version is the original, unsmoothed calculation of the oscillator. + +It is designed to identify overbought and oversold conditions by measuring the speed and momentum of price changes. Due to its high sensitivity, it reacts very quickly to price movements, making it a tool for traders who need to identify short-term momentum shifts. + +## 2. Mathematical Foundations and Calculation Logic + +The Fast Stochastic is the foundational calculation from which the "Slow" and "Full" versions are derived. + +### Required Components + +- **%K Period:** The main lookback period for the Stochastic calculation. +- **%D Period:** The period for the smoothing of the %K line to create the signal line. + +### Calculation Steps (Algorithm) + +1. **Calculate the Fast %K (Main Line):** This is the core of the Stochastic calculation. It measures where the current close is relative to the highest high and lowest low over the `%K Period`. This raw calculation produces the main line of the Fast Stochastic. + $\text{Fast \%K}_i = \frac{\text{Close}_i - \text{Lowest Low}_{\%K \text{ Period}}}{\text{Highest High}_{\%K \text{ Period}} - \text{Lowest Low}_{\%K \text{ Period}}} \times 100$ + Where: + + - $\text{Lowest Low}$ is the minimum low price over the `%K Period`. + - $\text{Highest High}$ is the maximum high price over the `%K Period`. + +2. **Calculate the %D (Signal Line):** The signal line is a moving average (typically a Simple Moving Average) of the Fast %K line. + $\text{\%D}_i = \text{SMA}(\text{Fast \%K}, \text{\%D Period})_i$ + +_Note: In the "Slow" Stochastic, the Fast %K line is smoothed an additional time before the %D line is calculated. In the "Fast" Stochastic, this intermediate smoothing step is omitted._ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the two-stage calculation (Price -> %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: + + 1. **Step 1:** The Fast %K line is calculated from the standard `high`, `low`, and `close` price arrays and stored in the `BufferK` plot buffer. + 2. **Step 2:** The %D signal line is calculated by applying a simple moving average with the `%D Period` to the `BufferK`. The result is stored in the `BufferD` plot buffer. + +- **Heikin Ashi Variant (`StochasticFast_HeikinAshi.mqmq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input instead of the standard price data. + - This results in a smoother oscillator, as the input data itself is already filtered, effectively creating a hybrid between the Fast and Slow Stochastics. + +## 4. Parameters + +- **%K Period (`InpKPeriod`):** The lookback period for the Stochastic calculation (finding the highest high and lowest low). Default is `14`. +- **%D Period (`InpDPeriod`):** The smoothing period for the signal line (%D). Default is `3`. + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The primary use of the Stochastic is to identify overbought (typically above 80) and oversold (typically below 20) conditions. The Fast version will enter and exit these zones very rapidly. +- **Crossovers:** The crossover of the %K line and the %D signal line is a common trade signal. A crossover of %K above %D is considered bullish, especially in oversold territory. A crossover of %K below %D is considered bearish, especially in overbought territory. +- **Divergence:** Look for divergences between the Stochastic and the price action. If the price is making a new high but the Stochastic is failing to do so (bearish divergence), it could signal weakening momentum and a potential reversal. +- **Caution:** The Fast Stochastic is highly sensitive and can produce many false signals ("whipsaws"), especially in choppy markets. It is often used by short-term traders or as a fast-reacting component in a larger trading system. For most applications, the "Slow" Stochastic is preferred due to its superior filtering of market noise. diff --git a/Indicators/MyIndicators/StochasticFast.mq5 b/Indicators/MyIndicators/StochasticFast.mq5 index b3983a2..2099a38 100644 --- a/Indicators/MyIndicators/StochasticFast.mq5 +++ b/Indicators/MyIndicators/StochasticFast.mq5 @@ -1,154 +1,154 @@ -//+------------------------------------------------------------------+ -//| StochasticFast.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Fast Stochastic Oscillator" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 2 // %K (Main) and %D (Signal) -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum 0.0 -#property indicator_maximum 100.0 - -//--- Plot 1: %K line (Fast) -#property indicator_label1 "%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpKPeriod = 14; // %K Period (Stochastic period) -input int InpDPeriod = 3; // %D Period (signal line smoothing) - -//--- Indicator Buffers --- -double BufferK[]; // Plotted buffer for the main %K line -double BufferD[]; // Plotted buffer for the signal %D line - -//--- Global Variables --- -int g_ExtKPeriod, g_ExtDPeriod; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; - g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; - -//--- Map the buffers and set as non-timeseries - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtDPeriod - 2); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fast Stoch(%d,%d)", g_ExtKPeriod, g_ExtDPeriod)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Fast Stochastic Oscillator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data - int start_pos = g_ExtKPeriod + g_ExtDPeriod - 1; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Calculate %K (Fast %K) - for(int i = g_ExtKPeriod - 1; i < rates_total; i++) - { - double highest_high = Highest(high, g_ExtKPeriod, i); - double lowest_low = Lowest(low, g_ExtKPeriod, i); - - double range = highest_high - lowest_low; - if(range > 0) - BufferK[i] = (close[i] - lowest_low) / range * 100.0; - else - BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; - } - -//--- STEP 2: Calculate %D (Signal Line) as an SMA of %K - int d_start_pos = g_ExtKPeriod + g_ExtDPeriod - 2; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtDPeriod; j++) - { - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtDPeriod; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochasticFast.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Fast Stochastic Oscillator" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 2 // %K (Main) and %D (Signal) +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum 0.0 +#property indicator_maximum 100.0 + +//--- Plot 1: %K line (Fast) +#property indicator_label1 "%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpKPeriod = 14; // %K Period (Stochastic period) +input int InpDPeriod = 3; // %D Period (signal line smoothing) + +//--- Indicator Buffers --- +double BufferK[]; // Plotted buffer for the main %K line +double BufferD[]; // Plotted buffer for the signal %D line + +//--- Global Variables --- +int g_ExtKPeriod, g_ExtDPeriod; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; + g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; + +//--- Map the buffers and set as non-timeseries + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtDPeriod - 2); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Fast Stoch(%d,%d)", g_ExtKPeriod, g_ExtDPeriod)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Fast Stochastic Oscillator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data + int start_pos = g_ExtKPeriod + g_ExtDPeriod - 1; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Calculate %K (Fast %K) + for(int i = g_ExtKPeriod - 1; i < rates_total; i++) + { + double highest_high = Highest(high, g_ExtKPeriod, i); + double lowest_low = Lowest(low, g_ExtKPeriod, i); + + double range = highest_high - lowest_low; + if(range > 0) + BufferK[i] = (close[i] - lowest_low) / range * 100.0; + else + BufferK[i] = (i > 0) ? BufferK[i-1] : 50.0; + } + +//--- STEP 2: Calculate %D (Signal Line) as an SMA of %K + int d_start_pos = g_ExtKPeriod + g_ExtDPeriod - 2; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtDPeriod; j++) + { + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtDPeriod; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochasticFast_HeikinAshi.mq5 b/Indicators/MyIndicators/StochasticFast_HeikinAshi.mq5 index 7e6f153..d75ca0d 100644 --- a/Indicators/MyIndicators/StochasticFast_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/StochasticFast_HeikinAshi.mq5 @@ -1,194 +1,194 @@ -//+------------------------------------------------------------------+ -//| StochasticFast_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Fast Stochastic Oscillator on Heikin Ashi data" - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 2 // %K (Main) and %D (Signal) -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum 0.0 -#property indicator_maximum 100.0 - -//--- Plot 1: %K line (Fast) -#property indicator_label1 "HA_%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "HA_%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpKPeriod = 14; // %K Period (Stochastic period) -input int InpDPeriod = 3; // %D Period (signal line smoothing) - -//--- Indicator Buffers --- -double BufferHA_K[]; // Plotted buffer for the main %K line -double BufferHA_D[]; // Plotted buffer for the signal %D line - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtKPeriod, g_ExtDPeriod; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; - g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; - -//--- Map the buffers and set as non-timeseries - SetIndexBuffer(0, BufferHA_K, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_D, INDICATOR_DATA); - ArraySetAsSeries(BufferHA_K, false); - ArraySetAsSeries(BufferHA_D, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod - 1); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtDPeriod - 2); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Fast_Stoch(%d,%d)", g_ExtKPeriod, g_ExtDPeriod)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Fast Stochastic on Heiken Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data - if(rates_total < g_ExtKPeriod + g_ExtDPeriod - 1) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate Raw %K using Heiken Ashi data - for(int i = g_ExtKPeriod - 1; i < rates_total; i++) - { - double highest_ha_high = Highest(ExtHaHighBuffer, g_ExtKPeriod, i); - double lowest_ha_low = Lowest(ExtHaLowBuffer, g_ExtKPeriod, i); - - double range = highest_ha_high - lowest_ha_low; - if(range > 0) - BufferHA_K[i] = (ExtHaCloseBuffer[i] - lowest_ha_low) / range * 100.0; - else - BufferHA_K[i] = (i > 0) ? BufferHA_K[i-1] : 50.0; - } - -//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K - int d_start_pos = g_ExtKPeriod + g_ExtDPeriod - 2; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtDPeriod; j++) - { - sum += BufferHA_K[i-j]; - } - BufferHA_D[i] = sum / g_ExtDPeriod; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochasticFast_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Fast Stochastic Oscillator on Heikin Ashi data" + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 2 // %K (Main) and %D (Signal) +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum 0.0 +#property indicator_maximum 100.0 + +//--- Plot 1: %K line (Fast) +#property indicator_label1 "HA_%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "HA_%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpKPeriod = 14; // %K Period (Stochastic period) +input int InpDPeriod = 3; // %D Period (signal line smoothing) + +//--- Indicator Buffers --- +double BufferHA_K[]; // Plotted buffer for the main %K line +double BufferHA_D[]; // Plotted buffer for the signal %D line + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtKPeriod, g_ExtDPeriod; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; + g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; + +//--- Map the buffers and set as non-timeseries + SetIndexBuffer(0, BufferHA_K, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_D, INDICATOR_DATA); + ArraySetAsSeries(BufferHA_K, false); + ArraySetAsSeries(BufferHA_D, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod - 1); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtDPeriod - 2); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Fast_Stoch(%d,%d)", g_ExtKPeriod, g_ExtDPeriod)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Fast Stochastic on Heiken Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data + if(rates_total < g_ExtKPeriod + g_ExtDPeriod - 1) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate Raw %K using Heiken Ashi data + for(int i = g_ExtKPeriod - 1; i < rates_total; i++) + { + double highest_ha_high = Highest(ExtHaHighBuffer, g_ExtKPeriod, i); + double lowest_ha_low = Lowest(ExtHaLowBuffer, g_ExtKPeriod, i); + + double range = highest_ha_high - lowest_ha_low; + if(range > 0) + BufferHA_K[i] = (ExtHaCloseBuffer[i] - lowest_ha_low) / range * 100.0; + else + BufferHA_K[i] = (i > 0) ? BufferHA_K[i-1] : 50.0; + } + +//--- STEP 3: Calculate %D (Signal Line) as an SMA of %K + int d_start_pos = g_ExtKPeriod + g_ExtDPeriod - 2; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtDPeriod; j++) + { + sum += BufferHA_K[i-j]; + } + BufferHA_D[i] = sum / g_ExtDPeriod; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochasticSlow.md b/Indicators/MyIndicators/StochasticSlow.md index 645c936..2e5c983 100644 --- a/Indicators/MyIndicators/StochasticSlow.md +++ b/Indicators/MyIndicators/StochasticSlow.md @@ -1,61 +1,61 @@ -# Slow Stochastic Oscillator - -## 1. Summary (Introduction) - -The Stochastic Oscillator, developed by George C. Lane in the late 1950s, is a momentum indicator that compares a particular closing price of a security to a range of its prices over a certain period of time. The "Slow" version is the most commonly used variant, as it includes an internal smoothing mechanism that filters out the "noise" of the more volatile "Fast" Stochastic, providing clearer signals. - -Its primary purpose is to identify overbought and oversold conditions and to spot potential trend reversals through divergences and line crossovers. - -## 2. Mathematical Foundations and Calculation Logic - -The Slow Stochastic is derived from the Fast Stochastic by adding an extra layer of smoothing. - -### Required Components - -- **%K Period:** The main lookback period for the Stochastic calculation. -- **Slowing Period:** The period for the first smoothing step, which transforms the "Fast %K" into the "Slow %K". -- **%D Period:** The period for the second smoothing step, which creates the signal line (%D) from the Slow %K. - -### Calculation Steps (Algorithm) - -1. **Calculate the Raw %K (Fast %K):** This is the core of the Stochastic calculation. It measures where the current close is relative to the highest high and lowest low over the `%K Period`. - $\text{Raw \%K}_i = \frac{\text{Close}_i - \text{Lowest Low}_{\%K \text{ Period}}}{\text{Highest High}_{\%K \text{ Period}} - \text{Lowest Low}_{\%K \text{ Period}}} \times 100$ - Where: - - - $\text{Lowest Low}$ is the minimum low price over the `%K Period`. - - $\text{Highest High}$ is the maximum high price over the `%K Period`. - -2. **Calculate the Slow %K (Main Line):** This is the key step that creates the "Slow" Stochastic. The Raw %K line is smoothed, typically with a Simple Moving Average (SMA), using the `Slowing Period`. This smoothed line becomes the main `%K` line of the Slow Stochastic. - $\text{Slow \%K}_i = \text{SMA}(\text{Raw \%K}, \text{Slowing Period})_i$ - -3. **Calculate the %D (Signal Line):** The signal line is a moving average of the Slow %K line, providing an additional layer of smoothing. - $\text{Slow \%D}_i = \text{SMA}(\text{Slow \%K}, \text{\%D Period})_i$ - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> Raw %K -> Slow %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. - -- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: - - 1. **Step 1:** The Raw %K (Fast %K) is calculated from the standard `high`, `low`, and `close` price arrays and stored in the `BufferRawK` calculation buffer. - 2. **Step 2:** The Slow %K (the main plot line) is calculated by applying a simple moving average with the `Slowing` period to `BufferRawK`. The result is stored in the `BufferK` plot buffer. - 3. **Step 3:** The %D signal line is calculated by applying a simple moving average with the `%D Period` to the already smoothed `BufferK`. The result is stored in the `BufferD` plot buffer. - -- **Heikin Ashi Variant (`StochasticSlow_HeikinAshi.mq5`):** - - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input instead of the standard price data. - - This results in a significantly smoother oscillator, as the input data itself is already filtered. This can be useful for traders who want to focus on the primary momentum shifts and filter out market noise. - -## 4. Parameters - -- **%K Period (`InpKPeriod`):** The lookback period for the initial Stochastic calculation (finding the highest high and lowest low). Default is `5`. -- **%D Period (`InpDPeriod`):** The smoothing period for the final signal line (%D). Default is `3`. -- **Slowing (`InpSlowing`):** The smoothing period applied to the Raw %K to create the main Slow %K line. A value of `1` would effectively result in a Fast Stochastic. Default is `3`. - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The primary use of the Stochastic is to identify overbought (typically above 80) and oversold (typically below 20) conditions. A move into these zones does not necessarily mean a reversal is imminent, but it indicates that the price is near the top or bottom of its recent trading range. -- **Crossovers:** The crossover of the %K line and the %D signal line is a common trade signal. A crossover of %K above %D is considered bullish, especially in oversold territory. A crossover of %K below %D is considered bearish, especially in overbought territory. -- **Divergence:** Look for divergences between the Stochastic and the price action. If the price is making a new high but the Stochastic is failing to do so (bearish divergence), it could signal weakening momentum and a potential reversal. Conversely, if the price makes a new low but the Stochastic makes a higher low (bullish divergence), it could signal a potential bottom. -- **Caution:** The Stochastic is a range-bound oscillator and performs best in sideways or choppy markets. In a strong trend, it can remain in overbought or oversold territory for extended periods, giving premature or false reversal signals. +# Slow Stochastic Oscillator + +## 1. Summary (Introduction) + +The Stochastic Oscillator, developed by George C. Lane in the late 1950s, is a momentum indicator that compares a particular closing price of a security to a range of its prices over a certain period of time. The "Slow" version is the most commonly used variant, as it includes an internal smoothing mechanism that filters out the "noise" of the more volatile "Fast" Stochastic, providing clearer signals. + +Its primary purpose is to identify overbought and oversold conditions and to spot potential trend reversals through divergences and line crossovers. + +## 2. Mathematical Foundations and Calculation Logic + +The Slow Stochastic is derived from the Fast Stochastic by adding an extra layer of smoothing. + +### Required Components + +- **%K Period:** The main lookback period for the Stochastic calculation. +- **Slowing Period:** The period for the first smoothing step, which transforms the "Fast %K" into the "Slow %K". +- **%D Period:** The period for the second smoothing step, which creates the signal line (%D) from the Slow %K. + +### Calculation Steps (Algorithm) + +1. **Calculate the Raw %K (Fast %K):** This is the core of the Stochastic calculation. It measures where the current close is relative to the highest high and lowest low over the `%K Period`. + $\text{Raw \%K}_i = \frac{\text{Close}_i - \text{Lowest Low}_{\%K \text{ Period}}}{\text{Highest High}_{\%K \text{ Period}} - \text{Lowest Low}_{\%K \text{ Period}}} \times 100$ + Where: + + - $\text{Lowest Low}$ is the minimum low price over the `%K Period`. + - $\text{Highest High}$ is the maximum high price over the `%K Period`. + +2. **Calculate the Slow %K (Main Line):** This is the key step that creates the "Slow" Stochastic. The Raw %K line is smoothed, typically with a Simple Moving Average (SMA), using the `Slowing Period`. This smoothed line becomes the main `%K` line of the Slow Stochastic. + $\text{Slow \%K}_i = \text{SMA}(\text{Raw \%K}, \text{Slowing Period})_i$ + +3. **Calculate the %D (Signal Line):** The signal line is a moving average of the Slow %K line, providing an additional layer of smoothing. + $\text{Slow \%D}_i = \text{SMA}(\text{Slow \%K}, \text{\%D Period})_i$ + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is designed for stability, clarity, and consistency with our existing indicator toolkit. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> Raw %K -> Slow %K -> %D) remains stable and accurate, especially during timeframe changes or history loading. + +- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps, each handled by a dedicated `for` loop. This improves code readability and makes the logic easy to follow: + + 1. **Step 1:** The Raw %K (Fast %K) is calculated from the standard `high`, `low`, and `close` price arrays and stored in the `BufferRawK` calculation buffer. + 2. **Step 2:** The Slow %K (the main plot line) is calculated by applying a simple moving average with the `Slowing` period to `BufferRawK`. The result is stored in the `BufferK` plot buffer. + 3. **Step 3:** The %D signal line is calculated by applying a simple moving average with the `%D Period` to the already smoothed `BufferK`. The result is stored in the `BufferD` plot buffer. + +- **Heikin Ashi Variant (`StochasticSlow_HeikinAshi.mq5`):** + - Our toolkit also includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input instead of the standard price data. + - This results in a significantly smoother oscillator, as the input data itself is already filtered. This can be useful for traders who want to focus on the primary momentum shifts and filter out market noise. + +## 4. Parameters + +- **%K Period (`InpKPeriod`):** The lookback period for the initial Stochastic calculation (finding the highest high and lowest low). Default is `5`. +- **%D Period (`InpDPeriod`):** The smoothing period for the final signal line (%D). Default is `3`. +- **Slowing (`InpSlowing`):** The smoothing period applied to the Raw %K to create the main Slow %K line. A value of `1` would effectively result in a Fast Stochastic. Default is `3`. + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The primary use of the Stochastic is to identify overbought (typically above 80) and oversold (typically below 20) conditions. A move into these zones does not necessarily mean a reversal is imminent, but it indicates that the price is near the top or bottom of its recent trading range. +- **Crossovers:** The crossover of the %K line and the %D signal line is a common trade signal. A crossover of %K above %D is considered bullish, especially in oversold territory. A crossover of %K below %D is considered bearish, especially in overbought territory. +- **Divergence:** Look for divergences between the Stochastic and the price action. If the price is making a new high but the Stochastic is failing to do so (bearish divergence), it could signal weakening momentum and a potential reversal. Conversely, if the price makes a new low but the Stochastic makes a higher low (bullish divergence), it could signal a potential bottom. +- **Caution:** The Stochastic is a range-bound oscillator and performs best in sideways or choppy markets. In a strong trend, it can remain in overbought or oversold territory for extended periods, giving premature or false reversal signals. diff --git a/Indicators/MyIndicators/StochasticSlow.mq5 b/Indicators/MyIndicators/StochasticSlow.mq5 index 0ff801c..0b0d882 100644 --- a/Indicators/MyIndicators/StochasticSlow.mq5 +++ b/Indicators/MyIndicators/StochasticSlow.mq5 @@ -1,172 +1,172 @@ -//+------------------------------------------------------------------+ -//| StochasticSlow.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Slow Stochastic Oscillator" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 3 // %K, %D, and Raw %K for calculation -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum 0.0 -#property indicator_maximum 100.0 - -//--- Plot 1: %K line (Slow) -#property indicator_label1 "%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpKPeriod = 5; // %K Period -input int InpDPeriod = 3; // %D Period (signal line smoothing) -input int InpSlowing = 3; // Slowing (initial %K smoothing) - -//--- Indicator Buffers --- -double BufferK[]; // Plotted buffer for the main (Slow) %K line -double BufferD[]; // Plotted buffer for the signal %D line -double BufferRawK[]; // Calculation buffer for raw %K before slowing - -//--- Global Variables --- -int g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; - g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; - g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; - -//--- Map the buffers and set as non-timeseries - SetIndexBuffer(0, BufferK, INDICATOR_DATA); - SetIndexBuffer(1, BufferD, INDICATOR_DATA); - SetIndexBuffer(2, BufferRawK, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferK, false); - ArraySetAsSeries(BufferD, false); - ArraySetAsSeries(BufferRawK, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing - 2); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Slow Stoch(%d,%d,%d)", g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Slow Stochastic Oscillator calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data - int start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 2; - if(rates_total <= start_pos) - return(0); - -//--- STEP 1: Calculate Raw %K (Fast %K) - for(int i = g_ExtKPeriod - 1; i < rates_total; i++) - { - double highest_high = Highest(high, g_ExtKPeriod, i); - double lowest_low = Lowest(low, g_ExtKPeriod, i); - - double range = highest_high - lowest_low; - if(range > 0) - BufferRawK[i] = (close[i] - lowest_low) / range * 100.0; - else - BufferRawK[i] = (i > 0) ? BufferRawK[i-1] : 50.0; - } - -//--- STEP 2: Calculate Slow %K (Main Line) by smoothing Raw %K - int k_slow_start_pos = g_ExtKPeriod + g_ExtSlowing - 2; - for(int i = k_slow_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSlowing; j++) - { - sum += BufferRawK[i-j]; - } - BufferK[i] = sum / g_ExtSlowing; - } - -//--- STEP 3: Calculate %D (Signal Line) by smoothing Slow %K - int d_start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtDPeriod; j++) - { - sum += BufferK[i-j]; - } - BufferD[i] = sum / g_ExtDPeriod; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochasticSlow.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Slow Stochastic Oscillator" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 3 // %K, %D, and Raw %K for calculation +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum 0.0 +#property indicator_maximum 100.0 + +//--- Plot 1: %K line (Slow) +#property indicator_label1 "%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpKPeriod = 5; // %K Period +input int InpDPeriod = 3; // %D Period (signal line smoothing) +input int InpSlowing = 3; // Slowing (initial %K smoothing) + +//--- Indicator Buffers --- +double BufferK[]; // Plotted buffer for the main (Slow) %K line +double BufferD[]; // Plotted buffer for the signal %D line +double BufferRawK[]; // Calculation buffer for raw %K before slowing + +//--- Global Variables --- +int g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; + g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; + g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; + +//--- Map the buffers and set as non-timeseries + SetIndexBuffer(0, BufferK, INDICATOR_DATA); + SetIndexBuffer(1, BufferD, INDICATOR_DATA); + SetIndexBuffer(2, BufferRawK, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferK, false); + ArraySetAsSeries(BufferD, false); + ArraySetAsSeries(BufferRawK, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing - 2); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Slow Stoch(%d,%d,%d)", g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Slow Stochastic Oscillator calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data + int start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 2; + if(rates_total <= start_pos) + return(0); + +//--- STEP 1: Calculate Raw %K (Fast %K) + for(int i = g_ExtKPeriod - 1; i < rates_total; i++) + { + double highest_high = Highest(high, g_ExtKPeriod, i); + double lowest_low = Lowest(low, g_ExtKPeriod, i); + + double range = highest_high - lowest_low; + if(range > 0) + BufferRawK[i] = (close[i] - lowest_low) / range * 100.0; + else + BufferRawK[i] = (i > 0) ? BufferRawK[i-1] : 50.0; + } + +//--- STEP 2: Calculate Slow %K (Main Line) by smoothing Raw %K + int k_slow_start_pos = g_ExtKPeriod + g_ExtSlowing - 2; + for(int i = k_slow_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSlowing; j++) + { + sum += BufferRawK[i-j]; + } + BufferK[i] = sum / g_ExtSlowing; + } + +//--- STEP 3: Calculate %D (Signal Line) by smoothing Slow %K + int d_start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtDPeriod; j++) + { + sum += BufferK[i-j]; + } + BufferD[i] = sum / g_ExtDPeriod; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/StochasticSlow_HeikinAshi.mq5 b/Indicators/MyIndicators/StochasticSlow_HeikinAshi.mq5 index be834c5..20f0dcc 100644 --- a/Indicators/MyIndicators/StochasticSlow_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/StochasticSlow_HeikinAshi.mq5 @@ -1,211 +1,211 @@ -//+------------------------------------------------------------------+ -//| StochasticSlow_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Slow Stochastic Oscillator on Heikin Ashi data" - -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_buffers 3 // %K, %D, and Raw %K for calculation -#property indicator_plots 2 -#property indicator_level1 20.0 -#property indicator_level2 80.0 -#property indicator_minimum 0.0 -#property indicator_maximum 100.0 - -//--- Plot 1: %K line (Slow) -#property indicator_label1 "HA_%K" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrLightSeaGreen -#property indicator_style1 STYLE_SOLID -#property indicator_width1 1 - -//--- Plot 2: %D line (Signal) -#property indicator_label2 "HA_%D" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrRed -#property indicator_style2 STYLE_DOT -#property indicator_width2 1 - -//--- Input Parameters --- -input int InpKPeriod = 5; // %K Period -input int InpDPeriod = 3; // %D Period (signal line smoothing) -input int InpSlowing = 3; // Slowing (initial %K smoothing) - -//--- Indicator Buffers --- -double BufferHA_K[]; // Plotted buffer for the main (Slow) %K line -double BufferHA_D[]; // Plotted buffer for the signal %D line -double BufferRawK[]; // Calculation buffer for raw %K before slowing - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; - g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; - g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; - -//--- Map the buffers and set as non-timeseries - SetIndexBuffer(0, BufferHA_K, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_D, INDICATOR_DATA); - SetIndexBuffer(2, BufferRawK, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferHA_K, false); - ArraySetAsSeries(BufferHA_D, false); - ArraySetAsSeries(BufferRawK, false); - -//--- Set indicator display properties - IndicatorSetInteger(INDICATOR_DIGITS, 2); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing - 2); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Slow_Stoch(%d,%d,%d)", g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Slow Stochastic on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data - if(rates_total < g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 2) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate Raw %K (Fast %K) using Heiken Ashi data - for(int i = g_ExtKPeriod - 1; i < rates_total; i++) - { - double highest_ha_high = Highest(ExtHaHighBuffer, g_ExtKPeriod, i); - double lowest_ha_low = Lowest(ExtHaLowBuffer, g_ExtKPeriod, i); - - double range = highest_ha_high - lowest_ha_low; - if(range > 0) - BufferRawK[i] = (ExtHaCloseBuffer[i] - lowest_ha_low) / range * 100.0; - else - BufferRawK[i] = (i > 0) ? BufferRawK[i-1] : 50.0; - } - -//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K - int k_slow_start_pos = g_ExtKPeriod + g_ExtSlowing - 2; - for(int i = k_slow_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtSlowing; j++) - { - sum += BufferRawK[i-j]; - } - BufferHA_K[i] = sum / g_ExtSlowing; - } - -//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K - int d_start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3; - for(int i = d_start_pos; i < rates_total; i++) - { - double sum = 0; - for(int j = 0; j < g_ExtDPeriod; j++) - { - sum += BufferHA_K[i-j]; - } - BufferHA_D[i] = sum / g_ExtDPeriod; - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| StochasticSlow_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Slow Stochastic Oscillator on Heikin Ashi data" + +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_buffers 3 // %K, %D, and Raw %K for calculation +#property indicator_plots 2 +#property indicator_level1 20.0 +#property indicator_level2 80.0 +#property indicator_minimum 0.0 +#property indicator_maximum 100.0 + +//--- Plot 1: %K line (Slow) +#property indicator_label1 "HA_%K" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrLightSeaGreen +#property indicator_style1 STYLE_SOLID +#property indicator_width1 1 + +//--- Plot 2: %D line (Signal) +#property indicator_label2 "HA_%D" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrRed +#property indicator_style2 STYLE_DOT +#property indicator_width2 1 + +//--- Input Parameters --- +input int InpKPeriod = 5; // %K Period +input int InpDPeriod = 3; // %D Period (signal line smoothing) +input int InpSlowing = 3; // Slowing (initial %K smoothing) + +//--- Indicator Buffers --- +double BufferHA_K[]; // Plotted buffer for the main (Slow) %K line +double BufferHA_D[]; // Plotted buffer for the signal %D line +double BufferRawK[]; // Calculation buffer for raw %K before slowing + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtKPeriod = (InpKPeriod < 1) ? 1 : InpKPeriod; + g_ExtDPeriod = (InpDPeriod < 1) ? 1 : InpDPeriod; + g_ExtSlowing = (InpSlowing < 1) ? 1 : InpSlowing; + +//--- Map the buffers and set as non-timeseries + SetIndexBuffer(0, BufferHA_K, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_D, INDICATOR_DATA); + SetIndexBuffer(2, BufferRawK, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferHA_K, false); + ArraySetAsSeries(BufferHA_D, false); + ArraySetAsSeries(BufferRawK, false); + +//--- Set indicator display properties + IndicatorSetInteger(INDICATOR_DIGITS, 2); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing - 2); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Slow_Stoch(%d,%d,%d)", g_ExtKPeriod, g_ExtDPeriod, g_ExtSlowing)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Slow Stochastic on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data + if(rates_total < g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 2) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate Raw %K (Fast %K) using Heiken Ashi data + for(int i = g_ExtKPeriod - 1; i < rates_total; i++) + { + double highest_ha_high = Highest(ExtHaHighBuffer, g_ExtKPeriod, i); + double lowest_ha_low = Lowest(ExtHaLowBuffer, g_ExtKPeriod, i); + + double range = highest_ha_high - lowest_ha_low; + if(range > 0) + BufferRawK[i] = (ExtHaCloseBuffer[i] - lowest_ha_low) / range * 100.0; + else + BufferRawK[i] = (i > 0) ? BufferRawK[i-1] : 50.0; + } + +//--- STEP 3: Calculate Slow %K (Main Line) by smoothing Raw %K + int k_slow_start_pos = g_ExtKPeriod + g_ExtSlowing - 2; + for(int i = k_slow_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtSlowing; j++) + { + sum += BufferRawK[i-j]; + } + BufferHA_K[i] = sum / g_ExtSlowing; + } + +//--- STEP 4: Calculate %D (Signal Line) by smoothing Slow %K + int d_start_pos = g_ExtKPeriod + g_ExtSlowing + g_ExtDPeriod - 3; + for(int i = d_start_pos; i < rates_total; i++) + { + double sum = 0; + for(int j = 0; j < g_ExtDPeriod; j++) + { + sum += BufferHA_K[i-j]; + } + BufferHA_D[i] = sum / g_ExtDPeriod; + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Supertrend.md b/Indicators/MyIndicators/Supertrend.md index abe0f81..e63b5fa 100644 --- a/Indicators/MyIndicators/Supertrend.md +++ b/Indicators/MyIndicators/Supertrend.md @@ -1,85 +1,85 @@ -# Supertrend Indicator - -## 1. Summary (Introduction) - -The Supertrend indicator was developed by Olivier Seban to identify the primary trend of a financial instrument. It is a popular tool among traders due to its simplicity and clear visual representation of trend direction. Plotted directly on the price chart, the Supertrend line changes color and position based on the market's trend, acting as a dynamic level of support or resistance. - -## 2. Mathematical Foundations and Calculation Logic - -The Supertrend indicator combines a measure of volatility (Average True Range - ATR) with a central price point (typically the median price) to construct upper and lower bands. The final Supertrend line then follows one of these bands based on the current trend direction. - -### Required Components - -- **ATR (Average True Range):** A measure of market volatility. A higher ATR indicates a more volatile market. -- **Factor (Multiplier):** A user-defined multiplier that adjusts the sensitivity of the bands. A larger factor creates wider bands, resulting in fewer but potentially more reliable signals. -- **Median Price (hl2):** The central point for the bands, calculated as `(High + Low) / 2`. - -### Calculation Steps (Algorithm) - -1. **Calculate the Average True Range (ATR)** for a given period. - -2. **Calculate the Basic Upper and Lower Bands:** These are the initial, unadjusted bands around the median price. - - $$ - \text{Upper Basic Band} = \frac{\text{High} + \text{Low}}{2} + (\text{Factor} \times \text{ATR}) - $$ - - $$ - \text{Lower Basic Band} = \frac{\text{High} + \text{Low}}{2} - (\text{Factor} \times \text{ATR}) - $$ - -3. **Calculate the Final Upper and Lower Bands:** This step creates the characteristic "stair-step" appearance of the indicator. The logic ensures that the bands never move against the trend (i.e., the upper band can only move down or stay flat, and the lower band can only move up or stay flat). - - $$ - \text{Final Upper Band}_i = - \begin{cases} - \text{Upper Basic Band}_i & \text{if } \text{Upper Basic Band}_i < \text{Final Upper Band}_{i-1} \text{ or } \text{Close}_{i-1} > \text{Final Upper Band}_{i-1} \\ - \text{Final Upper Band}_{i-1} & \text{otherwise} - \end{cases} - $$ - - $$ - \text{Final Lower Band}_i = - \begin{cases} - \text{Lower Basic Band}_i & \text{if } \text{Lower Basic Band}_i > \text{Final Lower Band}_{i-1} \text{ or } \text{Close}_{i-1} < \text{Final Lower Band}_{i-1} \\ - \text{Final Lower Band}_{i-1} & \text{otherwise} - \end{cases} - $$ - -4. **Determine the Trend Direction:** The trend is determined by comparing the closing price to the opposite band. - - - If the previous trend was **up**, the trend flips to **down** if the current close crosses below the **Final Lower Band**. - - If the previous trend was **down**, the trend flips to **up** if the current close crosses above the **Final Upper Band**. - - If neither condition is met, the trend continues. - -5. **Plot the Supertrend Line:** - - If the trend is **up**, the Supertrend line is plotted at the level of the **Final Lower Band**. - - If the trend is **down**, the Supertrend line is plotted at the level of the **Final Upper Band**. - -## 3. MQL5 Implementation Details - -Our MQL5 implementation was refactored based on our core principles to ensure maximum stability and code clarity. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like Supertrend, this approach is far more robust than using `prev_calculated` logic, as it prevents calculation errors during timeframe changes or history loading. - -- **State Management:** A dedicated calculation buffer, `BufferTrend[]`, is used to explicitly store the current trend direction (`1` for up, `-1` for down). This makes the trend-switching logic clean, readable, and less prone to errors. - -- **Robust Initialization:** The trend is explicitly initialized on the first valid bar (`i == g_ExtAtrPeriod`). The initial direction is determined by comparing the closing price to the median price (`hl2`), providing a stable starting point for the recursive logic. - -- **Visual Representation:** We opted for a "connected line" visualization for trend changes. When a trend flip occurs, the value of the previous bar's Supertrend line is overwritten to match the new bar's value. This creates a vertical line connecting the old and new trend lines, ensuring continuous visual information. - -- **Heikin Ashi Variants:** Our toolkit includes two Heikin Ashi versions of the Supertrend indicator, offering different perspectives on the trend. - - **Hybrid Version (`Supertrend_HeikinAshi.mq5`):** This version calculates the bands using the smoothed Heikin Ashi median price (`ha_hl2`) and closing price (`ha_close`) but utilizes the **standard ATR** calculated from regular candlesticks. This approach combines the smoothed trend signal of Heikin Ashi with the "true" market volatility. It may be preferred by traders looking for signals based on real volatility spikes. - - **"Pure" Version (`Supertrend_HeikinAshi_Pure.mq5`):** This version is based entirely on Heikin Ashi data. The ATR is manually calculated from the Heikin Ashi High, Low, and Close values. The result is a fully smoothed indicator where both the trend and the volatility component are filtered. This may be preferred by trend-following traders seeking a less "noisy" signal. - -## 4. Parameters - -- **ATR Period (`InpAtrPeriod`):** The lookback period for the Average True Range calculation. A shorter period makes the indicator more sensitive to recent volatility, while a longer period provides a smoother, less reactive line. Default is `10`. -- **Factor (`InpFactor`):** The multiplier applied to the ATR value. A smaller factor brings the line closer to the price, resulting in more frequent signals. A larger factor moves the line further away, filtering out minor price fluctuations and producing fewer signals. Default is `3.0`. - -## 5. Usage and Interpretation - -- **Trend Identification:** The primary use of the Supertrend is to identify the current market trend. A green line below the price indicates an uptrend, while a red line above the price indicates a downtrend. -- **Dynamic Support and Resistance:** In an uptrend, the green line often acts as a dynamic support level. In a downtrend, the red line acts as a dynamic resistance level. -- **Trade Signals:** A change in the indicator's color can be interpreted as a trade signal. A flip from red to green suggests a potential buy signal, while a flip from green to red suggests a potential sell signal. -- **Caution:** Like all trend-following indicators, Supertrend is most effective in trending markets. In sideways or ranging markets, it can produce frequent false signals ("whipsaws"). It is highly recommended to use it in conjunction with other indicators or forms of analysis for confirmation. +# Supertrend Indicator + +## 1. Summary (Introduction) + +The Supertrend indicator was developed by Olivier Seban to identify the primary trend of a financial instrument. It is a popular tool among traders due to its simplicity and clear visual representation of trend direction. Plotted directly on the price chart, the Supertrend line changes color and position based on the market's trend, acting as a dynamic level of support or resistance. + +## 2. Mathematical Foundations and Calculation Logic + +The Supertrend indicator combines a measure of volatility (Average True Range - ATR) with a central price point (typically the median price) to construct upper and lower bands. The final Supertrend line then follows one of these bands based on the current trend direction. + +### Required Components + +- **ATR (Average True Range):** A measure of market volatility. A higher ATR indicates a more volatile market. +- **Factor (Multiplier):** A user-defined multiplier that adjusts the sensitivity of the bands. A larger factor creates wider bands, resulting in fewer but potentially more reliable signals. +- **Median Price (hl2):** The central point for the bands, calculated as `(High + Low) / 2`. + +### Calculation Steps (Algorithm) + +1. **Calculate the Average True Range (ATR)** for a given period. + +2. **Calculate the Basic Upper and Lower Bands:** These are the initial, unadjusted bands around the median price. + + $$ + \text{Upper Basic Band} = \frac{\text{High} + \text{Low}}{2} + (\text{Factor} \times \text{ATR}) + $$ + + $$ + \text{Lower Basic Band} = \frac{\text{High} + \text{Low}}{2} - (\text{Factor} \times \text{ATR}) + $$ + +3. **Calculate the Final Upper and Lower Bands:** This step creates the characteristic "stair-step" appearance of the indicator. The logic ensures that the bands never move against the trend (i.e., the upper band can only move down or stay flat, and the lower band can only move up or stay flat). + + $$ + \text{Final Upper Band}_i = + \begin{cases} + \text{Upper Basic Band}_i & \text{if } \text{Upper Basic Band}_i < \text{Final Upper Band}_{i-1} \text{ or } \text{Close}_{i-1} > \text{Final Upper Band}_{i-1} \\ + \text{Final Upper Band}_{i-1} & \text{otherwise} + \end{cases} + $$ + + $$ + \text{Final Lower Band}_i = + \begin{cases} + \text{Lower Basic Band}_i & \text{if } \text{Lower Basic Band}_i > \text{Final Lower Band}_{i-1} \text{ or } \text{Close}_{i-1} < \text{Final Lower Band}_{i-1} \\ + \text{Final Lower Band}_{i-1} & \text{otherwise} + \end{cases} + $$ + +4. **Determine the Trend Direction:** The trend is determined by comparing the closing price to the opposite band. + + - If the previous trend was **up**, the trend flips to **down** if the current close crosses below the **Final Lower Band**. + - If the previous trend was **down**, the trend flips to **up** if the current close crosses above the **Final Upper Band**. + - If neither condition is met, the trend continues. + +5. **Plot the Supertrend Line:** + - If the trend is **up**, the Supertrend line is plotted at the level of the **Final Lower Band**. + - If the trend is **down**, the Supertrend line is plotted at the level of the **Final Upper Band**. + +## 3. MQL5 Implementation Details + +Our MQL5 implementation was refactored based on our core principles to ensure maximum stability and code clarity. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a state-dependent indicator like Supertrend, this approach is far more robust than using `prev_calculated` logic, as it prevents calculation errors during timeframe changes or history loading. + +- **State Management:** A dedicated calculation buffer, `BufferTrend[]`, is used to explicitly store the current trend direction (`1` for up, `-1` for down). This makes the trend-switching logic clean, readable, and less prone to errors. + +- **Robust Initialization:** The trend is explicitly initialized on the first valid bar (`i == g_ExtAtrPeriod`). The initial direction is determined by comparing the closing price to the median price (`hl2`), providing a stable starting point for the recursive logic. + +- **Visual Representation:** We opted for a "connected line" visualization for trend changes. When a trend flip occurs, the value of the previous bar's Supertrend line is overwritten to match the new bar's value. This creates a vertical line connecting the old and new trend lines, ensuring continuous visual information. + +- **Heikin Ashi Variants:** Our toolkit includes two Heikin Ashi versions of the Supertrend indicator, offering different perspectives on the trend. + - **Hybrid Version (`Supertrend_HeikinAshi.mq5`):** This version calculates the bands using the smoothed Heikin Ashi median price (`ha_hl2`) and closing price (`ha_close`) but utilizes the **standard ATR** calculated from regular candlesticks. This approach combines the smoothed trend signal of Heikin Ashi with the "true" market volatility. It may be preferred by traders looking for signals based on real volatility spikes. + - **"Pure" Version (`Supertrend_HeikinAshi_Pure.mq5`):** This version is based entirely on Heikin Ashi data. The ATR is manually calculated from the Heikin Ashi High, Low, and Close values. The result is a fully smoothed indicator where both the trend and the volatility component are filtered. This may be preferred by trend-following traders seeking a less "noisy" signal. + +## 4. Parameters + +- **ATR Period (`InpAtrPeriod`):** The lookback period for the Average True Range calculation. A shorter period makes the indicator more sensitive to recent volatility, while a longer period provides a smoother, less reactive line. Default is `10`. +- **Factor (`InpFactor`):** The multiplier applied to the ATR value. A smaller factor brings the line closer to the price, resulting in more frequent signals. A larger factor moves the line further away, filtering out minor price fluctuations and producing fewer signals. Default is `3.0`. + +## 5. Usage and Interpretation + +- **Trend Identification:** The primary use of the Supertrend is to identify the current market trend. A green line below the price indicates an uptrend, while a red line above the price indicates a downtrend. +- **Dynamic Support and Resistance:** In an uptrend, the green line often acts as a dynamic support level. In a downtrend, the red line acts as a dynamic resistance level. +- **Trade Signals:** A change in the indicator's color can be interpreted as a trade signal. A flip from red to green suggests a potential buy signal, while a flip from green to red suggests a potential sell signal. +- **Caution:** Like all trend-following indicators, Supertrend is most effective in trending markets. In sideways or ranging markets, it can produce frequent false signals ("whipsaws"). It is highly recommended to use it in conjunction with other indicators or forms of analysis for confirmation. diff --git a/Indicators/MyIndicators/Supertrend.mq5 b/Indicators/MyIndicators/Supertrend.mq5 index d3513d5..12b3f54 100644 --- a/Indicators/MyIndicators/Supertrend.mq5 +++ b/Indicators/MyIndicators/Supertrend.mq5 @@ -1,169 +1,169 @@ -//+------------------------------------------------------------------+ -//| Supertrend.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and robust logic -#property description "Supertrend Indicator for trend identification" - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 6 // Supertrend, Color, ATR, Upper, Lower, Trend -#property indicator_plots 1 - -//--- Plot 1: Supertrend line -#property indicator_label1 "Supertrend" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrLimeGreen, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAtrPeriod = 10; -input double InpFactor = 3.0; - -//--- Indicator Buffers --- -double BufferSupertrend[]; -double BufferColor[]; -double BufferATR[]; -double BufferUpperBand[]; -double BufferLowerBand[]; -double BufferTrend[]; - -//--- Global Variables --- -int g_ExtAtrPeriod; -double g_ExtFactor; -int g_handle_atr; - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; - - SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferSupertrend, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferATR, false); - ArraySetAsSeries(BufferUpperBand, false); - ArraySetAsSeries(BufferLowerBand, false); - ArraySetAsSeries(BufferTrend, false); - - g_handle_atr = iATR(_Symbol, _Period, g_ExtAtrPeriod); - if(g_handle_atr == INVALID_HANDLE) - { - Print("Error creating iATR handle."); - return(INIT_FAILED); - } - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Supertrend(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Release the indicator handle - IndicatorRelease(g_handle_atr); - } - -//+------------------------------------------------------------------+ -//| Supertrend calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- STEP 1: Get ATR values - if(CopyBuffer(g_handle_atr, 0, 0, rates_total, BufferATR) < rates_total) - { - Print("Error copying iATR buffer data."); - } - -//--- STEP 2: Main calculation loop with robust initialization - for(int i = 1; i < rates_total; i++) - { - double hl2 = (high[i] + low[i]) / 2.0; - double atr_val = g_ExtFactor * BufferATR[i]; - - //--- Calculate basic upper and lower bands - double upper_basic = hl2 + atr_val; - double lower_basic = hl2 - atr_val; - - //--- Final upper band (stair-step logic) - if(upper_basic < BufferUpperBand[i-1] || close[i-1] > BufferUpperBand[i-1]) - BufferUpperBand[i] = upper_basic; - else - BufferUpperBand[i] = BufferUpperBand[i-1]; - - //--- Final lower band (stair-step logic) - if(lower_basic > BufferLowerBand[i-1] || close[i-1] < BufferLowerBand[i-1]) - BufferLowerBand[i] = lower_basic; - else - BufferLowerBand[i] = BufferLowerBand[i-1]; - - //--- Determine trend direction - if(i == g_ExtAtrPeriod) // Explicit trend initialization - { - if(close[i] > hl2) - BufferTrend[i] = 1; - else - BufferTrend[i] = -1; - } - else - if(i > g_ExtAtrPeriod) // Subsequent points - { - if(BufferTrend[i-1] == 1 && close[i] < BufferLowerBand[i]) - BufferTrend[i] = -1; // Trend changed to down - else - if(BufferTrend[i-1] == -1 && close[i] > BufferUpperBand[i]) - BufferTrend[i] = 1; // Trend changed to up - else - BufferTrend[i] = BufferTrend[i-1]; // Trend continues - } - - //--- Set the final Supertrend value and color, and connect lines on change - if(BufferTrend[i] == 1) // Uptrend - { - BufferSupertrend[i] = BufferLowerBand[i]; - BufferColor[i] = 0; - if(BufferTrend[i-1] == -1) - BufferSupertrend[i-1] = BufferLowerBand[i]; - } - else // Downtrend - { - BufferSupertrend[i] = BufferUpperBand[i]; - BufferColor[i] = 1; - if(BufferTrend[i-1] == 1) - BufferSupertrend[i-1] = BufferUpperBand[i]; - } - } - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Supertrend.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and robust logic +#property description "Supertrend Indicator for trend identification" + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 6 // Supertrend, Color, ATR, Upper, Lower, Trend +#property indicator_plots 1 + +//--- Plot 1: Supertrend line +#property indicator_label1 "Supertrend" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrLimeGreen, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAtrPeriod = 10; +input double InpFactor = 3.0; + +//--- Indicator Buffers --- +double BufferSupertrend[]; +double BufferColor[]; +double BufferATR[]; +double BufferUpperBand[]; +double BufferLowerBand[]; +double BufferTrend[]; + +//--- Global Variables --- +int g_ExtAtrPeriod; +double g_ExtFactor; +int g_handle_atr; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; + + SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferSupertrend, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferATR, false); + ArraySetAsSeries(BufferUpperBand, false); + ArraySetAsSeries(BufferLowerBand, false); + ArraySetAsSeries(BufferTrend, false); + + g_handle_atr = iATR(_Symbol, _Period, g_ExtAtrPeriod); + if(g_handle_atr == INVALID_HANDLE) + { + Print("Error creating iATR handle."); + return(INIT_FAILED); + } + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Supertrend(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Release the indicator handle + IndicatorRelease(g_handle_atr); + } + +//+------------------------------------------------------------------+ +//| Supertrend calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- STEP 1: Get ATR values + if(CopyBuffer(g_handle_atr, 0, 0, rates_total, BufferATR) < rates_total) + { + Print("Error copying iATR buffer data."); + } + +//--- STEP 2: Main calculation loop with robust initialization + for(int i = 1; i < rates_total; i++) + { + double hl2 = (high[i] + low[i]) / 2.0; + double atr_val = g_ExtFactor * BufferATR[i]; + + //--- Calculate basic upper and lower bands + double upper_basic = hl2 + atr_val; + double lower_basic = hl2 - atr_val; + + //--- Final upper band (stair-step logic) + if(upper_basic < BufferUpperBand[i-1] || close[i-1] > BufferUpperBand[i-1]) + BufferUpperBand[i] = upper_basic; + else + BufferUpperBand[i] = BufferUpperBand[i-1]; + + //--- Final lower band (stair-step logic) + if(lower_basic > BufferLowerBand[i-1] || close[i-1] < BufferLowerBand[i-1]) + BufferLowerBand[i] = lower_basic; + else + BufferLowerBand[i] = BufferLowerBand[i-1]; + + //--- Determine trend direction + if(i == g_ExtAtrPeriod) // Explicit trend initialization + { + if(close[i] > hl2) + BufferTrend[i] = 1; + else + BufferTrend[i] = -1; + } + else + if(i > g_ExtAtrPeriod) // Subsequent points + { + if(BufferTrend[i-1] == 1 && close[i] < BufferLowerBand[i]) + BufferTrend[i] = -1; // Trend changed to down + else + if(BufferTrend[i-1] == -1 && close[i] > BufferUpperBand[i]) + BufferTrend[i] = 1; // Trend changed to up + else + BufferTrend[i] = BufferTrend[i-1]; // Trend continues + } + + //--- Set the final Supertrend value and color, and connect lines on change + if(BufferTrend[i] == 1) // Uptrend + { + BufferSupertrend[i] = BufferLowerBand[i]; + BufferColor[i] = 0; + if(BufferTrend[i-1] == -1) + BufferSupertrend[i-1] = BufferLowerBand[i]; + } + else // Downtrend + { + BufferSupertrend[i] = BufferUpperBand[i]; + BufferColor[i] = 1; + if(BufferTrend[i-1] == 1) + BufferSupertrend[i-1] = BufferUpperBand[i]; + } + } + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Supertrend_HeikinAshi.mq5 b/Indicators/MyIndicators/Supertrend_HeikinAshi.mq5 index 1403289..124baea 100644 --- a/Indicators/MyIndicators/Supertrend_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/Supertrend_HeikinAshi.mq5 @@ -1,201 +1,201 @@ -//+------------------------------------------------------------------+ -//| Supertrend_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for full recalculation and stability -#property description "Supertrend Indicator on Heikin Ashi data" - -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 6 // Supertrend, Color, ATR, Upper, Lower, Trend -#property indicator_plots 1 - -//--- Plot 1: Supertrend line -#property indicator_label1 "HA_Supertrend" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrLimeGreen, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAtrPeriod = 10; -input double InpFactor = 3.0; - -//--- Indicator Buffers --- -double BufferSupertrend[]; -double BufferColor[]; -double BufferATR[]; -double BufferUpperBand[]; -double BufferLowerBand[]; -double BufferTrend[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtAtrPeriod; -double g_ExtFactor; -int g_handle_atr; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; - - SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferSupertrend, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferATR, false); - ArraySetAsSeries(BufferUpperBand, false); - ArraySetAsSeries(BufferLowerBand, false); - ArraySetAsSeries(BufferTrend, false); - -// ATR is calculated on standard candles for true volatility - g_handle_atr = iATR(_Symbol, _Period, g_ExtAtrPeriod); - if(g_handle_atr == INVALID_HANDLE) - { - Print("Error creating iATR handle."); - return(INIT_FAILED); - } - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // For trend change gaps - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Supertrend(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - delete g_ha_calculator; - IndicatorRelease(g_handle_atr); - } - -//+------------------------------------------------------------------+ -//| Supertrend on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Get ATR values - if(CopyBuffer(g_handle_atr, 0, 0, rates_total, BufferATR) < rates_total) - { - Print("Error copying iATR buffer data."); - } - -//--- STEP 3: Main calculation loop with robust initialization - for(int i = 1; i < rates_total; i++) - { - double ha_hl2 = (ExtHaHighBuffer[i] + ExtHaLowBuffer[i]) / 2.0; - double atr_val = g_ExtFactor * BufferATR[i]; - - double upper_basic = ha_hl2 + atr_val; - double lower_basic = ha_hl2 - atr_val; - - if(upper_basic < BufferUpperBand[i-1] || ExtHaCloseBuffer[i-1] > BufferUpperBand[i-1]) - BufferUpperBand[i] = upper_basic; - else - BufferUpperBand[i] = BufferUpperBand[i-1]; - - if(lower_basic > BufferLowerBand[i-1] || ExtHaCloseBuffer[i-1] < BufferLowerBand[i-1]) - BufferLowerBand[i] = lower_basic; - else - BufferLowerBand[i] = BufferLowerBand[i-1]; - - if(i == g_ExtAtrPeriod) - { - if(ExtHaCloseBuffer[i] > ha_hl2) - BufferTrend[i] = 1; - else - BufferTrend[i] = -1; - } - else - if(i > g_ExtAtrPeriod) - { - if(BufferTrend[i-1] == 1 && ExtHaCloseBuffer[i] < BufferLowerBand[i]) - BufferTrend[i] = -1; - else - if(BufferTrend[i-1] == -1 && ExtHaCloseBuffer[i] > BufferUpperBand[i]) - BufferTrend[i] = 1; - else - BufferTrend[i] = BufferTrend[i-1]; - } - - // --- FIX: Logic for connected lines on trend change --- - if(BufferTrend[i] == 1) // Uptrend - { - BufferSupertrend[i] = BufferLowerBand[i]; - BufferColor[i] = 0; - // If trend just changed to UP, connect the previous point - if(BufferTrend[i-1] == -1) - { - BufferSupertrend[i-1] = BufferLowerBand[i]; - } - } - else // Downtrend - { - BufferSupertrend[i] = BufferUpperBand[i]; - BufferColor[i] = 1; - // If trend just changed to DOWN, connect the previous point - if(BufferTrend[i-1] == 1) - { - BufferSupertrend[i-1] = BufferUpperBand[i]; - } - } - } - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Supertrend_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for full recalculation and stability +#property description "Supertrend Indicator on Heikin Ashi data" + +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 6 // Supertrend, Color, ATR, Upper, Lower, Trend +#property indicator_plots 1 + +//--- Plot 1: Supertrend line +#property indicator_label1 "HA_Supertrend" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrLimeGreen, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAtrPeriod = 10; +input double InpFactor = 3.0; + +//--- Indicator Buffers --- +double BufferSupertrend[]; +double BufferColor[]; +double BufferATR[]; +double BufferUpperBand[]; +double BufferLowerBand[]; +double BufferTrend[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtAtrPeriod; +double g_ExtFactor; +int g_handle_atr; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; + + SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferATR, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferSupertrend, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferATR, false); + ArraySetAsSeries(BufferUpperBand, false); + ArraySetAsSeries(BufferLowerBand, false); + ArraySetAsSeries(BufferTrend, false); + +// ATR is calculated on standard candles for true volatility + g_handle_atr = iATR(_Symbol, _Period, g_ExtAtrPeriod); + if(g_handle_atr == INVALID_HANDLE) + { + Print("Error creating iATR handle."); + return(INIT_FAILED); + } + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // For trend change gaps + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Supertrend(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + delete g_ha_calculator; + IndicatorRelease(g_handle_atr); + } + +//+------------------------------------------------------------------+ +//| Supertrend on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Get ATR values + if(CopyBuffer(g_handle_atr, 0, 0, rates_total, BufferATR) < rates_total) + { + Print("Error copying iATR buffer data."); + } + +//--- STEP 3: Main calculation loop with robust initialization + for(int i = 1; i < rates_total; i++) + { + double ha_hl2 = (ExtHaHighBuffer[i] + ExtHaLowBuffer[i]) / 2.0; + double atr_val = g_ExtFactor * BufferATR[i]; + + double upper_basic = ha_hl2 + atr_val; + double lower_basic = ha_hl2 - atr_val; + + if(upper_basic < BufferUpperBand[i-1] || ExtHaCloseBuffer[i-1] > BufferUpperBand[i-1]) + BufferUpperBand[i] = upper_basic; + else + BufferUpperBand[i] = BufferUpperBand[i-1]; + + if(lower_basic > BufferLowerBand[i-1] || ExtHaCloseBuffer[i-1] < BufferLowerBand[i-1]) + BufferLowerBand[i] = lower_basic; + else + BufferLowerBand[i] = BufferLowerBand[i-1]; + + if(i == g_ExtAtrPeriod) + { + if(ExtHaCloseBuffer[i] > ha_hl2) + BufferTrend[i] = 1; + else + BufferTrend[i] = -1; + } + else + if(i > g_ExtAtrPeriod) + { + if(BufferTrend[i-1] == 1 && ExtHaCloseBuffer[i] < BufferLowerBand[i]) + BufferTrend[i] = -1; + else + if(BufferTrend[i-1] == -1 && ExtHaCloseBuffer[i] > BufferUpperBand[i]) + BufferTrend[i] = 1; + else + BufferTrend[i] = BufferTrend[i-1]; + } + + // --- FIX: Logic for connected lines on trend change --- + if(BufferTrend[i] == 1) // Uptrend + { + BufferSupertrend[i] = BufferLowerBand[i]; + BufferColor[i] = 0; + // If trend just changed to UP, connect the previous point + if(BufferTrend[i-1] == -1) + { + BufferSupertrend[i-1] = BufferLowerBand[i]; + } + } + else // Downtrend + { + BufferSupertrend[i] = BufferUpperBand[i]; + BufferColor[i] = 1; + // If trend just changed to DOWN, connect the previous point + if(BufferTrend[i-1] == 1) + { + BufferSupertrend[i-1] = BufferUpperBand[i]; + } + } + } + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/Supertrend_HeikinAshi_Pure.mq5 b/Indicators/MyIndicators/Supertrend_HeikinAshi_Pure.mq5 index 3ea2368..006efc3 100644 --- a/Indicators/MyIndicators/Supertrend_HeikinAshi_Pure.mq5 +++ b/Indicators/MyIndicators/Supertrend_HeikinAshi_Pure.mq5 @@ -1,200 +1,200 @@ -//+------------------------------------------------------------------+ -//| Supertrend_HeikinAshi_Pure.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.00" // Pure Heikin Ashi version with manual ATR -#property description "Supertrend Indicator based entirely on Heikin Ashi data" - -#include -#include - -//--- Indicator Window and Plot Properties --- -#property indicator_chart_window -#property indicator_buffers 6 // Supertrend, Color, HA_ATR, Upper, Lower, Trend -#property indicator_plots 1 - -//--- Plot 1: Supertrend line -#property indicator_label1 "HA_Supertrend_Pure" -#property indicator_type1 DRAW_COLOR_LINE -#property indicator_color1 clrLimeGreen, clrTomato -#property indicator_style1 STYLE_SOLID -#property indicator_width1 2 - -//--- Input Parameters --- -input int InpAtrPeriod = 10; -input double InpFactor = 3.0; - -//--- Indicator Buffers --- -double BufferSupertrend[]; -double BufferColor[]; -double BufferHA_ATR[]; -double BufferUpperBand[]; -double BufferLowerBand[]; -double BufferTrend[]; - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtAtrPeriod; -double g_ExtFactor; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; - g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; - - SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); - SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); - SetIndexBuffer(2, BufferHA_ATR, INDICATOR_CALCULATIONS); - SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); - SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); - - ArraySetAsSeries(BufferSupertrend, false); - ArraySetAsSeries(BufferColor, false); - ArraySetAsSeries(BufferHA_ATR, false); - ArraySetAsSeries(BufferUpperBand, false); - ArraySetAsSeries(BufferLowerBand, false); - ArraySetAsSeries(BufferTrend, false); - - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Supertrend_Pure(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - delete g_ha_calculator; - } - -//+------------------------------------------------------------------+ -//| Supertrend on Heikin Ashi calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total <= g_ExtAtrPeriod) - return(0); - -//--- Resize intermediate buffers - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate Heikin Ashi True Range - double ha_tr[]; - ArrayResize(ha_tr, rates_total); - for(int i = 1; i < rates_total; i++) - { - ha_tr[i] = MathMax(ExtHaHighBuffer[i], ExtHaCloseBuffer[i-1]) - MathMin(ExtHaLowBuffer[i], ExtHaCloseBuffer[i-1]); - } - -//--- STEP 3: Calculate Heikin Ashi ATR and Supertrend in a single loop - for(int i = 1; i < rates_total; i++) - { - // --- Calculate Heikin Ashi ATR (using Wilder's smoothing) --- - if(i == g_ExtAtrPeriod) // Initialization with SMA - { - BufferHA_ATR[i] = SimpleMA(i, g_ExtAtrPeriod, ha_tr); - } - else - if(i > g_ExtAtrPeriod) // Recursive calculation - { - BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; - } - - // --- Calculate Supertrend Bands and Trend --- - if(i >= g_ExtAtrPeriod) - { - double ha_hl2 = (ExtHaHighBuffer[i] + ExtHaLowBuffer[i]) / 2.0; - double atr_val = g_ExtFactor * BufferHA_ATR[i]; - - double upper_basic = ha_hl2 + atr_val; - double lower_basic = ha_hl2 - atr_val; - - if(upper_basic < BufferUpperBand[i-1] || ExtHaCloseBuffer[i-1] > BufferUpperBand[i-1]) - BufferUpperBand[i] = upper_basic; - else - BufferUpperBand[i] = BufferUpperBand[i-1]; - - if(lower_basic > BufferLowerBand[i-1] || ExtHaCloseBuffer[i-1] < BufferLowerBand[i-1]) - BufferLowerBand[i] = lower_basic; - else - BufferLowerBand[i] = BufferLowerBand[i-1]; - - if(i == g_ExtAtrPeriod) - { - if(ExtHaCloseBuffer[i] > ha_hl2) - BufferTrend[i] = 1; - else - BufferTrend[i] = -1; - } - else - { - if(BufferTrend[i-1] == 1 && ExtHaCloseBuffer[i] < BufferLowerBand[i]) - BufferTrend[i] = -1; - else - if(BufferTrend[i-1] == -1 && ExtHaCloseBuffer[i] > BufferUpperBand[i]) - BufferTrend[i] = 1; - else - BufferTrend[i] = BufferTrend[i-1]; - } - - if(BufferTrend[i] == 1) - { - BufferSupertrend[i] = BufferLowerBand[i]; - BufferColor[i] = 0; - if(BufferTrend[i-1] == -1) - BufferSupertrend[i-1] = BufferLowerBand[i]; - } - else - { - BufferSupertrend[i] = BufferUpperBand[i]; - BufferColor[i] = 1; - if(BufferTrend[i-1] == 1) - BufferSupertrend[i-1] = BufferUpperBand[i]; - } - } - } - return(rates_total); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Supertrend_HeikinAshi_Pure.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.00" // Pure Heikin Ashi version with manual ATR +#property description "Supertrend Indicator based entirely on Heikin Ashi data" + +#include +#include + +//--- Indicator Window and Plot Properties --- +#property indicator_chart_window +#property indicator_buffers 6 // Supertrend, Color, HA_ATR, Upper, Lower, Trend +#property indicator_plots 1 + +//--- Plot 1: Supertrend line +#property indicator_label1 "HA_Supertrend_Pure" +#property indicator_type1 DRAW_COLOR_LINE +#property indicator_color1 clrLimeGreen, clrTomato +#property indicator_style1 STYLE_SOLID +#property indicator_width1 2 + +//--- Input Parameters --- +input int InpAtrPeriod = 10; +input double InpFactor = 3.0; + +//--- Indicator Buffers --- +double BufferSupertrend[]; +double BufferColor[]; +double BufferHA_ATR[]; +double BufferUpperBand[]; +double BufferLowerBand[]; +double BufferTrend[]; + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtAtrPeriod; +double g_ExtFactor; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod; + g_ExtFactor = (InpFactor <= 0) ? 3.0 : InpFactor; + + SetIndexBuffer(0, BufferSupertrend, INDICATOR_DATA); + SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX); + SetIndexBuffer(2, BufferHA_ATR, INDICATOR_CALCULATIONS); + SetIndexBuffer(3, BufferUpperBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(4, BufferLowerBand, INDICATOR_CALCULATIONS); + SetIndexBuffer(5, BufferTrend, INDICATOR_CALCULATIONS); + + ArraySetAsSeries(BufferSupertrend, false); + ArraySetAsSeries(BufferColor, false); + ArraySetAsSeries(BufferHA_ATR, false); + ArraySetAsSeries(BufferUpperBand, false); + ArraySetAsSeries(BufferLowerBand, false); + ArraySetAsSeries(BufferTrend, false); + + IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAtrPeriod); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_Supertrend_Pure(%d, %.1f)", g_ExtAtrPeriod, g_ExtFactor)); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + delete g_ha_calculator; + } + +//+------------------------------------------------------------------+ +//| Supertrend on Heikin Ashi calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total <= g_ExtAtrPeriod) + return(0); + +//--- Resize intermediate buffers + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate Heikin Ashi True Range + double ha_tr[]; + ArrayResize(ha_tr, rates_total); + for(int i = 1; i < rates_total; i++) + { + ha_tr[i] = MathMax(ExtHaHighBuffer[i], ExtHaCloseBuffer[i-1]) - MathMin(ExtHaLowBuffer[i], ExtHaCloseBuffer[i-1]); + } + +//--- STEP 3: Calculate Heikin Ashi ATR and Supertrend in a single loop + for(int i = 1; i < rates_total; i++) + { + // --- Calculate Heikin Ashi ATR (using Wilder's smoothing) --- + if(i == g_ExtAtrPeriod) // Initialization with SMA + { + BufferHA_ATR[i] = SimpleMA(i, g_ExtAtrPeriod, ha_tr); + } + else + if(i > g_ExtAtrPeriod) // Recursive calculation + { + BufferHA_ATR[i] = (BufferHA_ATR[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod; + } + + // --- Calculate Supertrend Bands and Trend --- + if(i >= g_ExtAtrPeriod) + { + double ha_hl2 = (ExtHaHighBuffer[i] + ExtHaLowBuffer[i]) / 2.0; + double atr_val = g_ExtFactor * BufferHA_ATR[i]; + + double upper_basic = ha_hl2 + atr_val; + double lower_basic = ha_hl2 - atr_val; + + if(upper_basic < BufferUpperBand[i-1] || ExtHaCloseBuffer[i-1] > BufferUpperBand[i-1]) + BufferUpperBand[i] = upper_basic; + else + BufferUpperBand[i] = BufferUpperBand[i-1]; + + if(lower_basic > BufferLowerBand[i-1] || ExtHaCloseBuffer[i-1] < BufferLowerBand[i-1]) + BufferLowerBand[i] = lower_basic; + else + BufferLowerBand[i] = BufferLowerBand[i-1]; + + if(i == g_ExtAtrPeriod) + { + if(ExtHaCloseBuffer[i] > ha_hl2) + BufferTrend[i] = 1; + else + BufferTrend[i] = -1; + } + else + { + if(BufferTrend[i-1] == 1 && ExtHaCloseBuffer[i] < BufferLowerBand[i]) + BufferTrend[i] = -1; + else + if(BufferTrend[i-1] == -1 && ExtHaCloseBuffer[i] > BufferUpperBand[i]) + BufferTrend[i] = 1; + else + BufferTrend[i] = BufferTrend[i-1]; + } + + if(BufferTrend[i] == 1) + { + BufferSupertrend[i] = BufferLowerBand[i]; + BufferColor[i] = 0; + if(BufferTrend[i-1] == -1) + BufferSupertrend[i-1] = BufferLowerBand[i]; + } + else + { + BufferSupertrend[i] = BufferUpperBand[i]; + BufferColor[i] = 1; + if(BufferTrend[i-1] == 1) + BufferSupertrend[i-1] = BufferUpperBand[i]; + } + } + } + return(rates_total); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/WPR.md b/Indicators/MyIndicators/WPR.md index 9a72f9e..baca7fa 100644 --- a/Indicators/MyIndicators/WPR.md +++ b/Indicators/MyIndicators/WPR.md @@ -1,56 +1,56 @@ -# Williams' Percent Range (%R) - -## 1. Summary (Introduction) - -Williams' Percent Range, or %R, is a momentum oscillator developed by Larry Williams. It is very similar to the Stochastic Oscillator, but it is plotted on an inverted scale from 0 to -100. Its primary purpose is to identify overbought and oversold conditions in the market. - -The indicator measures the current closing price in relation to the highest high and lowest low over a specified lookback period. It shows where the current price is relative to the recent trading range, helping traders to spot potential exhaustion points in a trend. - -## 2. Mathematical Foundations and Calculation Logic - -The %R formula is straightforward and compares the current close to the recent high-low range. - -### Required Components - -- **Period (N):** The lookback period for the calculation (e.g., 14). -- **Price Data:** The `High`, `Low`, and `Close` of each bar. - -### Calculation Steps (Algorithm) - -1. **Find the Highest High and Lowest Low:** For each bar, determine the highest high and lowest low over the last `N` periods. - $\text{Highest High}_N = \text{Max}(\text{High}, N)_i$ - $\text{Lowest Low}_N = \text{Min}(\text{Low}, N)_i$ - -2. **Calculate the Williams %R:** Apply the main formula. - $\text{\%R}_i = -100 \times \frac{\text{Highest High}_N - \text{Close}_i}{\text{Highest High}_N - \text{Lowest Low}_N}$ - -The result is a value between 0 and -100. A reading close to 0 means the price is closing near the top of its recent range (overbought), while a reading close to -100 means the price is closing near the bottom of its range (oversold). - -## 3. MQL5 Implementation Details - -Our MQL5 implementation is a self-contained, robust, and clear representation of the Williams %R indicator. - -- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. As a non-recursive indicator, this is a simple and highly stable approach. - -- **Self-Contained Logic:** The indicator is completely self-contained. It does not use any external indicator handles (like `iWPR`). All calculations are performed manually within the `OnCalculate` function using the price data provided. - -- **Reusable Helper Functions:** The calculation of the highest high and lowest low is performed by our standard, reusable `Highest()` and `Lowest()` helper functions, ensuring consistency across our entire indicator toolkit. - -- **Clear, Staged Calculation:** The `OnCalculate` function uses a single, efficient `for` loop to calculate the %R for each bar. The logic is clear and directly follows the mathematical definition of the indicator. - -- **Heikin Ashi Variants:** - - **`WPR_HeikinAshi.mq5`:** Our toolkit includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. This results in a smoother oscillator that reflects the momentum of the underlying Heikin Ashi trend. - - **`WPRMA_HeikinAshi.mq5`:** We also have a version that adds a moving average signal line to the Heikin Ashi WPR, providing an additional layer of smoothing and potential crossover signals. - -## 4. Parameters - -- **WPR Period (`InpWPRPeriod`):** The lookback period for the indicator. Larry Williams' original recommendation and the most common value is `14`. - -## 5. Usage and Interpretation - -- **Overbought/Oversold Levels:** The primary use of %R is to identify overbought and oversold conditions. - - **Overbought:** Readings between **0 and -20** are considered overbought. This suggests that the price is near the top of its recent range and may be due for a pullback. - - **Oversold:** Readings between **-80 and -100** are considered oversold. This suggests that the price is near the bottom of its recent range and may be due for a bounce. -- **Divergence:** Look for divergences between the %R and the price action. A bearish divergence (higher price highs, lower %R highs) can signal weakening bullish momentum. A bullish divergence (lower price lows, higher %R lows) can signal weakening bearish momentum. -- **Momentum Failure:** A common signal is when the %R enters the overbought zone, pulls back, and then fails to re-enter the overbought zone on a subsequent price rally. This "momentum failure" can be an early sign of a trend reversal. -- **Caution:** Like all oscillators, %R can remain in overbought or oversold territory for extended periods during a strong trend. It is not a standalone signal for buying or selling but a tool to gauge momentum within a broader market context. +# Williams' Percent Range (%R) + +## 1. Summary (Introduction) + +Williams' Percent Range, or %R, is a momentum oscillator developed by Larry Williams. It is very similar to the Stochastic Oscillator, but it is plotted on an inverted scale from 0 to -100. Its primary purpose is to identify overbought and oversold conditions in the market. + +The indicator measures the current closing price in relation to the highest high and lowest low over a specified lookback period. It shows where the current price is relative to the recent trading range, helping traders to spot potential exhaustion points in a trend. + +## 2. Mathematical Foundations and Calculation Logic + +The %R formula is straightforward and compares the current close to the recent high-low range. + +### Required Components + +- **Period (N):** The lookback period for the calculation (e.g., 14). +- **Price Data:** The `High`, `Low`, and `Close` of each bar. + +### Calculation Steps (Algorithm) + +1. **Find the Highest High and Lowest Low:** For each bar, determine the highest high and lowest low over the last `N` periods. + $\text{Highest High}_N = \text{Max}(\text{High}, N)_i$ + $\text{Lowest Low}_N = \text{Min}(\text{Low}, N)_i$ + +2. **Calculate the Williams %R:** Apply the main formula. + $\text{\%R}_i = -100 \times \frac{\text{Highest High}_N - \text{Close}_i}{\text{Highest High}_N - \text{Lowest Low}_N}$ + +The result is a value between 0 and -100. A reading close to 0 means the price is closing near the top of its recent range (overbought), while a reading close to -100 means the price is closing near the bottom of its range (oversold). + +## 3. MQL5 Implementation Details + +Our MQL5 implementation is a self-contained, robust, and clear representation of the Williams %R indicator. + +- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. As a non-recursive indicator, this is a simple and highly stable approach. + +- **Self-Contained Logic:** The indicator is completely self-contained. It does not use any external indicator handles (like `iWPR`). All calculations are performed manually within the `OnCalculate` function using the price data provided. + +- **Reusable Helper Functions:** The calculation of the highest high and lowest low is performed by our standard, reusable `Highest()` and `Lowest()` helper functions, ensuring consistency across our entire indicator toolkit. + +- **Clear, Staged Calculation:** The `OnCalculate` function uses a single, efficient `for` loop to calculate the %R for each bar. The logic is clear and directly follows the mathematical definition of the indicator. + +- **Heikin Ashi Variants:** + - **`WPR_HeikinAshi.mq5`:** Our toolkit includes a Heikin Ashi version of this indicator. The calculation logic is identical, but it uses the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values as its input. This results in a smoother oscillator that reflects the momentum of the underlying Heikin Ashi trend. + - **`WPRMA_HeikinAshi.mq5`:** We also have a version that adds a moving average signal line to the Heikin Ashi WPR, providing an additional layer of smoothing and potential crossover signals. + +## 4. Parameters + +- **WPR Period (`InpWPRPeriod`):** The lookback period for the indicator. Larry Williams' original recommendation and the most common value is `14`. + +## 5. Usage and Interpretation + +- **Overbought/Oversold Levels:** The primary use of %R is to identify overbought and oversold conditions. + - **Overbought:** Readings between **0 and -20** are considered overbought. This suggests that the price is near the top of its recent range and may be due for a pullback. + - **Oversold:** Readings between **-80 and -100** are considered oversold. This suggests that the price is near the bottom of its recent range and may be due for a bounce. +- **Divergence:** Look for divergences between the %R and the price action. A bearish divergence (higher price highs, lower %R highs) can signal weakening bullish momentum. A bullish divergence (lower price lows, higher %R lows) can signal weakening bearish momentum. +- **Momentum Failure:** A common signal is when the %R enters the overbought zone, pulls back, and then fails to re-enter the overbought zone on a subsequent price rally. This "momentum failure" can be an early sign of a trend reversal. +- **Caution:** Like all oscillators, %R can remain in overbought or oversold territory for extended periods during a strong trend. It is not a standalone signal for buying or selling but a tool to gauge momentum within a broader market context. diff --git a/Indicators/MyIndicators/WPR.mq5 b/Indicators/MyIndicators/WPR.mq5 index 830741a..d1be5ad 100644 --- a/Indicators/MyIndicators/WPR.mq5 +++ b/Indicators/MyIndicators/WPR.mq5 @@ -1,121 +1,121 @@ -//+------------------------------------------------------------------+ -//| WPR.mq5 | -//| Copyright 2025, xxxxxxxx | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.00" // Refactored for stability and clarity -#property description "Larry Williams' Percent Range" - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 -20.0 -#property indicator_level2 -80.0 -#property indicator_levelstyle STYLE_DOT -#property indicator_levelcolor clrSilver -#property indicator_levelwidth 1 -#property indicator_maximum 0.0 -#property indicator_minimum -100.0 - -//--- Buffers and Plots --- -#property indicator_buffers 1 -#property indicator_plots 1 - -//--- Plot 1: WPR line -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_label1 "WPR" - -//--- Input Parameters --- -input int InpWPRPeriod = 14; // Period for WPR calculation - -//--- Indicator Buffers --- -double BufferWPR[]; - -//--- Global Variables --- -int g_ExtWPRPeriod; - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { - g_ExtWPRPeriod = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; - - SetIndexBuffer(0, BufferWPR, INDICATOR_DATA); - ArraySetAsSeries(BufferWPR, false); - - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtWPRPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("WPR(%d)", g_ExtWPRPeriod)); - IndicatorSetInteger(INDICATOR_DIGITS, 2); - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Williams’ Percent Range calculation function. | -//+------------------------------------------------------------------+ -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[]) - { - if(rates_total < g_ExtWPRPeriod) - return(0); - -//--- Main calculation loop - for(int i = g_ExtWPRPeriod - 1; i < rates_total; i++) - { - double highest_high = Highest(high, g_ExtWPRPeriod, i); - double lowest_low = Lowest(low, g_ExtWPRPeriod, i); - double range = highest_high - lowest_low; - - if(range > 0) - BufferWPR[i] = -100.0 * (highest_high - close[i]) / range; - else - BufferWPR[i] = (i > 0) ? BufferWPR[i-1] : -50.0; // Avoid division by zero - } - - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res < array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - if(res > array[current_pos - i]) - res = array[current_pos - i]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| WPR.mq5 | +//| Copyright 2025, xxxxxxxx | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.00" // Refactored for stability and clarity +#property description "Larry Williams' Percent Range" + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 -20.0 +#property indicator_level2 -80.0 +#property indicator_levelstyle STYLE_DOT +#property indicator_levelcolor clrSilver +#property indicator_levelwidth 1 +#property indicator_maximum 0.0 +#property indicator_minimum -100.0 + +//--- Buffers and Plots --- +#property indicator_buffers 1 +#property indicator_plots 1 + +//--- Plot 1: WPR line +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_label1 "WPR" + +//--- Input Parameters --- +input int InpWPRPeriod = 14; // Period for WPR calculation + +//--- Indicator Buffers --- +double BufferWPR[]; + +//--- Global Variables --- +int g_ExtWPRPeriod; + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { + g_ExtWPRPeriod = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; + + SetIndexBuffer(0, BufferWPR, INDICATOR_DATA); + ArraySetAsSeries(BufferWPR, false); + + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtWPRPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("WPR(%d)", g_ExtWPRPeriod)); + IndicatorSetInteger(INDICATOR_DIGITS, 2); + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Williams’ Percent Range calculation function. | +//+------------------------------------------------------------------+ +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[]) + { + if(rates_total < g_ExtWPRPeriod) + return(0); + +//--- Main calculation loop + for(int i = g_ExtWPRPeriod - 1; i < rates_total; i++) + { + double highest_high = Highest(high, g_ExtWPRPeriod, i); + double lowest_low = Lowest(low, g_ExtWPRPeriod, i); + double range = highest_high - lowest_low; + + if(range > 0) + BufferWPR[i] = -100.0 * (highest_high - close[i]) / range; + else + BufferWPR[i] = (i > 0) ? BufferWPR[i-1] : -50.0; // Avoid division by zero + } + + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res < array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + if(res > array[current_pos - i]) + res = array[current_pos - i]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/WPRMA_HeikinAshi.mq5 b/Indicators/MyIndicators/WPRMA_HeikinAshi.mq5 index 2f6257d..a7f0948 100644 --- a/Indicators/MyIndicators/WPRMA_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/WPRMA_HeikinAshi.mq5 @@ -1,227 +1,227 @@ -//+------------------------------------------------------------------+ -//| WPRMA_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx (Based on MetaQuotes WPR) | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "2.02" // Reverted to robust manual MA calculation -#property description "WPR on Heikin Ashi candles, with a Moving Average." - -// --- Standard and Custom Includes --- -#include // For SimpleMA and LinearWeightedMA single-value functions -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 -20.0 -#property indicator_level2 -80.0 -#property indicator_levelstyle STYLE_DOT -#property indicator_levelcolor clrSilver -#property indicator_levelwidth 1 -#property indicator_maximum 0.0 -#property indicator_minimum -100.0 - -//--- Buffers and Plots --- -#property indicator_buffers 2 // WPRMA and the raw WPR -#property indicator_plots 2 - -//--- Plot 1: WPR MA line (smoothed) -#property indicator_label1 "HA_WPRMA" -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrRed - -//--- Plot 2: WPR line (raw) -#property indicator_label2 "HA_WPR" -#property indicator_type2 DRAW_LINE -#property indicator_color2 clrDodgerBlue - -//--- Input Parameters --- -input int InpWPRPeriod = 14; // Period for WPR calculation -input int InpMAPeriod = 14; // Period for Moving Average -input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for Moving Average - -//--- Indicator Buffers --- -double BufferHA_WPRMA[]; // Buffer for the smoothed WPR line -double BufferHA_WPR[]; // Buffer for the raw Heikin Ashi WPR line - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtWPRPeriod; -int g_ExtMAPeriod; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store input periods - g_ExtWPRPeriod = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; - g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod; - -//--- Map the buffers - SetIndexBuffer(0, BufferHA_WPRMA, INDICATOR_DATA); - SetIndexBuffer(1, BufferHA_WPR, INDICATOR_DATA); - -//--- Set buffers to non-timeseries for stable calculation - ArraySetAsSeries(BufferHA_WPRMA, false); - ArraySetAsSeries(BufferHA_WPR, false); - -//--- Set indicator properties - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtWPRPeriod + g_ExtMAPeriod - 2); - PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtWPRPeriod - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_WPRMA(%d, %d)", g_ExtWPRPeriod, g_ExtMAPeriod)); - IndicatorSetInteger(INDICATOR_DIGITS, 2); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Williams’ Percent Range on Heikin Ashi with MA. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data for the first calculation - if(rates_total < g_ExtWPRPeriod) - return(0); - -//--- Resize intermediate buffers to match the available bars - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars using our toolkit (full recalculation) - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate the raw WPR based on the Heikin Ashi results - for(int i = g_ExtWPRPeriod - 1; i < rates_total; i++) - { - double max_ha_high = Highest(ExtHaHighBuffer, g_ExtWPRPeriod, i); - double min_ha_low = Lowest(ExtHaLowBuffer, g_ExtWPRPeriod, i); - if(max_ha_high != min_ha_low) - BufferHA_WPR[i] = - (max_ha_high - ExtHaCloseBuffer[i]) * 100.0 / (max_ha_high - min_ha_low); - else - BufferHA_WPR[i] = (i > 0) ? BufferHA_WPR[i-1] : -50.0; - } - -//--- STEP 3: Calculate the Moving Average on the raw WPR buffer using a manual loop - int ma_start_pos = g_ExtWPRPeriod + g_ExtMAPeriod - 2; - for(int i = ma_start_pos; i < rates_total; i++) - { - switch(InpMAMethod) - { - case MODE_EMA: - // --- Special handling for EMA --- - if(i == ma_start_pos) // First EMA value is an SMA - { - BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); - } - else // Subsequent EMA values are calculated recursively - { - double pr = 2.0 / (g_ExtMAPeriod + 1.0); - BufferHA_WPRMA[i] = BufferHA_WPR[i] * pr + BufferHA_WPRMA[i-1] * (1.0 - pr); - } - break; - - case MODE_SMMA: - if(i == ma_start_pos) // First SMMA value is an SMA - { - BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); - } - else // Subsequent SMMA values are calculated recursively - { - BufferHA_WPRMA[i] = (BufferHA_WPRMA[i-1] * (g_ExtMAPeriod - 1) + BufferHA_WPR[i]) / g_ExtMAPeriod; - } - break; - - case MODE_LWMA: - BufferHA_WPRMA[i] = LinearWeightedMA(i, g_ExtMAPeriod, BufferHA_WPR); - break; - - default: // MODE_SMA - BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); - break; - } - } - -//--- Return value of rates_total to signal a full recalculation - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| WPRMA_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx (Based on MetaQuotes WPR) | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "2.02" // Reverted to robust manual MA calculation +#property description "WPR on Heikin Ashi candles, with a Moving Average." + +// --- Standard and Custom Includes --- +#include // For SimpleMA and LinearWeightedMA single-value functions +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 -20.0 +#property indicator_level2 -80.0 +#property indicator_levelstyle STYLE_DOT +#property indicator_levelcolor clrSilver +#property indicator_levelwidth 1 +#property indicator_maximum 0.0 +#property indicator_minimum -100.0 + +//--- Buffers and Plots --- +#property indicator_buffers 2 // WPRMA and the raw WPR +#property indicator_plots 2 + +//--- Plot 1: WPR MA line (smoothed) +#property indicator_label1 "HA_WPRMA" +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrRed + +//--- Plot 2: WPR line (raw) +#property indicator_label2 "HA_WPR" +#property indicator_type2 DRAW_LINE +#property indicator_color2 clrDodgerBlue + +//--- Input Parameters --- +input int InpWPRPeriod = 14; // Period for WPR calculation +input int InpMAPeriod = 14; // Period for Moving Average +input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Method for Moving Average + +//--- Indicator Buffers --- +double BufferHA_WPRMA[]; // Buffer for the smoothed WPR line +double BufferHA_WPR[]; // Buffer for the raw Heikin Ashi WPR line + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtWPRPeriod; +int g_ExtMAPeriod; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store input periods + g_ExtWPRPeriod = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; + g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod; + +//--- Map the buffers + SetIndexBuffer(0, BufferHA_WPRMA, INDICATOR_DATA); + SetIndexBuffer(1, BufferHA_WPR, INDICATOR_DATA); + +//--- Set buffers to non-timeseries for stable calculation + ArraySetAsSeries(BufferHA_WPRMA, false); + ArraySetAsSeries(BufferHA_WPR, false); + +//--- Set indicator properties + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtWPRPeriod + g_ExtMAPeriod - 2); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtWPRPeriod - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_WPRMA(%d, %d)", g_ExtWPRPeriod, g_ExtMAPeriod)); + IndicatorSetInteger(INDICATOR_DIGITS, 2); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Williams’ Percent Range on Heikin Ashi with MA. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data for the first calculation + if(rates_total < g_ExtWPRPeriod) + return(0); + +//--- Resize intermediate buffers to match the available bars + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars using our toolkit (full recalculation) + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate the raw WPR based on the Heikin Ashi results + for(int i = g_ExtWPRPeriod - 1; i < rates_total; i++) + { + double max_ha_high = Highest(ExtHaHighBuffer, g_ExtWPRPeriod, i); + double min_ha_low = Lowest(ExtHaLowBuffer, g_ExtWPRPeriod, i); + if(max_ha_high != min_ha_low) + BufferHA_WPR[i] = - (max_ha_high - ExtHaCloseBuffer[i]) * 100.0 / (max_ha_high - min_ha_low); + else + BufferHA_WPR[i] = (i > 0) ? BufferHA_WPR[i-1] : -50.0; + } + +//--- STEP 3: Calculate the Moving Average on the raw WPR buffer using a manual loop + int ma_start_pos = g_ExtWPRPeriod + g_ExtMAPeriod - 2; + for(int i = ma_start_pos; i < rates_total; i++) + { + switch(InpMAMethod) + { + case MODE_EMA: + // --- Special handling for EMA --- + if(i == ma_start_pos) // First EMA value is an SMA + { + BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); + } + else // Subsequent EMA values are calculated recursively + { + double pr = 2.0 / (g_ExtMAPeriod + 1.0); + BufferHA_WPRMA[i] = BufferHA_WPR[i] * pr + BufferHA_WPRMA[i-1] * (1.0 - pr); + } + break; + + case MODE_SMMA: + if(i == ma_start_pos) // First SMMA value is an SMA + { + BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); + } + else // Subsequent SMMA values are calculated recursively + { + BufferHA_WPRMA[i] = (BufferHA_WPRMA[i-1] * (g_ExtMAPeriod - 1) + BufferHA_WPR[i]) / g_ExtMAPeriod; + } + break; + + case MODE_LWMA: + BufferHA_WPRMA[i] = LinearWeightedMA(i, g_ExtMAPeriod, BufferHA_WPR); + break; + + default: // MODE_SMA + BufferHA_WPRMA[i] = SimpleMA(i, g_ExtMAPeriod, BufferHA_WPR); + break; + } + } + +//--- Return value of rates_total to signal a full recalculation + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ diff --git a/Indicators/MyIndicators/WPR_HeikinAshi.mq5 b/Indicators/MyIndicators/WPR_HeikinAshi.mq5 index 2140212..291047c 100644 --- a/Indicators/MyIndicators/WPR_HeikinAshi.mq5 +++ b/Indicators/MyIndicators/WPR_HeikinAshi.mq5 @@ -1,181 +1,181 @@ -//+------------------------------------------------------------------+ -//| WPR_HeikinAshi.mq5 | -//| Copyright 2025, xxxxxxxx (Based on MetaQuotes WPR) | -//| | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" -#property link "" -#property version "3.00" // Refactored for stability and new calculator -#property description "Larry Williams' Percent Range based on Heikin Ashi candles" - -//--- Custom Toolkit Include --- -#include - -//--- Indicator Window and Level Properties --- -#property indicator_separate_window -#property indicator_level1 -20.0 -#property indicator_level2 -80.0 -#property indicator_levelstyle STYLE_DOT -#property indicator_levelcolor clrSilver -#property indicator_levelwidth 1 -#property indicator_maximum 0.0 -#property indicator_minimum -100.0 - -//--- Buffers and Plots --- -#property indicator_buffers 1 // Only one buffer is needed for the WPR line -#property indicator_plots 1 - -//--- Plot 1: WPR line -#property indicator_type1 DRAW_LINE -#property indicator_color1 clrDodgerBlue -#property indicator_label1 "HA_WPR" - -//--- Input Parameters --- -input int InpWPRPeriod=14; // Period for WPR calculation - -//--- Indicator Buffers --- -double BufferHA_WPR[]; // The final WPR values for plotting - -//--- Intermediate Heikin Ashi Buffers --- -double ExtHaOpenBuffer[]; -double ExtHaHighBuffer[]; -double ExtHaLowBuffer[]; -double ExtHaCloseBuffer[]; - -//--- Global Objects and Variables --- -int g_ExtPeriodWPR; -CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator - -//--- Forward declarations for helper functions --- -double Highest(const double &array[], int period, int current_pos); -double Lowest(const double &array[], int period, int current_pos); - -//+------------------------------------------------------------------+ -//| Custom indicator initialization function. | -//+------------------------------------------------------------------+ -int OnInit() - { -//--- Validate and store the WPR period - g_ExtPeriodWPR = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; - -//--- Map the buffer to the indicator's internal memory - SetIndexBuffer(0, BufferHA_WPR, INDICATOR_DATA); -//--- Set buffer as non-timeseries for stable calculation - ArraySetAsSeries(BufferHA_WPR, false); - -//--- Set indicator properties - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodWPR - 1); - IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_WPR(%d)", g_ExtPeriodWPR)); - IndicatorSetInteger(INDICATOR_DIGITS, 2); - -//--- Create the calculator instance - g_ha_calculator = new CHeikinAshi_Calculator(); - if(CheckPointer(g_ha_calculator) == POINTER_INVALID) - { - Print("Error creating CHeikinAshi_Calculator object"); - return(INIT_FAILED); - } - - return(INIT_SUCCEEDED); - } - -//+------------------------------------------------------------------+ -//| Custom indicator deinitialization function. | -//+------------------------------------------------------------------+ -void OnDeinit(const int reason) - { -//--- Free the calculator object to prevent memory leaks - if(CheckPointer(g_ha_calculator) != POINTER_INVALID) - { - delete g_ha_calculator; - g_ha_calculator = NULL; - } - } - -//+------------------------------------------------------------------+ -//| Williams’ Percent Range on Heikin Ashi. | -//+------------------------------------------------------------------+ -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[]) - { -//--- Check if there is enough historical data for the first calculation - if(rates_total < g_ExtPeriodWPR) - return(0); - -//--- Resize intermediate buffers to match the available bars - ArrayResize(ExtHaOpenBuffer, rates_total); - ArrayResize(ExtHaHighBuffer, rates_total); - ArrayResize(ExtHaLowBuffer, rates_total); - ArrayResize(ExtHaCloseBuffer, rates_total); - -//--- STEP 1: Calculate Heikin Ashi bars using our toolkit (full recalculation) - g_ha_calculator.Calculate(rates_total, open, high, low, close, - ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); - -//--- STEP 2: Calculate WPR based on the Heikin Ashi results -// Start from the first bar that has enough preceding data for the WPR period - for(int i = g_ExtPeriodWPR - 1; i < rates_total; i++) - { - // Find the highest HA_High and lowest HA_Low over the WPR period - double max_ha_high = Highest(ExtHaHighBuffer, g_ExtPeriodWPR, i); - double min_ha_low = Lowest(ExtHaLowBuffer, g_ExtPeriodWPR, i); - - // Calculate WPR using the current HA_Close - if(max_ha_high != min_ha_low) - BufferHA_WPR[i] = - (max_ha_high - ExtHaCloseBuffer[i]) * 100.0 / (max_ha_high - min_ha_low); - else - // If max high equals min low, avoid division by zero - BufferHA_WPR[i] = (i > 0) ? BufferHA_WPR[i-1] : -50.0; - } - -//--- Return value of rates_total to signal a full recalculation - return(rates_total); - } - -//+------------------------------------------------------------------+ -//| Finds the highest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Highest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; -//--- Loop backwards from the current position for 'period' bars - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; // Stop if we go out of bounds - - if(res < array[index]) - res = array[index]; - } - return(res); - } - -//+------------------------------------------------------------------+ -//| Finds the lowest value in a given period of an array. | -//+------------------------------------------------------------------+ -double Lowest(const double &array[], int period, int current_pos) - { - double res = array[current_pos]; -//--- Loop backwards from the current position for 'period' bars - for(int i = 1; i < period; i++) - { - int index = current_pos - i; - if(index < 0) - break; // Stop if we go out of bounds - - if(res > array[index]) - res = array[index]; - } - return(res); - } -//+------------------------------------------------------------------+ -//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| WPR_HeikinAshi.mq5 | +//| Copyright 2025, xxxxxxxx (Based on MetaQuotes WPR) | +//| | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" +#property link "" +#property version "3.00" // Refactored for stability and new calculator +#property description "Larry Williams' Percent Range based on Heikin Ashi candles" + +//--- Custom Toolkit Include --- +#include + +//--- Indicator Window and Level Properties --- +#property indicator_separate_window +#property indicator_level1 -20.0 +#property indicator_level2 -80.0 +#property indicator_levelstyle STYLE_DOT +#property indicator_levelcolor clrSilver +#property indicator_levelwidth 1 +#property indicator_maximum 0.0 +#property indicator_minimum -100.0 + +//--- Buffers and Plots --- +#property indicator_buffers 1 // Only one buffer is needed for the WPR line +#property indicator_plots 1 + +//--- Plot 1: WPR line +#property indicator_type1 DRAW_LINE +#property indicator_color1 clrDodgerBlue +#property indicator_label1 "HA_WPR" + +//--- Input Parameters --- +input int InpWPRPeriod=14; // Period for WPR calculation + +//--- Indicator Buffers --- +double BufferHA_WPR[]; // The final WPR values for plotting + +//--- Intermediate Heikin Ashi Buffers --- +double ExtHaOpenBuffer[]; +double ExtHaHighBuffer[]; +double ExtHaLowBuffer[]; +double ExtHaCloseBuffer[]; + +//--- Global Objects and Variables --- +int g_ExtPeriodWPR; +CHeikinAshi_Calculator *g_ha_calculator; // Pointer to our Heikin Ashi calculator + +//--- Forward declarations for helper functions --- +double Highest(const double &array[], int period, int current_pos); +double Lowest(const double &array[], int period, int current_pos); + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function. | +//+------------------------------------------------------------------+ +int OnInit() + { +//--- Validate and store the WPR period + g_ExtPeriodWPR = (InpWPRPeriod < 1) ? 1 : InpWPRPeriod; + +//--- Map the buffer to the indicator's internal memory + SetIndexBuffer(0, BufferHA_WPR, INDICATOR_DATA); +//--- Set buffer as non-timeseries for stable calculation + ArraySetAsSeries(BufferHA_WPR, false); + +//--- Set indicator properties + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtPeriodWPR - 1); + IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_WPR(%d)", g_ExtPeriodWPR)); + IndicatorSetInteger(INDICATOR_DIGITS, 2); + +//--- Create the calculator instance + g_ha_calculator = new CHeikinAshi_Calculator(); + if(CheckPointer(g_ha_calculator) == POINTER_INVALID) + { + Print("Error creating CHeikinAshi_Calculator object"); + return(INIT_FAILED); + } + + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function. | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { +//--- Free the calculator object to prevent memory leaks + if(CheckPointer(g_ha_calculator) != POINTER_INVALID) + { + delete g_ha_calculator; + g_ha_calculator = NULL; + } + } + +//+------------------------------------------------------------------+ +//| Williams’ Percent Range on Heikin Ashi. | +//+------------------------------------------------------------------+ +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[]) + { +//--- Check if there is enough historical data for the first calculation + if(rates_total < g_ExtPeriodWPR) + return(0); + +//--- Resize intermediate buffers to match the available bars + ArrayResize(ExtHaOpenBuffer, rates_total); + ArrayResize(ExtHaHighBuffer, rates_total); + ArrayResize(ExtHaLowBuffer, rates_total); + ArrayResize(ExtHaCloseBuffer, rates_total); + +//--- STEP 1: Calculate Heikin Ashi bars using our toolkit (full recalculation) + g_ha_calculator.Calculate(rates_total, open, high, low, close, + ExtHaOpenBuffer, ExtHaHighBuffer, ExtHaLowBuffer, ExtHaCloseBuffer); + +//--- STEP 2: Calculate WPR based on the Heikin Ashi results +// Start from the first bar that has enough preceding data for the WPR period + for(int i = g_ExtPeriodWPR - 1; i < rates_total; i++) + { + // Find the highest HA_High and lowest HA_Low over the WPR period + double max_ha_high = Highest(ExtHaHighBuffer, g_ExtPeriodWPR, i); + double min_ha_low = Lowest(ExtHaLowBuffer, g_ExtPeriodWPR, i); + + // Calculate WPR using the current HA_Close + if(max_ha_high != min_ha_low) + BufferHA_WPR[i] = - (max_ha_high - ExtHaCloseBuffer[i]) * 100.0 / (max_ha_high - min_ha_low); + else + // If max high equals min low, avoid division by zero + BufferHA_WPR[i] = (i > 0) ? BufferHA_WPR[i-1] : -50.0; + } + +//--- Return value of rates_total to signal a full recalculation + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| Finds the highest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Highest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; +//--- Loop backwards from the current position for 'period' bars + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; // Stop if we go out of bounds + + if(res < array[index]) + res = array[index]; + } + return(res); + } + +//+------------------------------------------------------------------+ +//| Finds the lowest value in a given period of an array. | +//+------------------------------------------------------------------+ +double Lowest(const double &array[], int period, int current_pos) + { + double res = array[current_pos]; +//--- Loop backwards from the current position for 'period' bars + for(int i = 1; i < period; i++) + { + int index = current_pos - i; + if(index < 0) + break; // Stop if we go out of bounds + + if(res > array[index]) + res = array[index]; + } + return(res); + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+