chore: remove old indicators

This commit is contained in:
Toh4iem9
2025-10-02 00:28:57 +02:00
parent 0db0533d49
commit 752f1f4362
86 changed files with 0 additions and 12582 deletions
-58
View File
@@ -1,58 +0,0 @@
# Accumulation/Distribution Line (ADL)
## 1. Summary (Introduction)
The Accumulation/Distribution Line (A/D Line or ADL) is a volume-based indicator developed by Marc Chaikin. It was designed to measure the cumulative flow of money into and out of a security. The ADL attempts to identify whether traders are primarily "accumulating" (buying) or "distributing" (selling) an asset by analyzing the relationship between the closing price and its trading range, weighted by volume.
It is a cumulative, running total. A rising ADL suggests that buying pressure is dominant, while a falling ADL suggests that selling pressure is dominant. It is primarily used to confirm the strength of a trend or to spot divergences that may signal a potential reversal.
## 2. Mathematical Foundations and Calculation Logic
The ADL is calculated by first determining the "Money Flow Multiplier" for each period and then using it to weight the volume.
### Required Components
- **Price Data:** The `High`, `Low`, and `Close` of each bar.
- **Volume Data:** The volume for each bar.
### Calculation Steps (Algorithm)
1. **Calculate the Money Flow Multiplier (MFM):** This value determines the proportion of the volume that was bullish or bearish. It ranges from +1 (if Close = High) to -1 (if Close = Low).
$\text{MFM} = \frac{(\text{Close} - \text{Low}) - (\text{High} - \text{Close})}{\text{High} - \text{Low}}$
_(Note: If High equals Low, the MFM is 0)._
2. **Calculate the Money Flow Volume (MFV):** Multiply the MFM by the volume for the period.
$\text{MFV}_i = \text{MFM}_i \times \text{Volume}_i$
3. **Calculate the Accumulation/Distribution Line (ADL):** The ADL is the cumulative sum of the Money Flow Volume.
$\text{ADL}_i = \text{ADL}_{i-1} + \text{MFV}_i$
## 3. MQL5 Implementation Details
Our MQL5 implementation is a self-contained, robust, and accurate representation of the classic A/D Line.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a cumulative, recursive indicator like the ADL, this is the most reliable method to ensure stability and prevent calculation errors.
- **Self-Contained Logic:** The indicator is completely self-contained. It does not use any external indicator handles. All calculations are performed manually within a single, efficient `for` loop in the `OnCalculate` function.
- **Correct Algorithm:** The implementation strictly follows the correct, textbook definition of the ADL, ensuring its results are consistent with other professional charting platforms. The logic correctly handles the selection of Tick or Real volume based on user input.
- **Heikin Ashi Variant (`AD_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 Money Flow Multiplier. The volume component remains the standard volume from the underlying chart.
- This results in a smoother ADL that reflects the buying and selling pressure of the underlying Heikin Ashi trend, effectively filtering out some of the noise from standard price action.
## 4. Parameters
- **Volume Type (`InpVolumeType`):** Allows the user to select between Tick Volume (`VOLUME_TICK`) and Real Volume (`VOLUME_REAL`) for the calculation.
## 5. Usage and Interpretation
The absolute value of the ADL is not important; its **slope and direction** are what matter.
- **Trend Confirmation:**
- If both the price and the ADL are making higher highs and higher lows, the uptrend is considered strong and likely to continue.
- If both the price and the ADL are making lower highs and lower lows, the downtrend is considered strong.
- **Divergence:** This is the most powerful signal from the ADL.
- **Bullish Divergence:** The price continues to fall and makes a new low, but the ADL fails to make a new low and starts to rise. This suggests that accumulation (buying) is taking place despite the lower prices, which can foreshadow a bullish reversal.
- **Bearish Divergence:** The price continues to rise and makes a new high, but the ADL fails to make a new high and starts to fall. This suggests that distribution (selling) is occurring on the rally, which can be an early warning of a bearish reversal.
- **Caution:** The ADL does not account for price gaps between periods. A significant gap down will not be reflected in the ADL's calculation, which can sometimes lead to a discrepancy between price and the indicator. It is best used for confirmation alongside other price-based indicators.
-79
View File
@@ -1,79 +0,0 @@
//+------------------------------------------------------------------+
//| AD.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.01" // Corrected volume source handling
#property description "Accumulation/Distribution Line"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_label1 "A/D"
//--- Input Parameters ---
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK; // Volume type
//--- Indicator Buffers ---
double BufferAD[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
void OnInit()
{
SetIndexBuffer(0, BufferAD, INDICATOR_DATA);
ArraySetAsSeries(BufferAD, false);
IndicatorSetInteger(INDICATOR_DIGITS, 0);
IndicatorSetString(INDICATOR_SHORTNAME, "A/D");
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 1);
}
//+------------------------------------------------------------------+
//| Accumulation/Distribution 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);
//--- Main calculation loop
for(int i = 0; i < rates_total; i++)
{
double mfm = 0; // Money Flow Multiplier
double range = high[i] - low[i];
if(range > 0)
{
mfm = ((close[i] - low[i]) - (high[i] - close[i])) / range;
}
// --- FIX: Use ternary operator to select volume source ---
long current_volume = (InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i];
double mfv = mfm * current_volume; // Money Flow Volume
if(i > 0)
BufferAD[i] = BufferAD[i-1] + mfv;
else
BufferAD[i] = mfv; // First value
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-91
View File
@@ -1,91 +0,0 @@
# 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.
-179
View File
@@ -1,179 +0,0 @@
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-232
View File
@@ -1,232 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-113
View File
@@ -1,113 +0,0 @@
//+------------------------------------------------------------------+
//| AD_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Accumulation/Distribution Line on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_label1 "HA_A/D"
//--- Input Parameters ---
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK; // Volume type
//--- Indicator Buffers ---
double BufferAD[];
//--- Global Objects and Variables ---
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferAD, INDICATOR_DATA);
ArraySetAsSeries(BufferAD, false);
IndicatorSetInteger(INDICATOR_DIGITS, 0);
IndicatorSetString(INDICATOR_SHORTNAME, "HA_A/D");
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 1);
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;
}
}
//+------------------------------------------------------------------+
//| A/D 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);
//--- 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: Main calculation loop on HA data
for(int i = 0; i < rates_total; i++)
{
double mfm = 0; // Money Flow Multiplier
double range = ha_high[i] - ha_low[i];
if(range > 0)
{
mfm = ((ha_close[i] - ha_low[i]) - (ha_high[i] - ha_close[i])) / range;
}
long current_volume = (InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i];
double mfv = mfm * current_volume; // Money Flow Volume
if(i > 0)
BufferAD[i] = BufferAD[i-1] + mfv;
else
BufferAD[i] = mfv; // First value
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-67
View File
@@ -1,67 +0,0 @@
# 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.
-134
View File
@@ -1,134 +0,0 @@
//+------------------------------------------------------------------+
//| 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i]= (high[i] + low[i] + 2*close[i]) / 4.0;
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Main calculation loop
double m = g_ExtAlmaOffset * (g_ExtAlmaPeriod - 1.0);
double s = (double)g_ExtAlmaPeriod / g_ExtAlmaSigma;
for(int i = g_ExtAlmaPeriod - 1; i < rates_total; i++)
{
double sum = 0.0;
double norm = 0.0;
for(int j = 0; j < g_ExtAlmaPeriod; j++)
{
double weight = MathExp(-1 * MathPow(j - m, 2) / (2 * s * s));
int price_index = i - (g_ExtAlmaPeriod - 1) + j;
sum += price_source[price_index] * weight;
norm += weight;
}
if(norm > 0)
BufferALMA[i] = sum / norm;
else
BufferALMA[i] = 0.0;
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-172
View File
@@ -1,172 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-64
View File
@@ -1,64 +0,0 @@
# Adaptive Moving Average (AMA)
## 1. Summary (Introduction)
The Adaptive Moving Average (AMA), developed by Perry J. Kaufman, is an advanced moving average designed to automatically adjust its speed based on market volatility. It addresses a core dilemma of traditional moving averages: the trade-off between lag and smoothness.
The AMA's key feature is its ability to move very slowly when the market is consolidating or moving sideways (high noise, low directional movement), and to speed up and track prices closely when the market is trending (low noise, high directional movement). This adaptability helps to filter out false signals in choppy markets while remaining responsive during strong trends.
## 2. Mathematical Foundations and Calculation Logic
The AMA's adaptability is achieved through the **Efficiency Ratio (ER)**, which quantifies the amount of "noise" in the market.
### Required Components
- **AMA Period (N):** The lookback period for calculating the Efficiency Ratio.
- **Fast/Slow EMA Periods:** Used to define the fastest and slowest possible speeds for the AMA.
- **Source Price (P):** The price series used for the calculation.
### Calculation Steps (Algorithm)
1. **Calculate the Efficiency Ratio (ER):** The ER is the ratio of the net price change (Direction) to the sum of all individual price changes (Volatility) over the period `N`.
- $\text{Direction}_i = \text{Abs}(\text{Price}_i - \text{Price}_{i-N})$
- $\text{Volatility}_i = \sum_{k=i-N+1}^{i} \text{Abs}(\text{Price}_k - \text{Price}_{k-1})$
- $\text{ER}_i = \frac{\text{Direction}_i}{\text{Volatility}_i}$
- An ER value close to `1` indicates an efficient, trending market. A value close to `0` indicates an inefficient, noisy market.
2. **Calculate the Scaled Smoothing Constant (SSC):** The ER is used to create a dynamic smoothing constant that varies between the constants of a fast and a slow EMA.
- $\text{Fast SC} = \frac{2}{\text{Fast Period} + 1}$
- $\text{Slow SC} = \frac{2}{\text{Slow Period} + 1}$
- $\text{SSC}_i = (\text{ER}_i \times (\text{Fast SC} - \text{Slow SC})) + \text{Slow SC}$
3. **Calculate the Final AMA:** The AMA is calculated recursively. The `SSC` is squared to give more weight to the faster smoothing constant during trends.
$\text{AMA}_i = \text{AMA}_{i-1} + (\text{SSC}_i)^2 \times (P_i - \text{AMA}_{i-1})$
## 3. MQL5 Implementation Details
Our MQL5 implementation is a self-contained, robust, and accurate representation of Kaufman's AMA.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. For a recursive indicator like the AMA, this is the most reliable method to ensure stability.
- **Robust Initialization:** The recursive AMA calculation is carefully initialized. The **first valid value** of the AMA line (`BufferAMA[g_ExtAmaPeriod]`) is set directly to the current source price. This provides a simple and highly stable starting point for the subsequent recursive calculations.
- **Self-Contained Logic:** The indicator is completely self-contained and does not use any external handles or libraries. The source price is prepared internally using a `switch` block that handles all `ENUM_APPLIED_PRICE` types.
- **Clear, Staged Calculation:** The `OnCalculate` function is structured into clear, sequential steps. After preparing the source price array, a single, efficient `for` loop handles the entire AMA calculation, including the ER and SSC computations.
- **Heikin Ashi Variant (`AMA_HeikinAshi.mq5`):**
- Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data as its input.
- **Behavioral Note:** This version can appear _more responsive_ than the standard version in strong trends. The smoothed Heikin Ashi data produces a very high Efficiency Ratio (close to 1), causing the AMA to switch to its fastest speed and closely track the underlying Heikin Ashi trend.
## 4. Parameters
- **AMA Period (`InpAmaPeriod`):** The lookback period for the Efficiency Ratio calculation. Default is `10`.
- **Fast EMA Period (`InpFastEmaPeriod`):** Defines the "fastest" speed of the AMA. Default is `2`.
- **Slow EMA Period (`InpSlowEmaPeriod`):** Defines the "slowest" speed of the AMA. Default is `30`.
- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation. Default is `PRICE_CLOSE`.
## 5. Usage and Interpretation
- **Trend Identification:** The AMA is used as an adaptive trend line. When the price is above the AMA and the line is rising, the trend is bullish. When the price is below the line and it is falling, the trend is bearish.
- **Trend Filter:** The key advantage of the AMA is its ability to flatten out and move slowly during sideways markets. A flat AMA line is a clear signal to avoid trend-following strategies. When the line begins to angle up or down sharply, it indicates that the market has entered a more efficient, trending phase.
- **Crossover Signals:** Crossovers of the price and the AMA line can be used as trade signals. These signals are naturally filtered by the indicator itself, as crossovers are less likely to occur during choppy conditions when the AMA is moving slowly.
-139
View File
@@ -1,139 +0,0 @@
//+------------------------------------------------------------------+
//| AMA.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.01" // Corrected standard version
#property description "Adaptive Moving Average (AMA) by Perry Kaufman"
//--- Indicator Window and Plot Properties ---
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label1 "AMA"
//--- Input Parameters ---
input int InpAmaPeriod = 10; // AMA Efficiency Ratio Period
input int InpFastEmaPeriod= 2; // Fast EMA Period for scaling
input int InpSlowEmaPeriod= 30; // Slow EMA Period for scaling
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied Price
//--- Indicator Buffers ---
double BufferAMA[];
//--- Global Variables ---
int g_ExtAmaPeriod;
int g_ExtFastEmaPeriod;
int g_ExtSlowEmaPeriod;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtAmaPeriod = (InpAmaPeriod < 1) ? 1 : InpAmaPeriod;
g_ExtFastEmaPeriod = (InpFastEmaPeriod < 1) ? 1 : InpFastEmaPeriod;
g_ExtSlowEmaPeriod = (InpSlowEmaPeriod < 1) ? 1 : InpSlowEmaPeriod;
SetIndexBuffer(0, BufferAMA, INDICATOR_DATA);
ArraySetAsSeries(BufferAMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAmaPeriod);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("AMA(%d,%d,%d)", g_ExtAmaPeriod, g_ExtFastEmaPeriod, g_ExtSlowEmaPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Adaptive 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_ExtAmaPeriod)
return(0);
//--- STEP 1: Prepare the source price array
double price_source[];
ArrayResize(price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i]= (high[i] + low[i] + 2*close[i]) / 4.0;
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Main calculation loop
double fast_sc = 2.0 / (g_ExtFastEmaPeriod + 1.0);
double slow_sc = 2.0 / (g_ExtSlowEmaPeriod + 1.0);
for(int i = 1; i < rates_total; i++)
{
// --- Initialization Step ---
if(i == g_ExtAmaPeriod)
{
// The first AMA value is simply the current price
BufferAMA[i] = price_source[i];
continue;
}
if(i > g_ExtAmaPeriod)
{
// --- Calculate Efficiency Ratio (ER) ---
double direction = MathAbs(price_source[i] - price_source[i - g_ExtAmaPeriod]);
double volatility = 0;
for(int j = 0; j < g_ExtAmaPeriod; j++)
{
volatility += MathAbs(price_source[i - j] - price_source[i - j - 1]);
}
double er = (volatility > 0) ? direction / volatility : 0;
// --- Calculate Scaled Smoothing Constant (SSC) ---
double ssc = er * (fast_sc - slow_sc) + slow_sc;
double ssc_sq = ssc * ssc;
// --- Calculate Final AMA ---
BufferAMA[i] = BufferAMA[i-1] + ssc_sq * (price_source[i] - BufferAMA[i-1]);
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-166
View File
@@ -1,166 +0,0 @@
//+------------------------------------------------------------------+
//| AMA_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Adaptive Moving Average (AMA) on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label1 "HA_AMA"
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW, HA_PRICE_TYPICAL, HA_PRICE_MEDIAN
};
//--- Input Parameters ---
input int InpAmaPeriod = 10;
input int InpFastEmaPeriod= 2;
input int InpSlowEmaPeriod= 30;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferAMA[];
//--- Global Objects and Variables ---
int g_ExtAmaPeriod, g_ExtFastEmaPeriod, g_ExtSlowEmaPeriod;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtAmaPeriod = (InpAmaPeriod < 1) ? 1 : InpAmaPeriod;
g_ExtFastEmaPeriod = (InpFastEmaPeriod < 1) ? 1 : InpFastEmaPeriod;
g_ExtSlowEmaPeriod = (InpSlowEmaPeriod < 1) ? 1 : InpSlowEmaPeriod;
SetIndexBuffer(0, BufferAMA, INDICATOR_DATA);
ArraySetAsSeries(BufferAMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtAmaPeriod);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_AMA(%d,%d,%d)", g_ExtAmaPeriod, g_ExtFastEmaPeriod, g_ExtSlowEmaPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
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;
}
}
//+------------------------------------------------------------------+
//| AMA 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_ExtAmaPeriod)
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: Prepare the Heikin Ashi source price array
double ha_price_source[];
ArrayResize(ha_price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case HA_PRICE_OPEN:
ha_price_source[i] = ha_open[i];
break;
case HA_PRICE_HIGH:
ha_price_source[i] = ha_high[i];
break;
case HA_PRICE_LOW:
ha_price_source[i] = ha_low[i];
break;
case HA_PRICE_TYPICAL:
ha_price_source[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
break;
case HA_PRICE_MEDIAN:
ha_price_source[i] = (ha_high[i] + ha_low[i]) / 2.0;
break;
default:
ha_price_source[i] = ha_close[i];
break;
}
}
//--- STEP 3: Main calculation loop on HA data
double fast_sc = 2.0 / (g_ExtFastEmaPeriod + 1.0);
double slow_sc = 2.0 / (g_ExtSlowEmaPeriod + 1.0);
for(int i = 1; i < rates_total; i++)
{
if(i == g_ExtAmaPeriod)
{
BufferAMA[i] = ha_price_source[i];
continue;
}
if(i > g_ExtAmaPeriod)
{
double direction = MathAbs(ha_price_source[i] - ha_price_source[i - g_ExtAmaPeriod]);
double volatility = 0;
for(int j = 0; j < g_ExtAmaPeriod; j++)
{
volatility += MathAbs(ha_price_source[i - j] - ha_price_source[i - j - 1]);
}
double er = (volatility > 0) ? direction / volatility : 0;
double ssc = er * (fast_sc - slow_sc) + slow_sc;
double ssc_sq = ssc * ssc;
BufferAMA[i] = BufferAMA[i-1] + ssc_sq * (ha_price_source[i] - BufferAMA[i-1]);
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,66 +0,0 @@
# AMA Trend Activity
## 1. Summary (Introduction)
The AMA Trend Activity is a custom-built "meta-indicator" designed to measure the directional strength and activity of the **Adaptive Moving Average (AMA)**. While the AMA line itself shows the trend, this oscillator quantifies _how trendy_ the market is according to the AMA's behavior, specifically its rate of change relative to volatility.
Developed as part of our indicator toolkit, its primary purpose is to act as a **trend filter**. It generates high values when the AMA is moving decisively in one direction (indicating an efficient, trending market) and low values when the AMA line flattens out (indicating a sideways, noisy market). It helps traders to visually distinguish between trending and non-trending environments.
## 2. Mathematical Foundations and Calculation Logic
This indicator analyzes the behavior of two underlying indicators, AMA and ATR, to produce a final, normalized oscillator.
### Required Components
- **AMA:** The underlying adaptive moving average. Its slope is the primary input.
- **ATR (Average True Range):** Used as a normalization factor to make the indicator's output comparable across different instruments and timeframes.
- **Smoothing Period:** A final smoothing period for the oscillator output.
### Calculation Steps (Algorithm)
1. **Calculate AMA:** First, the standard Kaufman's AMA is calculated for the chart based on its parameters.
2. **Calculate ATR:** Separately, the standard Wilder's ATR is calculated.
3. **Calculate Raw Activity:** For each bar, the indicator measures the rate of change (slope) of the AMA line and normalizes it by the market's current volatility (ATR). This produces a raw, unbounded value representing the trend's relative strength.
$\text{Raw Activity}_i = \frac{\text{Abs}(\text{AMA}_i - \text{AMA}_{i-1})}{\text{ATR}_i}$
4. **Normalize with Arctan:** To solve the problem of scale across different timeframes, the `Raw Activity` value is passed through the inverse tangent (`Arctan`) function and then scaled to a consistent `0..1` range. The `Arctan` function elegantly maps any positive input into a predictable range, making the indicator robust on any timeframe.
$\text{Scaled Activity}_i = \frac{\text{Arctan}(\text{Raw Activity}_i)}{\pi/2}$
5. **Final Smoothing:** The `Scaled Activity` values are smoothed with a Simple Moving Average (SMA) to create the final, plotted histogram.
$\text{Final Activity}_i = \text{SMA}(\text{Scaled Activity}, \text{Smoothing Period})_i$
## 3. MQL5 Implementation Details
Our MQL5 implementation is a completely self-contained indicator that internally calculates all its required components based on our established robust principles.
- **Stability via Full Recalculation:** The indicator employs a "brute-force" full recalculation within the `OnCalculate` function for maximum stability.
- **Internal Calculators:** The indicator does not use any external handles. It contains the full, robust, and manually implemented logic for calculating both the **AMA** and the **ATR**. All recursive calculations are carefully initialized to prevent floating-point overflows.
- **Robust Normalization:** The use of the `MathArctan` function for normalization is a key feature. It ensures that the indicator's output remains consistent and comparable across all instruments and timeframes, from M1 to Weekly.
- **Optimized Visualization:** The indicator's vertical scale is programmatically set to a `0.0` to `0.5` range. Our analysis showed that the vast majority of significant signals occur within this range. This "zooms in" on the most relevant area of activity, making the visual output much clearer and easier to interpret.
- **Heikin Ashi Variant (`AMA_TrendActivity_HeikinAshi.mq5`):**
- Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but all its inputs (AMA and ATR) are derived from the smoothed Heikin Ashi price data.
## 4. Parameters
- **AMA Settings:**
- `InpAmaPeriod`: The period for the AMA's Efficiency Ratio.
- `InpFastEmaPeriod`: The "fast" period for the AMA's scaling.
- `InpSlowEmaPeriod`: The "slow" period for the AMA's scaling.
- `InpAppliedPrice`: The source price for the underlying AMA.
- **Activity Calculation Settings:**
- `InpAtrPeriod`: The period for the ATR used in normalization.
- `InpSmoothingPeriod`: The period for the final SMA smoothing of the oscillator.
## 5. Usage and Interpretation
- **Trend Filter:** This is the indicator's primary function. A trader can establish a threshold (e.g., 0.1 or 0.2).
- **Activity > Threshold:** The market is considered to be in a **trending phase**. Trend-following strategies are more likely to be effective.
- **Activity < Threshold:** The market is considered to be in a **ranging or consolidating phase**. Mean-reversion strategies may be more appropriate.
- **Identifying Trend Exhaustion:** A sharp decline in the activity histogram after a strong trend can signal that momentum is waning and the trend may be nearing exhaustion or entering a consolidation phase.
- **Confirming Breakouts:** A spike in the activity histogram accompanying a price breakout from a range can provide strong confirmation that the breakout has momentum behind it.
@@ -1,190 +0,0 @@
//+------------------------------------------------------------------+
//| AMA_TrendActivity.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Measures the trend activity (slope) of an AMA line using Arctan normalization."
#property description "High values suggest a trending market, low values suggest a flat/ranging market."
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label1 "Activity"
#property indicator_minimum 0.0
#property indicator_maximum 0.5
//--- Input Parameters ---
input group "AMA Settings"
input int InpAmaPeriod = 10;
input int InpFastEmaPeriod= 2;
input int InpSlowEmaPeriod= 30;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
input group "Activity Calculation Settings"
input int InpAtrPeriod = 14;
input int InpSmoothingPeriod = 5;
//--- Indicator Buffers ---
double BufferActivity[];
//--- Global Variables ---
int g_ExtAmaPeriod, g_ExtFastEmaPeriod, g_ExtSlowEmaPeriod, g_ExtAtrPeriod, g_ExtSmoothingPeriod;
double g_M_PI_2;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtAmaPeriod = (InpAmaPeriod < 1) ? 1 : InpAmaPeriod;
g_ExtFastEmaPeriod = (InpFastEmaPeriod < 1) ? 1 : InpFastEmaPeriod;
g_ExtSlowEmaPeriod = (InpSlowEmaPeriod < 1) ? 1 : InpSlowEmaPeriod;
g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod;
g_ExtSmoothingPeriod = (InpSmoothingPeriod < 1) ? 1 : InpSmoothingPeriod;
g_M_PI_2 = M_PI / 2.0;
SetIndexBuffer(0, BufferActivity, INDICATOR_DATA);
ArraySetAsSeries(BufferActivity, false);
int draw_begin = g_ExtAmaPeriod + g_ExtAtrPeriod + g_ExtSmoothingPeriod;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("AMA Activity(%d,%d,%d)", g_ExtAmaPeriod, g_ExtAtrPeriod, g_ExtSmoothingPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 4);
IndicatorSetDouble(INDICATOR_MINIMUM, 0.0);
IndicatorSetDouble(INDICATOR_MAXIMUM, 0.5);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| AMA Trend Activity 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_ExtAmaPeriod + g_ExtAtrPeriod + g_ExtSmoothingPeriod;
if(rates_total <= start_pos)
return(0);
//--- STEP 1: Prepare the source price array for AMA
double price_source[];
ArrayResize(price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate AMA
double buffer_ama[];
ArrayResize(buffer_ama, rates_total);
double fast_sc = 2.0 / (g_ExtFastEmaPeriod + 1.0);
double slow_sc = 2.0 / (g_ExtSlowEmaPeriod + 1.0);
for(int i = 1; i < rates_total; i++)
{
if(i == g_ExtAmaPeriod)
{
buffer_ama[i] = price_source[i];
continue;
}
if(i > g_ExtAmaPeriod)
{
double direction = MathAbs(price_source[i] - price_source[i - g_ExtAmaPeriod]);
double volatility = 0;
for(int j = 0; j < g_ExtAmaPeriod; j++)
{
volatility += MathAbs(price_source[i - j] - price_source[i - j - 1]);
}
double er = (volatility > 0) ? direction / volatility : 0;
double ssc = er * (fast_sc - slow_sc) + slow_sc;
double ssc_sq = ssc * ssc;
buffer_ama[i] = buffer_ama[i-1] + ssc_sq * (price_source[i] - buffer_ama[i-1]);
}
}
//--- STEP 3: Calculate ATR
double buffer_atr[];
ArrayResize(buffer_atr, rates_total);
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]);
}
for(int i = 1; i < rates_total; i++)
{
if(i == g_ExtAtrPeriod)
{
double sum_tr = 0;
for(int j = 1; j <= g_ExtAtrPeriod; j++)
sum_tr += tr[j];
buffer_atr[i] = sum_tr / g_ExtAtrPeriod;
}
else
if(i > g_ExtAtrPeriod)
{
buffer_atr[i] = (buffer_atr[i-1] * (g_ExtAtrPeriod - 1) + tr[i]) / g_ExtAtrPeriod;
}
}
//--- STEP 4: Calculate Raw Activity and Scale it using MathArctan
double scaled_activity[];
ArrayResize(scaled_activity, rates_total);
for(int i = g_ExtAmaPeriod + 1; i < rates_total; i++)
{
if(buffer_atr[i] > 0)
{
double raw_activity = MathAbs(buffer_ama[i] - buffer_ama[i-1]) / buffer_atr[i];
scaled_activity[i] = MathArctan(raw_activity) / g_M_PI_2;
}
}
//--- STEP 5: Calculate Final Oscillator (SMA of Scaled Activity)
double sum = 0;
int final_start_pos = g_ExtAmaPeriod + g_ExtSmoothingPeriod;
for(int i = g_ExtAmaPeriod + 1; i < rates_total; i++)
{
sum += scaled_activity[i];
if(i >= final_start_pos)
{
if(i > final_start_pos)
{
sum -= scaled_activity[i - g_ExtSmoothingPeriod];
}
BufferActivity[i] = sum / g_ExtSmoothingPeriod;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,224 +0,0 @@
//+------------------------------------------------------------------+
//| AMA_TrendActivity_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Measures the trend activity of a Heikin Ashi AMA line using Arctan normalization."
#property description "High values suggest a trending market, low values suggest a flat/ranging market."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label1 "HA_Activity"
#property indicator_minimum 0.0
#property indicator_maximum 0.5
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input group "AMA Settings"
input int InpAmaPeriod = 10;
input int InpFastEmaPeriod= 2;
input int InpSlowEmaPeriod= 30;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE;
input group "Activity Calculation Settings"
input int InpAtrPeriod = 14;
input int InpSmoothingPeriod = 5;
//--- Indicator Buffers ---
double BufferActivity[];
//--- Global Objects and Variables ---
int g_ExtAmaPeriod, g_ExtFastEmaPeriod, g_ExtSlowEmaPeriod, g_ExtAtrPeriod, g_ExtSmoothingPeriod;
double g_M_PI_2;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtAmaPeriod = (InpAmaPeriod < 1) ? 1 : InpAmaPeriod;
g_ExtFastEmaPeriod = (InpFastEmaPeriod < 1) ? 1 : InpFastEmaPeriod;
g_ExtSlowEmaPeriod = (InpSlowEmaPeriod < 1) ? 1 : InpSlowEmaPeriod;
g_ExtAtrPeriod = (InpAtrPeriod < 1) ? 1 : InpAtrPeriod;
g_ExtSmoothingPeriod = (InpSmoothingPeriod < 1) ? 1 : InpSmoothingPeriod;
g_M_PI_2 = M_PI / 2.0;
SetIndexBuffer(0, BufferActivity, INDICATOR_DATA);
ArraySetAsSeries(BufferActivity, false);
int draw_begin = g_ExtAmaPeriod + g_ExtAtrPeriod + g_ExtSmoothingPeriod;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA AMA Activity(%d,%d,%d)", g_ExtAmaPeriod, g_ExtAtrPeriod, g_ExtSmoothingPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 4);
IndicatorSetDouble(INDICATOR_MINIMUM, 0.0);
IndicatorSetDouble(INDICATOR_MAXIMUM, 0.5);
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;
}
}
//+------------------------------------------------------------------+
//| AMA Trend Activity 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_ExtAmaPeriod + g_ExtAtrPeriod + g_ExtSmoothingPeriod;
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: Prepare the Heikin Ashi source price array for AMA
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 3: Calculate AMA on HA data
double buffer_ama[];
ArrayResize(buffer_ama, rates_total);
double fast_sc = 2.0 / (g_ExtFastEmaPeriod + 1.0);
double slow_sc = 2.0 / (g_ExtSlowEmaPeriod + 1.0);
for(int i = 1; i < rates_total; i++)
{
if(i == g_ExtAmaPeriod)
{
buffer_ama[i] = ha_price_source[i];
continue;
}
if(i > g_ExtAmaPeriod)
{
double direction = MathAbs(ha_price_source[i] - ha_price_source[i - g_ExtAmaPeriod]);
double volatility = 0;
for(int j = 0; j < g_ExtAmaPeriod; j++)
{
volatility += MathAbs(ha_price_source[i - j] - ha_price_source[i - j - 1]);
}
double er = (volatility > 0) ? direction / volatility : 0;
double ssc = er * (fast_sc - slow_sc) + slow_sc;
double ssc_sq = ssc * ssc;
buffer_ama[i] = buffer_ama[i-1] + ssc_sq * (ha_price_source[i] - buffer_ama[i-1]);
}
}
//--- STEP 4: Calculate Heikin Ashi ATR
double buffer_atr[];
ArrayResize(buffer_atr, rates_total);
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]);
}
for(int i = 1; i < rates_total; i++)
{
if(i == g_ExtAtrPeriod)
{
double sum_tr = 0;
for(int j = 1; j <= g_ExtAtrPeriod; j++)
sum_tr += ha_tr[j];
buffer_atr[i] = sum_tr / g_ExtAtrPeriod;
}
else
if(i > g_ExtAtrPeriod)
{
buffer_atr[i] = (buffer_atr[i-1] * (g_ExtAtrPeriod - 1) + ha_tr[i]) / g_ExtAtrPeriod;
}
}
//--- STEP 5: Calculate Raw Activity and Scale it using MathArctan
double scaled_activity[];
ArrayResize(scaled_activity, rates_total);
for(int i = g_ExtAmaPeriod + 1; i < rates_total; i++)
{
if(buffer_atr[i] > 0)
{
double raw_activity = MathAbs(buffer_ama[i] - buffer_ama[i-1]) / buffer_atr[i];
scaled_activity[i] = MathArctan(raw_activity) / g_M_PI_2;
}
}
//--- STEP 6: Calculate Final Oscillator (SMA of Scaled Activity)
double sum = 0;
int final_start_pos = g_ExtAmaPeriod + g_ExtSmoothingPeriod;
for(int i = g_ExtAmaPeriod + 1; i < rates_total; i++)
{
sum += scaled_activity[i];
if(i >= final_start_pos)
{
if(i > final_start_pos)
{
sum -= scaled_activity[i - g_ExtSmoothingPeriod];
}
BufferActivity[i] = sum / g_ExtSmoothingPeriod;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-64
View File
@@ -1,64 +0,0 @@
# 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.
-99
View File
@@ -1,99 +0,0 @@
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-131
View File
@@ -1,131 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-77
View File
@@ -1,77 +0,0 @@
# Commodity Channel Index (CCI)
## 1. Summary (Introduction)
The Commodity Channel Index (CCI) is a versatile momentum oscillator developed by Donald Lambert, first introduced in "Commodities" magazine in 1980. Despite its name, it is used effectively in any market, including stocks, forex, and futures.
The CCI measures the current price level relative to an average price level over a specified period. It is designed to identify cyclical turns but is widely used to detect overbought and oversold conditions. High values indicate that the price is unusually high compared to its average, and low values indicate it is unusually low.
The **CCI Oscillator** is a supplementary indicator that displays the difference between the main CCI line and its signal line as a histogram, providing a clearer visual of accelerating and decelerating momentum.
## 2. Mathematical Foundations and Calculation Logic
The CCI is based on the relationship between the price, its moving average, and the average deviation from that moving average.
### Required Components
- **Period (N):** The lookback period for all calculations (e.g., 20).
- **Source Price (P):** The price series used for the calculation. The classic definition uses the **Typical Price** `(High + Low + Close) / 3`.
- **Constant:** A statistical constant of `0.015` used to scale the result.
### Calculation Steps (Algorithm)
1. **Calculate the Source Price:** For each bar, calculate the source price (e.g., Typical Price).
$\text{P}_i = \frac{\text{High}_i + \text{Low}_i + \text{Close}_i}{3}$
2. **Calculate the Simple Moving Average (SMA):** Compute an `N`-period SMA of the source price.
$\text{SMA}_i = \text{SMA}(P, N)_i$
3. **Calculate the Mean Absolute Deviation (MAD):** For each bar, calculate the average absolute difference between the source price and its SMA over the `N` period.
$\text{MAD}_i = \frac{1}{N} \sum_{k=i-N+1}^{i} \text{Abs}(P_k - \text{SMA}_i)$
4. **Calculate the CCI Value:** Apply the final formula.
$\text{CCI}_i = \frac{P_i - \text{SMA}_i}{0.015 \times \text{MAD}_i}$
5. **Calculate the Signal Line & Oscillator:** The signal line is a moving average of the CCI line, and the oscillator is the difference between the two.
## 3. MQL5 Implementation Details
Our MQL5 toolkit includes two distinct standard implementations of the CCI, along with their Heikin Ashi counterparts and oscillator versions, to offer a choice between performance and perfect mathematical accuracy.
- **Stability via Full Recalculation:** All versions employ a "brute-force" full recalculation within the `OnCalculate` function to ensure maximum stability.
- **Self-Contained Logic:** All versions are completely self-contained, with fully manual calculations for all components.
- **Optional Signal Line:** All line-based versions have been enhanced with an optional, user-configurable moving average signal line.
### Our Two Calculation Methodologies
1. **Efficient Version (`CCI.mq5`):**
- **Concept:** A high-performance implementation suitable for most applications.
- **Logic:** This version uses an efficient **sliding window sum** technique to calculate both the SMA and the Mean Absolute Deviation (MAD). This is a very close approximation of the precise formula but avoids nested loops, making it significantly faster.
2. **Precise Version (`CCI_Precise.mq5`):**
- **Concept:** A version that adheres strictly to the mathematical definition for maximum accuracy.
- **Logic:** This implementation uses nested `for` loops. For every single bar, it recalculates the precise SMA and then the precise MAD based on that SMA.
### Indicator Family
- **Line Versions:** `CCI.mq5` and `CCI_Precise.mq5` plot the CCI line and its signal line.
- **Oscillator Versions:** `CCI_Oscillator.mq5` and `CCI_Precise_Oscillator.mq5` plot the difference between the CCI and its signal line as a histogram.
- **Heikin Ashi Variants:** All four indicators have "pure" Heikin Ashi counterparts, which use smoothed Heikin Ashi price data as their input.
## 4. Parameters
- **CCI Period (`InpCCIPeriod`):** The lookback period for the SMA and MAD calculations. Common values are 14 or 20.
- **Applied Price (`InpAppliedPrice`):** The source price for the calculation. The classic and default is `PRICE_TYPICAL`.
- **Signal Line Settings:**
- `InpMAPeriod`: The lookback period for the optional signal line.
- `InpMAMethod`: The type of moving average for the signal line.
## 5. Usage and Interpretation
- **Overbought/Oversold Levels:** The primary use of the CCI is to identify extreme conditions.
- **Overbought:** Readings above **+100**.
- **Oversold:** Readings below **-100**.
- **Zero Line Crossovers:** A crossover of the CCI line above the zero line is a bullish signal; a crossover below zero is a bearish signal.
- **Divergence:** A powerful signal where price and the CCI move in opposite directions, often foreshadowing a reversal.
- **Oscillator (Histogram):** The histogram provides a clear visual of the relationship between the CCI and its signal line, highlighting the acceleration and deceleration of momentum.
-210
View File
@@ -1,210 +0,0 @@
//+------------------------------------------------------------------+
//| CCI.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "2.00" // Added selectable MA signal line
#property description "Commodity Channel Index with a signal line."
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // CCI and Signal Line
#property indicator_plots 2
#property indicator_level1 -100.0
#property indicator_level2 100.0
#property indicator_level3 0.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: CCI line
#property indicator_label1 "CCI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferCCI[];
double BufferSignal[];
//--- Global Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferCCI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferCCI, false);
ArraySetAsSeries(BufferSignal, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtCCIPeriod - 1);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtCCIPeriod + g_ExtMAPeriod - 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CCI(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Commodity Channel 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[])
{
// --- FIX: Correct the overall start position check ---
int start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
// ... other price types ...
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate the Simple Moving Average of the price
double buffer_sma[];
ArrayResize(buffer_sma, rates_total);
double sma_sum = 0;
for(int i = 0; i < rates_total; i++)
{
sma_sum += price_source[i];
if(i >= g_ExtCCIPeriod)
{
sma_sum -= price_source[i - g_ExtCCIPeriod];
}
if(i >= g_ExtCCIPeriod - 1)
{
buffer_sma[i] = sma_sum / g_ExtCCIPeriod;
}
}
//--- STEP 3: Calculate the Mean Absolute Deviation (MAD)
double buffer_mad[];
ArrayResize(buffer_mad, rates_total);
double deviation_sum = 0;
double abs_dev[];
ArrayResize(abs_dev, rates_total);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
abs_dev[i] = MathAbs(price_source[i] - buffer_sma[i]);
}
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
deviation_sum += abs_dev[i];
if(i >= g_ExtCCIPeriod * 2 - 2)
{
if(i >= g_ExtCCIPeriod * 2 - 1)
{
deviation_sum -= abs_dev[i - g_ExtCCIPeriod];
}
buffer_mad[i] = deviation_sum / g_ExtCCIPeriod;
}
}
//--- STEP 4: Calculate the final CCI value
for(int i = g_ExtCCIPeriod * 2 - 2; i < rates_total; i++)
{
double mad_value = buffer_mad[i];
if(mad_value > 0)
{
BufferCCI[i] = (price_source[i] - buffer_sma[i]) / (CCI_CONSTANT * mad_value);
}
}
//--- STEP 5: Calculate the Signal Line (MA of CCI)
// --- FIX: Correct the starting position for the MA calculation ---
int ma_start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferCCI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferCCI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferCCI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
-256
View File
@@ -1,256 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "3.00" // Efficient sliding-window version with signal line
#property description "Commodity Channel Index on Heikin Ashi data, with a signal line."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // CCI and Signal Line
#property indicator_plots 2
#property indicator_level1 -100.0
#property indicator_level2 100.0
#property indicator_level3 0.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: CCI line
#property indicator_label1 "HA_CCI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "HA_Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_TYPICAL, // (HA_H + HA_L + HA_C) / 3
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferCCI[];
double BufferSignal[];
//--- Global Objects and Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferCCI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferCCI, false);
ArraySetAsSeries(BufferSignal, false);
int cci_draw_begin = g_ExtCCIPeriod * 2 - 2;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, cci_draw_begin);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, cci_draw_begin + g_ExtMAPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CCI(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| CCI 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_ExtCCIPeriod * 2 + g_ExtMAPeriod - 2;
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: Prepare the Heikin Ashi source price array
double ha_price_source[];
ArrayResize(ha_price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case HA_PRICE_OPEN:
ha_price_source[i] = ha_open[i];
break;
case HA_PRICE_HIGH:
ha_price_source[i] = ha_high[i];
break;
case HA_PRICE_LOW:
ha_price_source[i] = ha_low[i];
break;
case HA_PRICE_CLOSE:
ha_price_source[i] = ha_close[i];
break;
default:
ha_price_source[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
break;
}
}
//--- STEP 3: Calculate the SMA of the HA price
double buffer_sma[];
ArrayResize(buffer_sma, rates_total);
double sma_sum = 0;
for(int i = 0; i < rates_total; i++)
{
sma_sum += ha_price_source[i];
if(i >= g_ExtCCIPeriod)
{
sma_sum -= ha_price_source[i - g_ExtCCIPeriod];
}
if(i >= g_ExtCCIPeriod - 1)
{
buffer_sma[i] = sma_sum / g_ExtCCIPeriod;
}
}
//--- STEP 4: Calculate the Mean Absolute Deviation (MAD) on HA data
double buffer_mad[];
ArrayResize(buffer_mad, rates_total);
double deviation_sum = 0;
double abs_dev[];
ArrayResize(abs_dev, rates_total);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
abs_dev[i] = MathAbs(ha_price_source[i] - buffer_sma[i]);
}
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
deviation_sum += abs_dev[i];
if(i >= g_ExtCCIPeriod * 2 - 2)
{
if(i >= g_ExtCCIPeriod * 2 - 1)
{
deviation_sum -= abs_dev[i - g_ExtCCIPeriod];
}
buffer_mad[i] = deviation_sum / g_ExtCCIPeriod;
}
}
//--- STEP 5: Calculate the final CCI value
for(int i = g_ExtCCIPeriod * 2 - 2; i < rates_total; i++)
{
double mad_value = buffer_mad[i];
if(mad_value > 0)
{
BufferCCI[i] = (ha_price_source[i] - buffer_sma[i]) / (CCI_CONSTANT * mad_value);
}
}
//--- STEP 6: Calculate the Signal Line (MA of CCI)
int ma_start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferCCI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferCCI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferCCI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-192
View File
@@ -1,192 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Oscillator.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "CCI Oscillator (Histogram of CCI vs Signal Line)"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1 // Only the final Histogram buffer is needed
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "CCI Oscillator"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int draw_begin = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CCI Osc(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| CCI 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[])
{
int start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
if(rates_total <= start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_cci[], buffer_signal[];
ArrayResize(buffer_cci, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate CCI internally ---
{
double price_source[];
ArrayResize(price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
default:
price_source[i] = close[i];
break;
}
}
double buffer_sma[];
ArrayResize(buffer_sma, rates_total);
double sma_sum = 0;
for(int i = 0; i < rates_total; i++)
{
sma_sum += price_source[i];
if(i >= g_ExtCCIPeriod)
sma_sum -= price_source[i - g_ExtCCIPeriod];
if(i >= g_ExtCCIPeriod - 1)
buffer_sma[i] = sma_sum / g_ExtCCIPeriod;
}
double buffer_mad[];
ArrayResize(buffer_mad, rates_total);
double deviation_sum = 0;
double abs_dev[];
ArrayResize(abs_dev, rates_total);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
abs_dev[i] = MathAbs(price_source[i] - buffer_sma[i]);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
deviation_sum += abs_dev[i];
if(i >= g_ExtCCIPeriod * 2 - 2)
{
if(i >= g_ExtCCIPeriod * 2 - 1)
deviation_sum -= abs_dev[i - g_ExtCCIPeriod];
buffer_mad[i] = deviation_sum / g_ExtCCIPeriod;
}
}
for(int i = g_ExtCCIPeriod * 2 - 2; i < rates_total; i++)
{
if(buffer_mad[i] > 0)
buffer_cci[i] = (price_source[i] - buffer_sma[i]) / (CCI_CONSTANT * buffer_mad[i]);
}
}
//--- STEP 2: Calculate the Signal Line (MA of CCI) ---
int ma_start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
buffer_signal[i] = buffer_cci[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtMAPeriod-1)+buffer_cci[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=buffer_cci[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = ma_start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_cci[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,237 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Oscillator_HeikinAshi.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "CCI Oscillator on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "HA_CCI_Osc"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_TYPICAL, // (HA_H + HA_L + HA_C) / 3
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Objects and Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int cci_draw_begin = g_ExtCCIPeriod * 2 - 2;
int draw_begin = cci_draw_begin + g_ExtMAPeriod - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CCI_Osc(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| CCI Oscillator 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_ExtCCIPeriod * 2 + g_ExtMAPeriod - 2;
if(rates_total <= start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_cci[], buffer_signal[];
ArrayResize(buffer_cci, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate Heikin Ashi CCI internally ---
{
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);
g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close);
double ha_price_source[];
ArrayResize(ha_price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case HA_PRICE_OPEN:
ha_price_source[i] = ha_open[i];
break;
case HA_PRICE_HIGH:
ha_price_source[i] = ha_high[i];
break;
case HA_PRICE_LOW:
ha_price_source[i] = ha_low[i];
break;
case HA_PRICE_CLOSE:
ha_price_source[i] = ha_close[i];
break;
default:
ha_price_source[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
break;
}
}
double buffer_sma[];
ArrayResize(buffer_sma, rates_total);
double sma_sum = 0;
for(int i = 0; i < rates_total; i++)
{
sma_sum += ha_price_source[i];
if(i >= g_ExtCCIPeriod)
sma_sum -= ha_price_source[i - g_ExtCCIPeriod];
if(i >= g_ExtCCIPeriod - 1)
buffer_sma[i] = sma_sum / g_ExtCCIPeriod;
}
double buffer_mad[];
ArrayResize(buffer_mad, rates_total);
double deviation_sum = 0;
double abs_dev[];
ArrayResize(abs_dev, rates_total);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
abs_dev[i] = MathAbs(ha_price_source[i] - buffer_sma[i]);
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
deviation_sum += abs_dev[i];
if(i >= g_ExtCCIPeriod * 2 - 2)
{
if(i >= g_ExtCCIPeriod * 2 - 1)
deviation_sum -= abs_dev[i - g_ExtCCIPeriod];
buffer_mad[i] = deviation_sum / g_ExtCCIPeriod;
}
}
for(int i = g_ExtCCIPeriod * 2 - 2; i < rates_total; i++)
{
if(buffer_mad[i] > 0)
buffer_cci[i] = (ha_price_source[i] - buffer_sma[i]) / (CCI_CONSTANT * buffer_mad[i]);
}
}
//--- STEP 2: Calculate the Signal Line (MA of CCI) ---
int ma_start_pos = g_ExtCCIPeriod * 2 + g_ExtMAPeriod - 3;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
buffer_signal[i] = buffer_cci[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtMAPeriod-1)+buffer_cci[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=buffer_cci[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = ma_start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_cci[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-200
View File
@@ -1,200 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Precise.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Commodity Channel Index (Precise mathematical definition) with a signal line."
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // CCI and Signal Line
#property indicator_plots 2
#property indicator_level1 -100.0
#property indicator_level2 100.0
#property indicator_level3 0.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: CCI line
#property indicator_label1 "CCI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferCCI[];
double BufferSignal[];
//--- Global Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferCCI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferCCI, false);
ArraySetAsSeries(BufferSignal, false);
int cci_draw_begin = g_ExtCCIPeriod - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, cci_draw_begin);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, cci_draw_begin + g_ExtMAPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CCI Precise(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Commodity Channel Index (Precise) 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_ExtCCIPeriod + g_ExtMAPeriod - 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i]= (high[i] + low[i] + 2*close[i]) / 4.0;
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate CCI using the precise definition
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
// --- Calculate the SMA for the current position 'i' ---
double sma = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
{
sma += price_source[i-j];
}
sma /= g_ExtCCIPeriod;
// --- Calculate the Mean Absolute Deviation for the current position 'i' ---
double mad = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
{
mad += MathAbs(price_source[i-j] - sma);
}
mad /= g_ExtCCIPeriod;
// --- Calculate the final CCI value ---
if(mad > 0)
{
BufferCCI[i] = (price_source[i] - sma) / (CCI_CONSTANT * mad);
}
}
//--- STEP 3: Calculate the Signal Line (MA of CCI)
int ma_start_pos = g_ExtCCIPeriod + g_ExtMAPeriod - 2;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferCCI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferCCI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferCCI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,229 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Precise_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "CCI (Precise definition) on Heikin Ashi data, with a signal line."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // CCI and Signal Line
#property indicator_plots 2
#property indicator_level1 -100.0
#property indicator_level2 100.0
#property indicator_level3 0.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: CCI line
#property indicator_label1 "HA_CCI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "HA_Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_TYPICAL, // (HA_H + HA_L + HA_C) / 3
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferCCI[];
double BufferSignal[];
//--- Global Objects and Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferCCI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferCCI, false);
ArraySetAsSeries(BufferSignal, false);
int cci_draw_begin = g_ExtCCIPeriod - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, cci_draw_begin);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, cci_draw_begin + g_ExtMAPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CCI_Precise(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| CCI Precise 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_ExtCCIPeriod + g_ExtMAPeriod - 1;
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: Prepare the Heikin Ashi source price array
double ha_price_source[];
ArrayResize(ha_price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case HA_PRICE_OPEN:
ha_price_source[i] = ha_open[i];
break;
case HA_PRICE_HIGH:
ha_price_source[i] = ha_high[i];
break;
case HA_PRICE_LOW:
ha_price_source[i] = ha_low[i];
break;
case HA_PRICE_CLOSE:
ha_price_source[i] = ha_close[i];
break;
default:
ha_price_source[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
break;
}
}
//--- STEP 3: Calculate CCI using the precise definition
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
double sma = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
{
sma += ha_price_source[i-j];
}
sma /= g_ExtCCIPeriod;
double mad = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
{
mad += MathAbs(ha_price_source[i-j] - sma);
}
mad /= g_ExtCCIPeriod;
if(mad > 0)
{
BufferCCI[i] = (ha_price_source[i] - sma) / (CCI_CONSTANT * mad);
}
}
//--- STEP 4: Calculate the Signal Line (MA of CCI)
int ma_start_pos = g_ExtCCIPeriod + g_ExtMAPeriod - 2;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferCCI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferCCI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferCCI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferCCI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,172 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Precise_Oscillator.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "CCI Oscillator (Precise) - Histogram of CCI vs Signal Line"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1 // Only the final Histogram buffer is needed
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "CCI Oscillator"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int cci_draw_begin = g_ExtCCIPeriod - 1;
int draw_begin = cci_draw_begin + g_ExtMAPeriod - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CCI Osc Precise(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| CCI Oscillator (Precise) 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_ExtCCIPeriod + g_ExtMAPeriod - 1;
if(rates_total < start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_cci[], buffer_signal[];
ArrayResize(buffer_cci, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate CCI (Precise) internally ---
{
double price_source[];
ArrayResize(price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
default:
price_source[i] = close[i];
break;
}
}
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
double sma = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
sma += price_source[i-j];
sma /= g_ExtCCIPeriod;
double mad = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
mad += MathAbs(price_source[i-j] - sma);
mad /= g_ExtCCIPeriod;
if(mad > 0)
buffer_cci[i] = (price_source[i] - sma) / (CCI_CONSTANT * mad);
}
}
//--- STEP 2: Calculate the Signal Line (MA of CCI) ---
int ma_start_pos = g_ExtCCIPeriod + g_ExtMAPeriod - 2;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
buffer_signal[i] = buffer_cci[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtMAPeriod-1)+buffer_cci[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=buffer_cci[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = ma_start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_cci[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,216 +0,0 @@
//+------------------------------------------------------------------+
//| CCI_Precise_Oscillator_HeikinAshi.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "CCI Oscillator (Precise) on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "HA_CCI_Osc_Precise"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_TYPICAL, // (HA_H + HA_L + HA_C) / 3
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpCCIPeriod = 20;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_TYPICAL;
input group "Signal Line Settings"
input int InpMAPeriod = 14;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Objects and Variables ---
int g_ExtCCIPeriod, g_ExtMAPeriod;
const double CCI_CONSTANT = 0.015;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtCCIPeriod = (InpCCIPeriod < 1) ? 1 : InpCCIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int cci_draw_begin = g_ExtCCIPeriod - 1;
int draw_begin = cci_draw_begin + g_ExtMAPeriod - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CCI_Osc_Precise(%d, %d)", g_ExtCCIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| CCI Oscillator (Precise) 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_ExtCCIPeriod + g_ExtMAPeriod - 1;
if(rates_total < start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_cci[], buffer_signal[];
ArrayResize(buffer_cci, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate Heikin Ashi CCI internally ---
{
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);
g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close);
double ha_price_source[];
ArrayResize(ha_price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case HA_PRICE_OPEN:
ha_price_source[i] = ha_open[i];
break;
case HA_PRICE_HIGH:
ha_price_source[i] = ha_high[i];
break;
case HA_PRICE_LOW:
ha_price_source[i] = ha_low[i];
break;
case HA_PRICE_CLOSE:
ha_price_source[i] = ha_close[i];
break;
default:
ha_price_source[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
break;
}
}
for(int i = g_ExtCCIPeriod - 1; i < rates_total; i++)
{
double sma = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
sma += ha_price_source[i-j];
sma /= g_ExtCCIPeriod;
double mad = 0;
for(int j=0; j<g_ExtCCIPeriod; j++)
mad += MathAbs(ha_price_source[i-j] - sma);
mad /= g_ExtCCIPeriod;
if(mad > 0)
buffer_cci[i] = (ha_price_source[i] - sma) / (CCI_CONSTANT * mad);
}
}
//--- STEP 2: Calculate the Signal Line (MA of CCI) ---
int ma_start_pos = g_ExtCCIPeriod + g_ExtMAPeriod - 2;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
buffer_signal[i] = buffer_cci[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtMAPeriod-1)+buffer_cci[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=buffer_cci[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=buffer_cci[i-j];
buffer_signal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = ma_start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_cci[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-65
View File
@@ -1,65 +0,0 @@
# Chaikin Oscillator (CHO)
## 1. Summary (Introduction)
The Chaikin Oscillator (CHO) is a momentum indicator developed by Marc Chaikin. It is an "indicator of an indicator," as it is derived from the Accumulation/Distribution Line (ADL). The CHO measures the momentum of the ADL by comparing a fast and a slow exponential moving average (EMA) of the ADL.
Its primary purpose is to anticipate changes in the direction of the ADL, and by extension, to signal shifts in buying and selling pressure. It does not measure overbought or oversold levels but rather the momentum of money flow, making it a valuable tool for confirming trends and spotting divergences.
## 2. Mathematical Foundations and Calculation Logic
The Chaikin Oscillator is calculated by subtracting a slow EMA of the Accumulation/Distribution Line from a fast EMA of the ADL.
### Required Components
- **Accumulation/Distribution Line (ADL):** The underlying cumulative money flow indicator.
- **Fast EMA Period:** The period for the shorter-term EMA of the ADL (standard is 3).
- **Slow EMA Period:** The period for the longer-term EMA of the ADL (standard is 10).
### Calculation Steps (Algorithm)
1. **Calculate the Accumulation/Distribution Line (ADL):** First, the full ADL data series is calculated.
- $\text{Money Flow Multiplier (MFM)} = \frac{(\text{Close} - \text{Low}) - (\text{High} - \text{Close})}{\text{High} - \text{Low}}$
- $\text{Money Flow Volume (MFV)} = \text{MFM} \times \text{Volume}$
- $\text{ADL}_i = \text{ADL}_{i-1} + \text{MFV}_i$
2. **Calculate the Fast and Slow EMAs of the ADL:** Compute two separate EMAs on the ADL data series calculated in the first step.
$\text{FastEMA}_{\text{ADL}} = \text{EMA}(\text{ADL}, \text{Fast Period})$
$\text{SlowEMA}_{\text{ADL}} = \text{EMA}(\text{ADL}, \text{Slow Period})$
3. **Calculate the Chaikin Oscillator:** Subtract the Slow EMA from the Fast EMA.
$\text{CHO}_i = \text{FastEMA}_{\text{ADL}, i} - \text{SlowEMA}_{\text{ADL}, i}$
## 3. MQL5 Implementation Details
Our MQL5 implementation is a self-contained, robust, and flexible representation of the Chaikin Oscillator.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This ensures that the multi-stage calculation (Price -> ADL -> EMAs -> CHO) remains stable and accurate.
- **Self-Contained Logic:** The indicator is completely self-contained and does not use any external handles (like `iAD`). All calculations, including the underlying ADL and the subsequent moving averages, are performed manually within the `OnCalculate` function.
- **Flexible MA Types:** While the classic CHO uses EMAs, our "Pro" version allows the user to select from four different moving average types (**SMA, EMA, SMMA, LWMA**) via the `InpMaMethod` input parameter, providing greater flexibility.
- **Robust MA Calculations:** All moving average calculations are performed manually to ensure 100% accuracy and consistency within our `non-timeseries` model. Recursive MA types (EMA, SMMA) are carefully initialized with a manual Simple Moving Average (SMA) to prevent floating-point overflows.
- **Heikin Ashi Variant (`CHO_HeikinAshi.mq5`):**
- Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but the underlying ADL is calculated from the smoothed Heikin Ashi `ha_high`, `ha_low`, and `ha_close` values.
- This results in a significantly smoother oscillator that filters out price noise and can provide clearer signals regarding the momentum of the underlying Heikin Ashi trend.
## 4. Parameters
- **Fast Period (`InpFastPeriod`):** The period for the shorter-term MA of the ADL. Default is `3`.
- **Slow Period (`InpSlowPeriod`):** The period for the longer-term MA of the ADL. Default is `10`.
- **MA Method (`InpMaMethod`):** The type of moving average to use for the Fast and Slow MAs. Default is `MODE_EMA`.
- **Volume Type (`InpVolumeType`):** Allows the user to select between Tick Volume and Real Volume.
## 5. Usage and Interpretation
- **Zero Line Crossovers:** This is the most direct signal from the CHO.
- **Bullish Crossover:** When the oscillator crosses above the zero line, it indicates that buying pressure (accumulation) is strengthening. This can be used to confirm an uptrend or a bullish reversal.
- **Bearish Crossover:** When the oscillator crosses below the zero line, it indicates that selling pressure (distribution) is strengthening. This can confirm a downtrend or a bearish reversal.
- **Divergence:** This is the CHO's most powerful signal.
- **Bullish Divergence:** Price makes a lower low, but the CHO makes a higher low. This suggests that selling pressure is waning despite the lower price, often foreshadowing a bottom.
- **Bearish Divergénce:** Price makes a higher high, but the CHO makes a lower high. This suggests that the rally is not supported by strong buying pressure and may be nearing exhaustion.
- **Caution:** The Chaikin Oscillator is a momentum indicator, not a trend indicator. It should be used in conjunction with price action analysis or trend-following tools to confirm signals and avoid trading against the primary trend.
-210
View File
@@ -1,210 +0,0 @@
//+------------------------------------------------------------------+
//| CHO.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.01" // Added selectable MA Method
#property description "Chaikin Oscillator with selectable MA type"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 4 // CHO, ADL, FastMA, SlowMA
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_label1 "CHO"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpFastPeriod = 3;
input int InpSlowPeriod = 10;
input ENUM_MA_METHOD InpMaMethod = MODE_EMA;
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK;
//--- Indicator Buffers ---
double BufferCHO[];
double BufferADL[];
double BufferFastMA[];
double BufferSlowMA[];
//--- Global Variables ---
int g_ExtFastPeriod, g_ExtSlowPeriod;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtFastPeriod = (InpFastPeriod < 1) ? 1 : InpFastPeriod;
g_ExtSlowPeriod = (InpSlowPeriod < 1) ? 1 : InpSlowPeriod;
if(g_ExtFastPeriod > g_ExtSlowPeriod)
{
int temp = g_ExtFastPeriod;
g_ExtFastPeriod = g_ExtSlowPeriod;
g_ExtSlowPeriod = temp;
}
SetIndexBuffer(0, BufferCHO, INDICATOR_DATA);
SetIndexBuffer(1, BufferADL, INDICATOR_CALCULATIONS);
SetIndexBuffer(2, BufferFastMA, INDICATOR_CALCULATIONS);
SetIndexBuffer(3, BufferSlowMA, INDICATOR_CALCULATIONS);
ArraySetAsSeries(BufferCHO, false);
ArraySetAsSeries(BufferADL, false);
ArraySetAsSeries(BufferFastMA, false);
ArraySetAsSeries(BufferSlowMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtSlowPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CHO(%d,%d)", g_ExtFastPeriod, g_ExtSlowPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Chaikin 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[])
{
if(rates_total < g_ExtSlowPeriod)
return(0);
//--- STEP 1: Calculate Accumulation/Distribution Line (ADL)
for(int i = 0; i < rates_total; i++)
{
double mfm = 0;
double range = high[i] - low[i];
if(range > 0)
{
mfm = ((close[i] - low[i]) - (high[i] - close[i])) / range;
}
long current_volume = (InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i];
double mfv = mfm * current_volume;
if(i > 0)
BufferADL[i] = BufferADL[i-1] + mfv;
else
BufferADL[i] = mfv;
}
//--- STEP 2: Calculate Fast MA on ADL
for(int i = g_ExtFastPeriod - 1; i < rates_total; i++)
{
switch(InpMaMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtFastPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=BufferADL[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
else
{
if(InpMaMethod == MODE_EMA)
{
double pr=2.0/(g_ExtFastPeriod+1.0);
BufferFastMA[i] = BufferADL[i]*pr + BufferFastMA[i-1]*(1.0-pr);
}
else
BufferFastMA[i] = (BufferFastMA[i-1]*(g_ExtFastPeriod-1)+BufferADL[i])/g_ExtFastPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
{
int weight=g_ExtFastPeriod-j;
lwma_sum+=BufferADL[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferFastMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=BufferADL[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
break;
}
}
//--- STEP 3: Calculate Slow MA on ADL
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
switch(InpMaMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtSlowPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=BufferADL[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
else
{
if(InpMaMethod == MODE_EMA)
{
double pr=2.0/(g_ExtSlowPeriod+1.0);
BufferSlowMA[i] = BufferADL[i]*pr + BufferSlowMA[i-1]*(1.0-pr);
}
else
BufferSlowMA[i] = (BufferSlowMA[i-1]*(g_ExtSlowPeriod-1)+BufferADL[i])/g_ExtSlowPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
{
int weight=g_ExtSlowPeriod-j;
lwma_sum+=BufferADL[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSlowMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=BufferADL[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
break;
}
}
//--- STEP 4: Calculate final Chaikin Oscillator value
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
BufferCHO[i] = BufferFastMA[i] - BufferSlowMA[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-241
View File
@@ -1,241 +0,0 @@
//+------------------------------------------------------------------+
//| CHO_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.01" // Added selectable MA Method
#property description "Chaikin Oscillator on Heikin Ashi data with selectable MA type"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 4 // CHO, ADL, FastMA, SlowMA
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrLightSeaGreen
#property indicator_label1 "HA_CHO"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpFastPeriod = 3;
input int InpSlowPeriod = 10;
input ENUM_MA_METHOD InpMaMethod = MODE_EMA;
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK;
//--- Indicator Buffers ---
double BufferCHO[];
double BufferADL[];
double BufferFastMA[];
double BufferSlowMA[];
//--- Global Objects and Variables ---
int g_ExtFastPeriod, g_ExtSlowPeriod;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtFastPeriod = (InpFastPeriod < 1) ? 1 : InpFastPeriod;
g_ExtSlowPeriod = (InpSlowPeriod < 1) ? 1 : InpSlowPeriod;
if(g_ExtFastPeriod > g_ExtSlowPeriod)
{
int temp = g_ExtFastPeriod;
g_ExtFastPeriod = g_ExtSlowPeriod;
g_ExtSlowPeriod = temp;
}
SetIndexBuffer(0, BufferCHO, INDICATOR_DATA);
SetIndexBuffer(1, BufferADL, INDICATOR_CALCULATIONS);
SetIndexBuffer(2, BufferFastMA, INDICATOR_CALCULATIONS);
SetIndexBuffer(3, BufferSlowMA, INDICATOR_CALCULATIONS);
ArraySetAsSeries(BufferCHO, false);
ArraySetAsSeries(BufferADL, false);
ArraySetAsSeries(BufferFastMA, false);
ArraySetAsSeries(BufferSlowMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtSlowPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CHO(%d,%d)", g_ExtFastPeriod, g_ExtSlowPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 0);
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;
}
}
//+------------------------------------------------------------------+
//| Chaikin Oscillator 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_ExtSlowPeriod)
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 Accumulation/Distribution Line (ADL) on HA data
for(int i = 0; i < rates_total; i++)
{
double mfm = 0;
double range = ha_high[i] - ha_low[i];
if(range > 0)
{
mfm = ((ha_close[i] - ha_low[i]) - (ha_high[i] - ha_close[i])) / range;
}
long current_volume = (InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i];
double mfv = mfm * current_volume;
if(i > 0)
BufferADL[i] = BufferADL[i-1] + mfv;
else
BufferADL[i] = mfv;
}
//--- STEP 3: Calculate Fast MA on ADL
for(int i = g_ExtFastPeriod - 1; i < rates_total; i++)
{
switch(InpMaMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtFastPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=BufferADL[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
else
{
if(InpMaMethod == MODE_EMA)
{
double pr=2.0/(g_ExtFastPeriod+1.0);
BufferFastMA[i] = BufferADL[i]*pr + BufferFastMA[i-1]*(1.0-pr);
}
else
BufferFastMA[i] = (BufferFastMA[i-1]*(g_ExtFastPeriod-1)+BufferADL[i])/g_ExtFastPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
{
int weight=g_ExtFastPeriod-j;
lwma_sum+=BufferADL[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferFastMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=BufferADL[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
break;
}
}
//--- STEP 4: Calculate Slow MA on ADL
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
switch(InpMaMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtSlowPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=BufferADL[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
else
{
if(InpMaMethod == MODE_EMA)
{
double pr=2.0/(g_ExtSlowPeriod+1.0);
BufferSlowMA[i] = BufferADL[i]*pr + BufferSlowMA[i-1]*(1.0-pr);
}
else
BufferSlowMA[i] = (BufferSlowMA[i-1]*(g_ExtSlowPeriod-1)+BufferADL[i])/g_ExtSlowPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
{
int weight=g_ExtSlowPeriod-j;
lwma_sum+=BufferADL[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSlowMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=BufferADL[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
break;
}
}
//--- STEP 5: Calculate final Chaikin Oscillator value
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
BufferCHO[i] = BufferFastMA[i] - BufferSlowMA[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-74
View File
@@ -1,74 +0,0 @@
# 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.
The **Cutler's RSI Oscillator** is a supplementary indicator that displays the difference between the main RSI line and its signal line as a histogram, providing a clearer visual of accelerating and decelerating momentum.
## 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)$.
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}$
5. **Calculate the Signal Line & Oscillator:** The signal line is a moving average of the Cutler's RSI line, and the oscillator is the difference between the two.
## 3. MQL5 Implementation Details
Our MQL5 implementations were refactored for maximum stability, clarity, and computational efficiency.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function for maximum 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. This is mathematically equivalent to an SMA but significantly faster.
- **Self-Contained Logic:** The indicators are completely self-contained. They do not use external handles and directly process the price arrays provided by `OnCalculate`.
- **Fully Manual MA Calculations:** To guarantee 100% accuracy and consistency, all moving average calculations for the signal line (**SMA, EMA, SMMA, LWMA**) are performed **manually**. This makes the indicators independent of the `<MovingAverages.mqh>` library and ensures robust behavior on `non-timeseries` arrays.
- **Indicator Family:**
- **Line Versions:** `CutlerRSI_MA.mq5` plots the RSI line and its signal line.
- **Oscillator Versions:** `CutlerRSI_Oscillator.mq5` plots the difference between the two lines as a histogram.
- **Heikin Ashi Variants:** Both indicators have "pure" Heikin Ashi counterparts, which use the smoothed Heikin Ashi `ha_close` values as their input.
## 4. Parameters
- **RSI Period (`InpPeriodRSI`):** The lookback period for the SMA of price changes. Default is `14`.
- **Applied Price (`InpAppliedPrice`):** The source price for the calculation. Default is `PRICE_CLOSE`.
- **Signal Line Settings:**
- `InpPeriodMA`: The lookback period for the optional signal line.
- `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.
- **Oscillator (Histogram):** The histogram provides a clear visual of the relationship between the Cutler's RSI and its signal line, highlighting the acceleration and deceleration of momentum.
-220
View File
@@ -1,220 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MovingAverages.mqh>
//--- 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i]= (high[i] + low[i] + 2*close[i]) / 4.0;
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate Cutler's RSI (SMA-based) using a sliding window sum
double sum_pos = 0, sum_neg = 0;
for(int i = 1; i < rates_total; i++)
{
double diff = price_source[i] - price_source[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 = 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)
int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
for(int i = ma_start_pos; i < rates_total; i++)
{
// --- FIX: Full, robust switch block for all MA types ---
switch(InpMethodMA)
{
case MODE_EMA:
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
{
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:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=BufferCutlerRSI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferCutlerRSI_MA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferCutlerRSI[i-j];
BufferCutlerRSI_MA[i] = sum/g_ExtPeriodMA;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,213 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MovingAverages.mqh>
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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)
int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
for(int i = ma_start_pos; i < rates_total; i++)
{
// --- FIX: Full, robust switch block for all MA types ---
switch(InpMethodMA)
{
case MODE_EMA:
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
{
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:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=BufferCutlerRSI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferCutlerRSI_MA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferCutlerRSI[i-j];
BufferCutlerRSI_MA[i] = sum/g_ExtPeriodMA;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,185 +0,0 @@
//+------------------------------------------------------------------+
//| CutlerRSI_Oscillator.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Cutler's RSI Oscillator (Histogram of RSI vs Signal Line)"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "Cutler's RSI Osc"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpPeriodRSI = 14;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
input group "Signal Line Settings"
input int InpPeriodMA = 14;
input ENUM_MA_METHOD InpMethodMA = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Variables ---
int g_ExtPeriodRSI, g_ExtPeriodMA;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI;
g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int draw_begin = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("CutlerRSI Osc(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Cutler's RSI 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[])
{
int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
if(rates_total <= start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_rsi[], buffer_signal[];
ArrayResize(buffer_rsi, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate Cutler's RSI internally ---
{
double price_source[];
ArrayResize(price_source, rates_total);
for(int i=0; i<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
default:
price_source[i] = close[i];
break;
}
}
double sum_pos = 0, sum_neg = 0;
for(int i = 1; i < rates_total; i++)
{
double diff = price_source[i] - price_source[i-1];
double pos_change = (diff > 0) ? diff : 0;
double neg_change = (diff < 0) ? -diff : 0;
sum_pos += pos_change;
sum_neg += neg_change;
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);
buffer_rsi[i] = 100.0 - (100.0 / (1.0 + rs));
}
else
buffer_rsi[i] = 100.0;
}
}
}
//--- STEP 2: 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+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
else
{
if(InpMethodMA == MODE_EMA)
{
double pr=2.0/(g_ExtPeriodMA+1.0);
buffer_signal[i] = buffer_rsi[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtPeriodMA-1)+buffer_rsi[i])/g_ExtPeriodMA;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=buffer_rsi[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_rsi[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,191 +0,0 @@
//+------------------------------------------------------------------+
//| CutlerRSI_Oscillator_HeikinAshi.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Cutler's RSI Oscillator on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "HA_CutlerRSI_Osc"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpPeriodRSI = 14;
input group "Signal Line Settings"
input int InpPeriodMA = 14;
input ENUM_MA_METHOD InpMethodMA = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Objects and Variables ---
int g_ExtPeriodRSI, g_ExtPeriodMA;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI;
g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int draw_begin = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_CutlerRSI_Osc(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| Cutler's RSI Oscillator 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_ExtPeriodRSI + g_ExtPeriodMA - 1;
if(rates_total <= start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_rsi[], buffer_signal[];
ArrayResize(buffer_rsi, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate Heikin Ashi Cutler's RSI internally ---
{
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);
g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close);
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;
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);
buffer_rsi[i] = 100.0 - (100.0 / (1.0 + rs));
}
else
buffer_rsi[i] = 100.0;
}
}
}
//--- STEP 2: 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+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
else
{
if(InpMethodMA == MODE_EMA)
{
double pr=2.0/(g_ExtPeriodMA+1.0);
buffer_signal[i] = buffer_rsi[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtPeriodMA-1)+buffer_rsi[i])/g_ExtPeriodMA;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=buffer_rsi[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_rsi[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-65
View File
@@ -1,65 +0,0 @@
# Fibonacci Weighted Moving Average (Fibonacci WMA)
## 1. Summary (Introduction)
The Fibonacci Weighted Moving Average (Fibonacci WMA) is a specialized type of weighted moving average that uses the Fibonacci number sequence to assign weights to price data. Unlike a Simple Moving Average (SMA) where all prices are weighted equally, the Fibonacci WMA assigns exponentially increasing weights to more recent prices.
The core principle is rooted in the idea that the most recent price action is exponentially more significant than older data. By using the Fibonacci sequence (1, 1, 2, 3, 5, 8, ...), the indicator creates a smooth, responsive moving average that closely follows the trend while effectively filtering out minor market noise. It is a pure **trend-following tool** designed to identify and track the direction of the market.
## 2. Mathematical Foundations and Calculation Logic
The Fibonacci WMA calculates a weighted average where the weights are determined by the numbers in the Fibonacci sequence.
### Required Components
- **Period (N):** The lookback period for the moving average.
- **Source Price:** The price series used for calculation (e.g., `PRICE_CLOSE`).
### Calculation Steps (Algorithm)
1. **Generate Fibonacci Weights:** First, generate the first `N` numbers of the Fibonacci sequence (e.g., for N=5: 1, 1, 2, 3, 5).
2. **Calculate the Weighted Sum:** For each bar `t`, multiply the last `N` prices by the corresponding Fibonacci numbers. The **most recent price gets the largest Fibonacci number** as its weight, and the oldest price in the period gets the smallest weight.
- $\text{Weighted Sum}_t = \sum_{i=0}^{N-1} (\text{Price}_{t-i} \cdot Fib_{N-i})$
3. **Calculate the Sum of Weights:** Sum the first `N` Fibonacci numbers used as weights.
- $\text{Sum of Weights} = \sum_{i=1}^{N} Fib_i$
4. **Calculate the Final WMA Value:** Divide the weighted sum of prices by the sum of the weights.
- $\text{Fibonacci WMA}_t = \frac{\text{Weighted Sum}_t}{\text{Sum of Weights}}$
This process results in an asymmetrically weighted average that is highly sensitive to recent price changes.
## 3. MQL5 Implementation Details
Our MQL5 implementation is a clean, robust, and self-contained indicator that accurately reflects the mathematical definition of the Fibonacci WMA.
- **Modular, Reusable Calculation Engine (`Fibonacci_WMA_Calculator.mqh`):** The entire calculation logic for both standard and Heikin Ashi versions is encapsulated within a single, powerful include file.
- **`CFibonacciWMACalculator`**: The base class that performs the calculation on standard price data.
- **`CFibonacciWMACalculator_HA`**: A child class that inherits from the base class and overrides the data preparation step to use smoothed Heikin Ashi prices as its input. This object-oriented approach eliminates code duplication.
- **Efficient Weight Generation:** The Fibonacci weights are calculated only once during the indicator's initialization in the `Init()` method. The weights are generated and then assigned to the internal weights array in **reverse order**, ensuring that the largest weight is at index `0`. This simplifies the main calculation loop and improves performance.
- **Stability via Full Recalculation:** In line with our core principles, the indicator employs a "brute-force" full recalculation within the `OnCalculate` function. This ensures maximum stability and prevents any potential glitches, while keeping the code simple and robust.
- **Correct Weight Application:** The `Calculate` method applies the pre-calculated weights directly. The most recent price (`m_price[i - j]` where `j=0`) is correctly multiplied by the largest weight (`m_weights[j]` where `j=0`), ensuring the proper trend-following behavior of the indicator.
- **Overflow Protection:** The Fibonacci sequence grows exponentially. To prevent potential `long` integer overflow with very large periods, the implementation caps the calculation period at `40`.
- **Heikin Ashi Variant (`Fibonacci_WMA_HeikinAshi.mq5`):**
- Our toolkit also includes a Heikin Ashi version. It uses the same robust calculation engine, but the engine is configured to first transform the standard OHLC prices into Heikin Ashi prices and then perform the WMA calculation on the **Heikin Ashi Close** values.
- This results in an even smoother, more trend-stable moving average, ideal for strategies that aim to filter out market noise as much as possible.
## 4. Parameters
- **Period (`InpPeriod`):** The lookback period for the moving average. A longer period results in a smoother, slower-reacting average, while a shorter period makes it more sensitive to price changes. Default is `21`.
- **Source Price (`InpSourcePrice`):** The price data used for the calculation (Close, Open, High, Low, etc.). **Note: This parameter is ignored by the Heikin Ashi version**, which always uses the HA Close price. Default is `PRICE_CLOSE`.
## 5. Usage and Interpretation
The Fibonacci WMA should be interpreted as a responsive, trend-following moving average.
- **Trend Identification:** The primary use is to identify the direction of the trend. When the price is consistently above the Fibonacci WMA and the line is sloping upwards, it indicates a bullish trend. When the price is below the line and the line is sloping downwards, it indicates a bearish trend.
- **Dynamic Support and Resistance:** In a strong trend, the Fibonacci WMA can act as a dynamic level of support (in an uptrend) or resistance (in a downtrend). Pullbacks to the line can offer potential entry opportunities in the direction of the trend.
- **Crossover Signals:** The crossover of the price and the Fibonacci WMA can be used as a basic trading signal. A price close above the line can be a buy signal, while a close below can be a sell signal.
- **Caution:** Like all moving averages, the Fibonacci WMA is a lagging indicator and can produce false signals in sideways or choppy markets. It is most effective when used in clearly trending markets and in conjunction with other forms of analysis to confirm signals.
-76
View File
@@ -1,76 +0,0 @@
//+------------------------------------------------------------------+
//| Fibonacci_WMA.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "2.00"
#property description "Fibonacci Weighted Moving Average."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#include <MyIncludes\Fibonacci_WMA_Calculator.mqh>
//--- Plot 1: Fibonacci WMA Line
#property indicator_label1 "Fibonacci WMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 21;
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferWMA[];
//--- Global calculator object ---
CFibonacciWMACalculator *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferWMA, INDICATOR_DATA);
ArraySetAsSeries(BufferWMA, false);
g_calculator = new CFibonacciWMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod))
{
Print("Failed to initialize Fibonacci WMA Calculator.");
return(INIT_FAILED);
}
int actual_period = InpPeriod > 40 ? 40 : InpPeriod;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, actual_period - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("FibonacciWMA(%d)", InpPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
g_calculator.Calculate(rates_total, InpSourcePrice, open, high, low, close, BufferWMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,76 +0,0 @@
//+------------------------------------------------------------------+
//| Fibonacci_WMA_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Fibonacci Weighted Moving Average on Heikin Ashi data."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#include <MyIncludes\Fibonacci_WMA_Calculator.mqh>
//--- Plot 1: Fibonacci WMA Line
#property indicator_label1 "Fibonacci WMA (HA)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 21;
//--- Indicator Buffers ---
double BufferWMA[];
//--- Global calculator object ---
CFibonacciWMACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferWMA, INDICATOR_DATA);
ArraySetAsSeries(BufferWMA, false);
g_calculator = new CFibonacciWMACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod))
{
Print("Failed to initialize Fibonacci WMA HA Calculator.");
return(INIT_FAILED);
}
int actual_period = InpPeriod > 40 ? 40 : InpPeriod;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, actual_period - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("FibonacciWMA_HA(%d)", InpPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
//--- The price_type parameter is ignored by the HA calculator, so we can pass a default
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, BufferWMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,66 +0,0 @@
# 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.
-181
View File
@@ -1,181 +0,0 @@
//+------------------------------------------------------------------+
//| 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<rates_total; i++)
{
hl2[i] = (high[i] + low[i]) / 2.0;
}
//--- STEP 2: Main calculation loop for Fisher Transform
for(int i = 1; i < rates_total; i++)
{
if(i < g_ExtLength)
continue;
double high_ = Highest(hl2, g_ExtLength, i);
double low_ = Lowest(hl2, g_ExtLength, i);
double range = high_ - low_;
if(range < _Point)
range = _Point;
double price_pos = (hl2[i] - low_) / range - 0.5;
// Recursive smoothing for 'value'
BufferValue[i] = 0.33 * 2 * price_pos + 0.67 * BufferValue[i-1];
// Clamp the value to prevent log() errors
if(BufferValue[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);
}
//+------------------------------------------------------------------+
@@ -1,226 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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<rates_total; i++)
{
ha_hl2[i] = (ExtHaHighBuffer[i] + ExtHaLowBuffer[i]) / 2.0;
}
//--- STEP 3: Main calculation loop for Fisher Transform
for(int i = 1; i < rates_total; i++)
{
// Skip bars that don't have enough history for the period
if(i < g_ExtLength)
continue;
// Get Highest/Lowest of Heikin Ashi HL2
double high_ = Highest(ha_hl2, g_ExtLength, i);
double low_ = Lowest(ha_hl2, g_ExtLength, i);
double range = high_ - low_;
if(range < _Point)
range = _Point;
// Calculate the intermediate 'value'
double price_pos = (ha_hl2[i] - low_) / range - 0.5;
BufferValue[i] = 0.33 * 2 * price_pos + 0.67 * BufferValue[i-1];
// Clamp the value to avoid issues with MathLog
if(BufferValue[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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-66
View File
@@ -1,66 +0,0 @@
# 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 `<MovingAverages.mqh>` 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.
-189
View File
@@ -1,189 +0,0 @@
//+------------------------------------------------------------------+
//| 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<g_ExtPeriod; j++)
{
sum_h += high[i-j];
sum_l += low[i-j];
}
BufferHiAvg[i] = sum_h / g_ExtPeriod;
BufferLoAvg[i] = sum_l / g_ExtPeriod;
}
else // Recursive calculation
{
if(InpMAMethod == MODE_EMA)
{
double pr = 2.0 / (g_ExtPeriod + 1.0);
BufferHiAvg[i] = high[i] * pr + BufferHiAvg[i-1] * (1.0 - pr);
BufferLoAvg[i] = low[i] * pr + BufferLoAvg[i-1] * (1.0 - pr);
}
else
{
BufferHiAvg[i] = (BufferHiAvg[i-1] * (g_ExtPeriod - 1) + high[i]) / g_ExtPeriod;
BufferLoAvg[i] = (BufferLoAvg[i-1] * (g_ExtPeriod - 1) + low[i]) / g_ExtPeriod;
}
}
break;
case MODE_LWMA:
{
double lwma_sum_h=0, lwma_sum_l=0;
double weight_sum=0;
for(int j=0; j<g_ExtPeriod; j++)
{
int weight = g_ExtPeriod - j;
lwma_sum_h += high[i-j] * weight;
lwma_sum_l += low[i-j] * weight;
weight_sum += weight;
}
if(weight_sum > 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<g_ExtPeriod; j++)
{
sma_sum_high += high[i-j];
sma_sum_low += low[i-j];
}
}
else
{
sma_sum_high += high[i] - high[i - g_ExtPeriod];
sma_sum_low += low[i] - low[i - g_ExtPeriod];
}
BufferHiAvg[i] = sma_sum_high / g_ExtPeriod;
BufferLoAvg[i] = sma_sum_low / g_ExtPeriod;
break;
}
// --- STEP 2: Determine trend and set the final Gann HiLo value ---
if(i < g_ExtPeriod)
continue; // Trend logic starts one bar later
if(close[i] > 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,190 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MovingAverages.mqh>
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-60
View File
@@ -1,60 +0,0 @@
# 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 `<MovingAverages.mqh>` 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.
-163
View File
@@ -1,163 +0,0 @@
//+------------------------------------------------------------------+
//| 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i]= (high[i] + low[i] + 2*close[i]) / 4.0;
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate all HMA components
int period_half = (int)MathMax(1, MathRound(g_ExtPeriodHMA / 2.0));
int period_sqrt = (int)MathMax(1, MathRound(MathSqrt(g_ExtPeriodHMA)));
// --- First Pass: Calculate base WMAs and Raw HMA ---
for(int i = g_ExtPeriodHMA - 1; i < rates_total; i++)
{
// Manual WMA for half period
double lwma_sum_half = 0;
double weight_sum_half = 0;
for(int j=0; j<period_half; j++)
{
int weight = period_half - j;
lwma_sum_half += price_source[i-j] * weight;
weight_sum_half += weight;
}
if(weight_sum_half > 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<g_ExtPeriodHMA; j++)
{
int weight = g_ExtPeriodHMA - j;
lwma_sum_full += price_source[i-j] * weight;
weight_sum_full += weight;
}
if(weight_sum_full > 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<period_sqrt; j++)
{
int weight = period_sqrt - j;
lwma_sum_sqrt += BufferRawHMA[i-j] * weight;
weight_sum_sqrt += weight;
}
if(weight_sum_sqrt > 0)
BufferHMA[i] = lwma_sum_sqrt / weight_sum_sqrt;
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-169
View File
@@ -1,169 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
#include <MovingAverages.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-125
View File
@@ -1,125 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_Channel.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Holt's Forecast Channel. Displays a channel based on"
#property description "the multi-period forecast of the Holt's Linear Trend model."
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Upper Band
#property indicator_label1 "Upper Channel"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrSilver
#property indicator_style1 STYLE_DOT
#property indicator_width1 1
//--- Plot 2: Lower Band
#property indicator_label2 "Lower Channel"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrSilver
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Plot 3: Center Line (Holt MA)
#property indicator_label3 "Center Line"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrMediumSeaGreen
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
input int InpForecastPeriod = 5; // Forecast period for the channel
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferUpperBand[];
double BufferLowerBand[];
double BufferCenterLine[];
//--- Global calculator object ---
CHoltMACalculator *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferUpperBand, INDICATOR_DATA);
SetIndexBuffer(1, BufferLowerBand, INDICATOR_DATA);
SetIndexBuffer(2, BufferCenterLine, INDICATOR_DATA);
ArraySetAsSeries(BufferUpperBand, false);
ArraySetAsSeries(BufferLowerBand, false);
ArraySetAsSeries(BufferCenterLine, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 2);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt Channel(%d, %d)", InpPeriod, InpForecastPeriod));
g_calculator = new CHoltMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration 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(CheckPointer(g_calculator) != POINTER_INVALID)
{
//--- Step 1: Run the main calculation to get the core components
double trend_buffer[];
g_calculator.Calculate(rates_total, InpSourcePrice, open, high, low, close, BufferCenterLine, trend_buffer);
//--- Step 2: Calculate the channel bands based on the forecast and trend
int forecast_period = (InpForecastPeriod < 1) ? 1 : InpForecastPeriod;
for(int i = 2; i < rates_total; i++)
{
// Reconstruct the Level component: Level = Forecast - Trend
double level = BufferCenterLine[i] - trend_buffer[i];
// Calculate the multi-period forecast for the bands
BufferUpperBand[i] = level + forecast_period * trend_buffer[i];
BufferLowerBand[i] = level - forecast_period * trend_buffer[i];
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,107 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_Channel_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Holt's Forecast Channel on Heikin Ashi data."
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Upper Band
#property indicator_label1 "Upper Channel (HA)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrSilver
#property indicator_style1 STYLE_DOT
#property indicator_width1 1
//--- Plot 2: Lower Band
#property indicator_label2 "Lower Channel (HA)"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrSilver
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Plot 3: Center Line (Holt MA)
#property indicator_label3 "Center Line (HA)"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrMediumSeaGreen
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
input int InpForecastPeriod = 5;
//--- Indicator Buffers ---
double BufferUpperBand[];
double BufferLowerBand[];
double BufferCenterLine[];
//--- Global calculator object ---
CHoltMACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferUpperBand, INDICATOR_DATA);
SetIndexBuffer(1, BufferLowerBand, INDICATOR_DATA);
SetIndexBuffer(2, BufferCenterLine, INDICATOR_DATA);
ArraySetAsSeries(BufferUpperBand, false);
ArraySetAsSeries(BufferLowerBand, false);
ArraySetAsSeries(BufferCenterLine, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 2);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt Channel HA(%d, %d)", InpPeriod, InpForecastPeriod));
g_calculator = new CHoltMACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA HA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
double trend_buffer[], level_buffer[];
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, BufferCenterLine, trend_buffer, level_buffer);
int forecast_period = (InpForecastPeriod < 1) ? 1 : InpForecastPeriod;
for(int i = 2; i < rates_total; i++)
{
BufferUpperBand[i] = level_buffer[i] + forecast_period * trend_buffer[i];
BufferLowerBand[i] = level_buffer[i] - forecast_period * trend_buffer[i];
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-85
View File
@@ -1,85 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_MA.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "2.00"
#property description "Holt's Linear Trend Method (Double Exponential Smoothing)."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Holt MA Forecast Line
#property indicator_label1 "Holt MA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferHoltMA[];
//--- Global calculator object ---
CHoltMACalculator *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferHoltMA, INDICATOR_DATA);
ArraySetAsSeries(BufferHoltMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt MA(%d, %.2f, %.2f)", InpPeriod, InpAlpha, InpBeta));
g_calculator = new CHoltMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration 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(CheckPointer(g_calculator) != POINTER_INVALID)
{
double dummy_trend[];
g_calculator.Calculate(rates_total, InpSourcePrice, open, high, low, close, BufferHoltMA, dummy_trend);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,75 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_MA_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Holt's Linear Trend Method on Heikin Ashi data."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Holt MA Forecast Line
#property indicator_label1 "Holt MA (HA)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumSeaGreen
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
//--- Indicator Buffers ---
double BufferHoltMA[];
//--- Global calculator object ---
CHoltMACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferHoltMA, INDICATOR_DATA);
ArraySetAsSeries(BufferHoltMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt MA HA(%d, %.2f, %.2f)", InpPeriod, InpAlpha, InpBeta));
g_calculator = new CHoltMACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA HA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
double dummy_trend[], dummy_level[];
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, BufferHoltMA, dummy_trend, dummy_level);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,89 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_Oscillator.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "2.00"
#property description "Holt's Trend Oscillator. Shows the smoothed trend component."
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
#property indicator_levelcolor clrGray
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Holt Trend Oscillator
#property indicator_label1 "Holt Trend"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSeaGreen, clrTomato
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global calculator object ---
CHoltMACalculator *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt Osc(%d, %.2f, %.2f)", InpPeriod, InpAlpha, InpBeta));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits+2);
g_calculator = new CHoltMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration 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(CheckPointer(g_calculator) != POINTER_INVALID)
{
double dummy_forecast[];
g_calculator.Calculate(rates_total, InpSourcePrice, open, high, low, close, dummy_forecast, BufferOscillator);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,79 +0,0 @@
//+------------------------------------------------------------------+
//| Holt_Oscillator_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Holt's Trend Oscillator on Heikin Ashi data."
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
#property indicator_levelcolor clrGray
#include <MyIncludes\Holt_Calculator.mqh>
//--- Plot 1: Holt Trend Oscillator
#property indicator_label1 "Holt Trend (HA)"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSeaGreen, clrTomato
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 20;
input double InpAlpha = 0.1;
input double InpBeta = 0.05;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global calculator object ---
CHoltMACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 2);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Holt Osc HA(%d, %.2f, %.2f)", InpPeriod, InpAlpha, InpBeta));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits+2);
g_calculator = new CHoltMACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpAlpha, InpBeta))
{
Print("Failed to initialize Holt MA HA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
double dummy_forecast[], dummy_level[];
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, dummy_forecast, BufferOscillator, dummy_level);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-72
View File
@@ -1,72 +0,0 @@
# 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.
-158
View File
@@ -1,158 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MovingAverages.mqh> // 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,255 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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<g_ExtMaPeriod; j++)
sum += ha_price_source[i-j];
BufferMiddle[i] = sum / g_ExtMaPeriod;
}
else
{
if(InpMaMethod == MODE_EMA)
{
double pr = 2.0 / (g_ExtMaPeriod + 1.0);
BufferMiddle[i] = ha_price_source[i] * pr + BufferMiddle[i-1] * (1.0 - pr);
}
else
{
BufferMiddle[i] = (BufferMiddle[i-1] * (g_ExtMaPeriod - 1) + ha_price_source[i]) / g_ExtMaPeriod;
}
}
break;
case MODE_LWMA:
{
double lwma_sum = 0;
double weight_sum = 0;
for(int j=0; j<g_ExtMaPeriod; j++)
{
int weight = g_ExtMaPeriod - j;
lwma_sum += ha_price_source[i-j] * weight;
weight_sum += weight;
}
if(weight_sum > 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<g_ExtMaPeriod; j++)
sma_sum += ha_price_source[i-j];
}
else // Subsequent calculations use the sliding window
{
sma_sum += ha_price_source[i];
sma_sum -= ha_price_source[i - g_ExtMaPeriod];
}
BufferMiddle[i] = sma_sum / g_ExtMaPeriod;
break;
}
}
// --- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,230 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
#include <MovingAverages.mqh> // <-- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,47 +0,0 @@
# Linear Regression Channel
## 1. Summary (Introduction)
The Linear Regression Channel is a technical analysis tool that consists of three parallel lines plotted on a price chart. It is a statistically-based indicator that uses the **linear regression** (or "least squares fit") method to determine the primary trend direction.
- The **Middle Line** is the actual linear regression trendline.
- The **Upper and Lower Channel Lines** are plotted based on the **maximum price deviation** from the middle line over the calculation period.
The indicator provides an objective, mathematical measure of a trend and its trading channel.
## 2. Mathematical Foundations and Calculation Logic
The indicator's core is the linear regression trendline, which is the straight line that best fits a series of `Close` prices over a specified period.
### Calculation Steps (Algorithm)
For the last `N` bars at any given point in time:
1. **Calculate the Linear Regression Line:** Using the method of least squares, find the straight line that best fits the `N` closing prices.
2. **Calculate Maximum Deviation:** Find the largest vertical distance between any of the `N` closing prices and the calculated regression line.
3. **Calculate the Upper and Lower Channel Lines:** Shift the regression line up and down by the maximum deviation found in the previous step.
**Important Note on "Repainting":** The Linear Regression Channel is a "repainting" indicator by nature. Because the entire line is recalculated for the most recent `N` bars every time a new bar forms, its position in the recent past can change.
## 3. MQL5 Implementation Details
Our MQL5 implementation is designed to be highly efficient and visually clean by leveraging MetaTrader 5's built-in **`OBJ_REGRESSION`** graphical object.
- **Object-Based Plotting:** The indicator uses a single, built-in `OBJ_REGRESSION` object. This object is managed by the MetaTrader terminal, which handles the complex regression and maximum deviation calculations internally using highly optimized code.
- **Clean, Non-Continuous Display:** By default, the indicator only displays the single, most current regression channel calculated on the last `N` bars.
- **Efficient "On New Bar" Updates:** The indicator is extremely light on terminal resources. The channel object is only updated **once per bar** when a new candle forms, preventing unnecessary recalculations on every tick.
- **Automatic Cleanup (RAII):** The graphical object is given a unique name and is always deleted from the chart when the indicator is removed.
## 4. Parameters
- **Regression Period (`InpRegressionPeriod`):** The number of bars to include in the regression calculation. Default is `100`.
- **Channel Color (`InpChannelColor`):** Allows the user to customize the color of the channel lines. Default is `clrRed`.
- **Channel Extensions:**
- **`InpRayRight`**: If `true`, the channel is extended indefinitely into the future. Default is `false`.
- **`InpRayLeft`**: If `true`, the channel is extended indefinitely into the past. Default is `false`.
## 5. Usage and Interpretation
- **Trend Identification:** The slope of the middle line indicates the direction of the trend.
- **Dynamic Support and Resistance:** The channel lines act as dynamic support and resistance levels.
- **Caution:** Due to its repainting nature, the indicator is best used for confirming the current market structure rather than for generating precise entry signals from past data.
@@ -1,124 +0,0 @@
//+------------------------------------------------------------------+
//| LinearRegressionChannel.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.01" // Added color selection
#property description "Draws a Linear Regression Channel where width is based on max deviation."
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//--- Input Parameters ---
input int InpRegressionPeriod = 100; // Period for the regression calculation
input color InpChannelColor = clrRed; // Channel color
input group "Channel Extensions"
input bool InpRayRight = false; // Extend channel to the right
input bool InpRayLeft = false; // Extend channel to the left
//--- Global Variables ---
int g_ExtPeriod;
string g_channel_name;
datetime g_last_update_time;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriod = (InpRegressionPeriod < 2) ? 2 : InpRegressionPeriod;
g_channel_name = "LinRegChannel_" + IntegerToString(ChartID()) + "_" + IntegerToString(GetTickCount());
g_last_update_time = 0;
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("LinReg(%d)", g_ExtPeriod));
EventSetTimer(1);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
ObjectDelete(0, g_channel_name);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Timer event handler. |
//+------------------------------------------------------------------+
void OnTimer()
{
UpdateChannel();
}
//+------------------------------------------------------------------+
//| Main 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);
datetime last_bar_time = time[rates_total - 1];
if(last_bar_time > g_last_update_time)
{
UpdateChannel();
g_last_update_time = last_bar_time;
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Updates the position and properties of the regression channel. |
//+------------------------------------------------------------------+
void UpdateChannel()
{
if(Bars(_Symbol, _Period) < g_ExtPeriod)
return;
datetime time1 = iTime(_Symbol, _Period, g_ExtPeriod - 1);
datetime time2 = iTime(_Symbol, _Period, 0);
if(ObjectFind(0, g_channel_name) < 0)
{
if(!ObjectCreate(0, g_channel_name, OBJ_REGRESSION, 0, time1, 0, time2, 0))
{
Print("Error creating regression channel object: ", GetLastError());
return;
}
// Set visual properties only once on creation
ObjectSetInteger(0, g_channel_name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, g_channel_name, OBJPROP_FILL, false);
ObjectSetInteger(0, g_channel_name, OBJPROP_SELECTABLE, false);
}
// Update properties on every call to allow for dynamic changes
ObjectSetInteger(0, g_channel_name, OBJPROP_TIME, 0, time1);
ObjectSetInteger(0, g_channel_name, OBJPROP_TIME, 1, time2);
ObjectSetInteger(0, g_channel_name, OBJPROP_RAY_RIGHT, InpRayRight);
ObjectSetInteger(0, g_channel_name, OBJPROP_RAY_LEFT, InpRayLeft);
// --- FIX: Set color based on input ---
ObjectSetInteger(0, g_channel_name, OBJPROP_COLOR, InpChannelColor);
ChartRedraw();
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,246 +0,0 @@
//+------------------------------------------------------------------+
//| LinearRegression_Pro_HeikinAshi.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "A flexible, manually calculated Linear Regression Channel on Heikin Ashi data."
#property description "Updates only on new bars for efficiency."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_chart_window
#property indicator_buffers 3 // Upper, Lower, Middle
#property indicator_plots 3
//--- Plot 1: Upper Channel
#property indicator_label1 "HA_Upper"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_DOT
//--- Plot 2: Lower Channel
#property indicator_label2 "HA_Lower"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrDodgerBlue
#property indicator_style2 STYLE_DOT
//--- Plot 3: Regression Line (Middle)
#property indicator_label3 "HA_Regression"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrRed
#property indicator_style3 STYLE_SOLID
//--- Enum for Channel Calculation Mode ---
enum ENUM_CHANNEL_MODE
{
DEVIATION_STANDARD, // Channel width based on Standard Deviation
DEVIATION_MAXIMUM // Channel width based on Maximum Deviation
};
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW, HA_PRICE_TYPICAL, HA_PRICE_MEDIAN
};
//--- Input Parameters ---
input int InpRegressionPeriod = 100;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE;
input ENUM_CHANNEL_MODE InpChannelMode = DEVIATION_STANDARD;
input double InpDeviations = 2.0;
//--- Indicator Buffers ---
double BufferUpper[];
double BufferLower[];
double BufferMiddle[];
//--- Global Objects and Variables ---
int g_ExtPeriod;
double g_ExtDeviations;
datetime g_last_update_time;
CHeikinAshi_Calculator *g_ha_calculator;
//--- Forward declarations ---
void CalculateChannel(int rates_total, const double &ha_open[], const double &ha_high[], const double &ha_low[], const double &ha_close[]);
double GetHAPrice(int index, ENUM_HA_APPLIED_PRICE type, const double &ha_open[], const double &ha_high[], const double &ha_low[], const double &ha_close[]);
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriod = (InpRegressionPeriod < 2) ? 2 : InpRegressionPeriod;
g_ExtDeviations = (InpDeviations <= 0) ? 2.0 : InpDeviations;
g_last_update_time = 0;
SetIndexBuffer(0, BufferUpper, INDICATOR_DATA);
SetIndexBuffer(1, BufferLower, INDICATOR_DATA);
SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA);
ArraySetAsSeries(BufferUpper, false);
ArraySetAsSeries(BufferLower, false);
ArraySetAsSeries(BufferMiddle, false);
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA LinReg Pro(%d)", g_ExtPeriod));
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;
}
}
//+------------------------------------------------------------------+
//| Linear Regression 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[])
{
if(rates_total < g_ExtPeriod)
return(0);
if(time[rates_total - 1] > g_last_update_time)
{
ArrayInitialize(BufferUpper, EMPTY_VALUE);
ArrayInitialize(BufferLower, EMPTY_VALUE);
ArrayInitialize(BufferMiddle, EMPTY_VALUE);
//--- 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);
//--- Calculate Heikin Ashi bars
g_ha_calculator.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close);
//--- Calculate the channel using HA data
CalculateChannel(rates_total, ha_open, ha_high, ha_low, ha_close);
g_last_update_time = time[rates_total - 1];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Main calculation logic moved to a helper function |
//+------------------------------------------------------------------+
void CalculateChannel(int rates_total, const double &ha_open[], const double &ha_high[], const double &ha_low[], const double &ha_close[])
{
int start_index = rates_total - g_ExtPeriod;
//--- STEP 1: Calculate sums for the regression formula
double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
double y = GetHAPrice(start_index + i, InpAppliedPrice, ha_open, ha_high, ha_low, ha_close);
double x = i;
sum_x += x;
sum_y += y;
sum_xy += x * y;
sum_x2 += x * x;
}
//--- STEP 2: Calculate slope (b) and intercept (a)
double b = (g_ExtPeriod * sum_xy - sum_x * sum_y) / (g_ExtPeriod * sum_x2 - sum_x * sum_x);
double a = (sum_y - b * sum_x) / g_ExtPeriod;
//--- STEP 3: Calculate regression values and deviation
double deviation_offset = 0;
double regression_values[];
ArrayResize(regression_values, g_ExtPeriod);
if(InpChannelMode == DEVIATION_STANDARD)
{
double deviation_sum_sq = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
regression_values[i] = a + b * i;
double price = GetHAPrice(start_index + i, InpAppliedPrice, ha_open, ha_high, ha_low, ha_close);
double diff = price - regression_values[i];
deviation_sum_sq += diff * diff;
}
double std_dev = MathSqrt(deviation_sum_sq / g_ExtPeriod);
deviation_offset = g_ExtDeviations * std_dev;
}
else // DEVIATION_MAXIMUM
{
double max_dev = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
regression_values[i] = a + b * i;
double price = GetHAPrice(start_index + i, InpAppliedPrice, ha_open, ha_high, ha_low, ha_close);
double dev = MathAbs(price - regression_values[i]);
if(dev > max_dev)
max_dev = dev;
}
deviation_offset = max_dev;
}
//--- STEP 4: Fill the indicator buffers for the last N bars
for(int i = 0; i < g_ExtPeriod; i++)
{
int buffer_index = start_index + i;
BufferMiddle[buffer_index] = regression_values[i];
BufferUpper[buffer_index] = regression_values[i] + deviation_offset;
BufferLower[buffer_index] = regression_values[i] - deviation_offset;
}
//--- Dynamically set the draw begin to only show the last channel
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, start_index);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, start_index);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, start_index);
}
//+------------------------------------------------------------------+
//| Helper function to get the correct Heikin Ashi price type |
//+------------------------------------------------------------------+
double GetHAPrice(int index, ENUM_HA_APPLIED_PRICE type, const double &ha_open[], const double &ha_high[], const double &ha_low[], const double &ha_close[])
{
switch(type)
{
case HA_PRICE_OPEN:
return ha_open[index];
case HA_PRICE_HIGH:
return ha_high[index];
case HA_PRICE_LOW:
return ha_low[index];
case HA_PRICE_MEDIAN:
return (ha_high[index] + ha_low[index]) / 2.0;
case HA_PRICE_TYPICAL:
return (ha_high[index] + ha_low[index] + ha_close[index]) / 3.0;
default:
return ha_close[index];
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,215 +0,0 @@
//+------------------------------------------------------------------+
//| LinearRegression_Pro_Sample.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "Linear Regression Channel using SAMPLE standard deviation (n-1)."
#property description "Updates only on new bars for efficiency."
//--- Indicator Window and Plot Properties ---
#property indicator_chart_window
#property indicator_buffers 3 // Upper, Lower, Middle
#property indicator_plots 3
//--- Plot 1: Upper Channel
#property indicator_label1 "Upper"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_DOT
//--- Plot 2: Lower Channel
#property indicator_label2 "Lower"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrDodgerBlue
#property indicator_style2 STYLE_DOT
//--- Plot 3: Regression Line (Middle)
#property indicator_label3 "Regression"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrRed
#property indicator_style3 STYLE_SOLID
//--- Enum for Channel Calculation Mode ---
enum ENUM_CHANNEL_MODE
{
DEVIATION_STANDARD, // Channel width based on Standard Deviation
DEVIATION_MAXIMUM // Channel width based on Maximum Deviation
};
//--- Input Parameters ---
input int InpRegressionPeriod = 100;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
input ENUM_CHANNEL_MODE InpChannelMode = DEVIATION_STANDARD;
input double InpDeviations = 2.0;
//--- Indicator Buffers ---
double BufferUpper[];
double BufferLower[];
double BufferMiddle[];
//--- Global Variables ---
int g_ExtPeriod;
double g_ExtDeviations;
datetime g_last_update_time;
//--- Forward declarations ---
double GetPrice(int index, ENUM_APPLIED_PRICE type, const double &open[], const double &high[], const double &low[], const double &close[]);
void CalculateChannel(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[]);
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriod = (InpRegressionPeriod < 2) ? 2 : InpRegressionPeriod;
g_ExtDeviations = (InpDeviations <= 0) ? 2.0 : InpDeviations;
g_last_update_time = 0;
SetIndexBuffer(0, BufferUpper, INDICATOR_DATA);
SetIndexBuffer(1, BufferLower, INDICATOR_DATA);
SetIndexBuffer(2, BufferMiddle, INDICATOR_DATA);
ArraySetAsSeries(BufferUpper, false);
ArraySetAsSeries(BufferLower, false);
ArraySetAsSeries(BufferMiddle, false);
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("LinReg Pro Sample(%d)", g_ExtPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Linear Regression 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[])
{
if(rates_total < g_ExtPeriod)
return(0);
if(time[rates_total - 1] > g_last_update_time)
{
ArrayInitialize(BufferUpper, EMPTY_VALUE);
ArrayInitialize(BufferLower, EMPTY_VALUE);
ArrayInitialize(BufferMiddle, EMPTY_VALUE);
CalculateChannel(rates_total, open, high, low, close);
g_last_update_time = time[rates_total - 1];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Main calculation logic moved to a helper function |
//+------------------------------------------------------------------+
void CalculateChannel(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[])
{
int start_index = rates_total - g_ExtPeriod;
//--- STEP 1: Calculate sums for the regression formula
double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
double y = GetPrice(start_index + i, InpAppliedPrice, open, high, low, close);
double x = i;
sum_x += x;
sum_y += y;
sum_xy += x * y;
sum_x2 += x * x;
}
//--- STEP 2: Calculate slope (b) and intercept (a)
double b = (g_ExtPeriod * sum_xy - sum_x * sum_y) / (g_ExtPeriod * sum_x2 - sum_x * sum_x);
double a = (sum_y - b * sum_x) / g_ExtPeriod;
//--- STEP 3: Calculate regression values and deviation
double deviation_offset = 0;
double regression_values[];
ArrayResize(regression_values, g_ExtPeriod);
if(InpChannelMode == DEVIATION_STANDARD)
{
double deviation_sum_sq = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
regression_values[i] = a + b * i;
double price = GetPrice(start_index + i, InpAppliedPrice, open, high, low, close);
double diff = price - regression_values[i];
deviation_sum_sq += diff * diff;
}
// Use Sample Standard Deviation (n-1)
if(g_ExtPeriod > 1)
{
double std_dev = MathSqrt(deviation_sum_sq / (g_ExtPeriod - 1));
deviation_offset = g_ExtDeviations * std_dev;
}
}
else // DEVIATION_MAXIMUM
{
double max_dev = 0;
for(int i = 0; i < g_ExtPeriod; i++)
{
regression_values[i] = a + b * i;
double price = GetPrice(start_index + i, InpAppliedPrice, open, high, low, close);
double dev = MathAbs(price - regression_values[i]);
if(dev > max_dev)
max_dev = dev;
}
deviation_offset = max_dev;
}
//--- STEP 4: Fill the indicator buffers for the last N bars
for(int i = 0; i < g_ExtPeriod; i++)
{
int buffer_index = start_index + i;
BufferMiddle[buffer_index] = regression_values[i];
BufferUpper[buffer_index] = regression_values[i] + deviation_offset;
BufferLower[buffer_index] = regression_values[i] - deviation_offset;
}
//--- Dynamically set the draw begin to only show the last channel
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, start_index);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, start_index);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, start_index);
}
//+------------------------------------------------------------------+
//| Helper function to get the correct price type |
//+------------------------------------------------------------------+
double GetPrice(int index, ENUM_APPLIED_PRICE type, const double &open[], const double &high[], const double &low[], const double &close[])
{
switch(type)
{
case PRICE_OPEN:
return open[index];
case PRICE_HIGH:
return high[index];
case PRICE_LOW:
return low[index];
case PRICE_MEDIAN:
return (high[index] + low[index]) / 2.0;
case PRICE_TYPICAL:
return (high[index] + low[index] + close[index]) / 3.0;
case PRICE_WEIGHTED:
return (high[index] + low[index] + 2*close[index]) / 4.0;
default:
return close[index];
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-82
View File
@@ -1,82 +0,0 @@
# Moving Average Convergence/Divergence (MACD)
## 1. Summary (Introduction)
The Moving Average Convergence/Divergence (MACD), developed by Gerald Appel in the late 1970s, is one of the most popular and versatile technical indicators. It is a trend-following momentum indicator that shows the relationship between two exponential moving averages (EMAs) of a securitys price.
The MACD is composed of three main components, which together provide a comprehensive view of trend direction, momentum, and potential reversal points:
- **The MACD Line:** The core of the indicator.
- **The Signal Line:** A moving average of the MACD Line, used to generate trade signals.
- **The Histogram:** Represents the difference between the MACD Line and the Signal Line.
## 2. Mathematical Foundations and Calculation Logic
The MACD is calculated through a series of subtractions and exponential smoothing steps.
### Required Components
- **Fast EMA Period:** The period for the shorter-term EMA (standard is 12).
- **Slow EMA Period:** The period for the longer-term EMA (standard is 26).
- **Signal EMA Period:** The period for the EMA that smooths the MACD Line (standard is 9).
- **Source Price (P):** The price series used for the calculation (e.g., Close).
### Calculation Steps (Algorithm)
1. **Calculate the Fast EMA:** Compute an EMA of the source price using the fast period.
$\text{FastEMA} = \text{EMA}(P, \text{FastPeriod})$
2. **Calculate the Slow EMA:** Compute an EMA of the source price using the slow period.
$\text{SlowEMA} = \text{EMA}(P, \text{SlowPeriod})$
3. **Calculate the MACD Line:** Subtract the Slow EMA from the Fast EMA. This is the main momentum line.
$\text{MACD Line} = \text{FastEMA} - \text{SlowEMA}$
4. **Calculate the Signal Line:** Compute an EMA of the MACD Line using the signal period.
$\text{Signal Line} = \text{EMA}(\text{MACD Line}, \text{SignalPeriod})$
5. **Calculate the Histogram:** Subtract the Signal Line from the MACD Line.
$\text{Histogram} = \text{MACD Line} - \text{Signal Line}$
## 3. MQL5 Implementation Details
Our MQL5 implementation was refactored to be a completely self-contained, robust, and accurate representation of the classic, TradingView-style MACD.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function. This is our standard practice for indicators with multiple recursive calculations to ensure maximum stability.
- **Fully Manual EMA Calculations:** To guarantee 100% accuracy and consistency, all three Exponential Moving Averages (Fast, Slow, and Signal) are calculated **manually**. The indicator is completely independent of external handles or libraries.
- **Robust Initialization:** Each recursive EMA calculation is carefully initialized with a **manual Simple Moving Average (SMA)**. This provides a stable starting point for the recursive calculations and completely eliminates the risk of floating-point overflows.
- **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 source price array is prepared.
2. **Step 2 & 3:** The Fast and Slow EMAs are calculated and stored in calculation buffers.
3. **Step 4:** The MACD Line is calculated from the two EMAs.
4. **Step 5:** The Signal Line (EMA of the MACD Line) and the final Histogram value are calculated.
- **TradingView-Style Visualization:** Our implementation plots all three standard components: the MACD Line (blue), the Signal Line (orange/red), and the true Histogram (the difference between the two lines), providing a more informative visual than the default MetaTrader MACD.
- **Heikin Ashi Variant (`MACD_HeikinAshi.mq5`):**
- Our toolkit also includes a "pure" Heikin Ashi version. The calculation logic is identical, but it uses the smoothed Heikin Ashi price data as its input for the initial Fast and Slow EMAs.
- This results in a "doubly smoothed" MACD, which is excellent for filtering out market noise and identifying the most significant, underlying momentum shifts.
## 4. Parameters
- **Fast EMA Period (`InpFastEMA`):** The period for the shorter-term EMA. Default is `12`.
- **Slow EMA Period (`InpSlowEMA`):** The period for the longer-term EMA. Default is `26`.
- **Signal EMA Period (`InpSignalEMA`):** The period for the signal line's EMA. Default is `9`.
- **Applied Price (`InpAppliedPrice`):** The source price used for the calculation. Default is `PRICE_CLOSE`.
## 5. Usage and Interpretation
- **Signal Line Crossovers:** This is the most common MACD signal.
- **Bullish Crossover:** When the MACD Line (blue) crosses above the Signal Line (red).
- **Bearish Crossover:** When the MACD Line crosses below the Signal Line.
- **Zero Line Crossovers:** These indicate a potential change in the overall trend direction.
- **Bullish Crossover:** When the MACD Line crosses above the zero line.
- **Bearish Crossover:** When the MACD Line crosses below the zero line.
- **Divergence:** This is one of the most powerful MACD signals.
- **Bullish Divergence:** Price makes a lower low, but the MACD makes a higher low, suggesting weakening bearish momentum.
- **Bearish Divergence:** Price makes a higher high, but the MACD makes a lower high, suggesting weakening bullish momentum.
- **Histogram:** The histogram visually represents the distance between the MACD and Signal lines. When the bars grow taller, momentum is increasing. When they shrink, momentum is decreasing, which can be an early warning of a potential crossover.
-197
View File
@@ -1,197 +0,0 @@
//+------------------------------------------------------------------+
//| MACD.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "6.00" // TradingView style: MACD Line, Signal Line, and Histogram
#property description "Moving Average Convergence/Divergence (TradingView Style)"
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 5 // Histogram, Signal, MACD Line, FastEMA, SlowEMA
#property indicator_plots 3 // Histogram, MACD Line, Signal Line
//--- Plot 1: MACD Histogram
#property indicator_label1 "Histogram"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
//--- Plot 2: MACD Line
#property indicator_label2 "MACD"
#property indicator_type2 DRAW_LINE
// --- FIX: Replaced hex code with a standard MQL5 color constant ---
#property indicator_color2 clrDodgerBlue
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Plot 3: Signal Line
#property indicator_label3 "Signal"
#property indicator_type3 DRAW_LINE
// --- FIX: Replaced hex code with a standard MQL5 color constant ---
#property indicator_color3 clrOrangeRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
//--- Input Parameters ---
input int InpFastEMA = 12;
input int InpSlowEMA = 26;
input int InpSignalEMA = 9;
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferMACD_Histogram[]; // Plot 1
double BufferMACDLine[]; // Plot 2
double BufferSignalLine[]; // Plot 3
double BufferFastEMA[]; // Calculation
double BufferSlowEMA[]; // Calculation
//--- Global Variables ---
int g_ExtFastEMA, g_ExtSlowEMA, g_ExtSignalEMA;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtFastEMA = (InpFastEMA < 1) ? 1 : InpFastEMA;
g_ExtSlowEMA = (InpSlowEMA < 1) ? 1 : InpSlowEMA;
g_ExtSignalEMA = (InpSignalEMA < 1) ? 1 : InpSignalEMA;
if(g_ExtFastEMA > g_ExtSlowEMA)
{
int temp = g_ExtFastEMA;
g_ExtFastEMA = g_ExtSlowEMA;
g_ExtSlowEMA = temp;
}
SetIndexBuffer(0, BufferMACD_Histogram, INDICATOR_DATA);
SetIndexBuffer(1, BufferMACDLine, INDICATOR_DATA);
SetIndexBuffer(2, BufferSignalLine, INDICATOR_DATA);
SetIndexBuffer(3, BufferFastEMA, INDICATOR_CALCULATIONS);
SetIndexBuffer(4, BufferSlowEMA, INDICATOR_CALCULATIONS);
ArraySetAsSeries(BufferMACD_Histogram, false);
ArraySetAsSeries(BufferMACDLine, false);
ArraySetAsSeries(BufferSignalLine, false);
ArraySetAsSeries(BufferFastEMA, false);
ArraySetAsSeries(BufferSlowEMA, false);
int macd_line_draw_begin = g_ExtSlowEMA - 1;
int signal_draw_begin = g_ExtSlowEMA + g_ExtSignalEMA - 2;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, signal_draw_begin); // Histogram
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, macd_line_draw_begin); // MACD Line
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, signal_draw_begin); // Signal Line
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("MACD(%d,%d,%d)", g_ExtFastEMA, g_ExtSlowEMA, g_ExtSignalEMA));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Moving Average Convergence/Divergence 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_ExtSlowEMA + g_ExtSignalEMA - 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_OPEN:
price_source[i] = open[i];
break;
case PRICE_HIGH:
price_source[i] = high[i];
break;
case PRICE_LOW:
price_source[i] = low[i];
break;
default:
price_source[i] = close[i];
break;
}
}
//--- STEP 2: Calculate Fast EMA
double pr_fast = 2.0 / (g_ExtFastEMA + 1.0);
for(int i = g_ExtFastEMA - 1; i < rates_total; i++)
{
if(i == g_ExtFastEMA - 1)
{
double sum = 0;
for(int j=0; j<g_ExtFastEMA; j++)
sum += price_source[i-j];
BufferFastEMA[i] = sum / g_ExtFastEMA;
}
else
{
BufferFastEMA[i] = price_source[i] * pr_fast + BufferFastEMA[i-1] * (1.0 - pr_fast);
}
}
//--- STEP 3: Calculate Slow EMA
double pr_slow = 2.0 / (g_ExtSlowEMA + 1.0);
for(int i = g_ExtSlowEMA - 1; i < rates_total; i++)
{
if(i == g_ExtSlowEMA - 1)
{
double sum = 0;
for(int j=0; j<g_ExtSlowEMA; j++)
sum += price_source[i-j];
BufferSlowEMA[i] = sum / g_ExtSlowEMA;
}
else
{
BufferSlowEMA[i] = price_source[i] * pr_slow + BufferSlowEMA[i-1] * (1.0 - pr_slow);
}
}
//--- STEP 4: Calculate MACD Line
for(int i = g_ExtSlowEMA - 1; i < rates_total; i++)
{
BufferMACDLine[i] = BufferFastEMA[i] - BufferSlowEMA[i];
}
//--- STEP 5: Calculate Signal Line (EMA of MACD Line) and Histogram
double pr_signal = 2.0 / (g_ExtSignalEMA + 1.0);
for(int i = start_pos; i < rates_total; i++)
{
if(i == start_pos)
{
double sum = 0;
for(int j=0; j<g_ExtSignalEMA; j++)
sum += BufferMACDLine[i-j];
BufferSignalLine[i] = sum / g_ExtSignalEMA;
}
else
{
BufferSignalLine[i] = BufferMACDLine[i] * pr_signal + BufferSignalLine[i-1] * (1.0 - pr_signal);
}
BufferMACD_Histogram[i] = BufferMACDLine[i] - BufferSignalLine[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-229
View File
@@ -1,229 +0,0 @@
//+------------------------------------------------------------------+
//| MACD_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00" // TradingView style on Heikin Ashi data
#property description "MACD on Heikin Ashi data (TradingView Style)"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 5 // Histogram, MACD Line, Signal Line, FastEMA, SlowEMA
#property indicator_plots 3 // Histogram, MACD Line, Signal Line
//--- Plot 1: MACD Histogram
#property indicator_label1 "HA_Hist"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
//--- Plot 2: MACD Line
#property indicator_label2 "HA_MACD"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrDodgerBlue
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Plot 3: Signal Line
#property indicator_label3 "HA_Signal"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrOrangeRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpFastEMA = 12;
input int InpSlowEMA = 26;
input int InpSignalEMA = 9;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferMACD_Histogram[];
double BufferMACDLine[];
double BufferSignalLine[];
double BufferFastEMA[];
double BufferSlowEMA[];
//--- Global Objects and Variables ---
int g_ExtFastEMA, g_ExtSlowEMA, g_ExtSignalEMA;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtFastEMA = (InpFastEMA < 1) ? 1 : InpFastEMA;
g_ExtSlowEMA = (InpSlowEMA < 1) ? 1 : InpSlowEMA;
g_ExtSignalEMA = (InpSignalEMA < 1) ? 1 : InpSignalEMA;
if(g_ExtFastEMA > g_ExtSlowEMA)
{
int temp = g_ExtFastEMA;
g_ExtFastEMA = g_ExtSlowEMA;
g_ExtSlowEMA = temp;
}
SetIndexBuffer(0, BufferMACD_Histogram, INDICATOR_DATA);
SetIndexBuffer(1, BufferMACDLine, INDICATOR_DATA);
SetIndexBuffer(2, BufferSignalLine, INDICATOR_DATA);
SetIndexBuffer(3, BufferFastEMA, INDICATOR_CALCULATIONS);
SetIndexBuffer(4, BufferSlowEMA, INDICATOR_CALCULATIONS);
ArraySetAsSeries(BufferMACD_Histogram, false);
ArraySetAsSeries(BufferMACDLine, false);
ArraySetAsSeries(BufferSignalLine, false);
ArraySetAsSeries(BufferFastEMA, false);
ArraySetAsSeries(BufferSlowEMA, false);
int macd_line_draw_begin = g_ExtSlowEMA - 1;
int signal_draw_begin = g_ExtSlowEMA + g_ExtSignalEMA - 2;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, signal_draw_begin);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, macd_line_draw_begin);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, signal_draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_MACD(%d,%d,%d)", g_ExtFastEMA, g_ExtSlowEMA, g_ExtSignalEMA));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
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;
}
}
//+------------------------------------------------------------------+
//| MACD 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_ExtSlowEMA + g_ExtSignalEMA - 2;
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: Prepare the Heikin Ashi source price array
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 3: Calculate Fast EMA on HA data
double pr_fast = 2.0 / (g_ExtFastEMA + 1.0);
for(int i = g_ExtFastEMA - 1; i < rates_total; i++)
{
if(i == g_ExtFastEMA - 1)
{
double sum = 0;
for(int j=0; j<g_ExtFastEMA; j++)
sum += ha_price_source[i-j];
BufferFastEMA[i] = sum / g_ExtFastEMA;
}
else
{
BufferFastEMA[i] = ha_price_source[i] * pr_fast + BufferFastEMA[i-1] * (1.0 - pr_fast);
}
}
//--- STEP 4: Calculate Slow EMA on HA data
double pr_slow = 2.0 / (g_ExtSlowEMA + 1.0);
for(int i = g_ExtSlowEMA - 1; i < rates_total; i++)
{
if(i == g_ExtSlowEMA - 1)
{
double sum = 0;
for(int j=0; j<g_ExtSlowEMA; j++)
sum += ha_price_source[i-j];
BufferSlowEMA[i] = sum / g_ExtSlowEMA;
}
else
{
BufferSlowEMA[i] = ha_price_source[i] * pr_slow + BufferSlowEMA[i-1] * (1.0 - pr_slow);
}
}
//--- STEP 5: Calculate MACD Line
for(int i = g_ExtSlowEMA - 1; i < rates_total; i++)
{
BufferMACDLine[i] = BufferFastEMA[i] - BufferSlowEMA[i];
}
//--- STEP 6: Calculate Signal Line (EMA of MACD Line) and Histogram
double pr_signal = 2.0 / (g_ExtSignalEMA + 1.0);
for(int i = start_pos; i < rates_total; i++)
{
if(i == start_pos)
{
double sum = 0;
for(int j=0; j<g_ExtSignalEMA; j++)
sum += BufferMACDLine[i-j];
BufferSignalLine[i] = sum / g_ExtSignalEMA;
}
else
{
BufferSignalLine[i] = BufferMACDLine[i] * pr_signal + BufferSignalLine[i-1] * (1.0 - pr_signal);
}
BufferMACD_Histogram[i] = BufferMACDLine[i] - BufferSignalLine[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,327 +0,0 @@
//+------------------------------------------------------------------+
//| MACD_Pro_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "MACD Pro with selectable MA types on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 5 // Histogram, MACD Line, Signal Line, FastMA, SlowMA
#property indicator_plots 3 // Histogram, MACD Line, Signal Line
//--- Plot 1: MACD Histogram
#property indicator_label1 "HA_Hist"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
//--- Plot 2: MACD Line
#property indicator_label2 "HA_MACD"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrDodgerBlue
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Plot 3: Signal Line
#property indicator_label3 "HA_Signal"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrOrangeRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
//--- Enum for selecting Heikin Ashi price source ---
enum ENUM_HA_APPLIED_PRICE
{
HA_PRICE_CLOSE, HA_PRICE_OPEN, HA_PRICE_HIGH, HA_PRICE_LOW
};
//--- Input Parameters ---
input int InpFastPeriod = 12;
input int InpSlowPeriod = 26;
input int InpSignalPeriod = 9;
input ENUM_HA_APPLIED_PRICE InpAppliedPrice = HA_PRICE_CLOSE;
input ENUM_MA_METHOD InpSourceMAType = MODE_EMA; // MA Type for Fast and Slow lines
input ENUM_MA_METHOD InpSignalMAType = MODE_EMA; // MA Type for Signal line
//--- Indicator Buffers ---
double BufferMACD_Histogram[];
double BufferMACDLine[];
double BufferSignalLine[];
double BufferFastMA[];
double BufferSlowMA[];
//--- Global Objects and Variables ---
int g_ExtFastPeriod, g_ExtSlowPeriod, g_ExtSignalPeriod;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtFastPeriod = (InpFastPeriod < 1) ? 1 : InpFastPeriod;
g_ExtSlowPeriod = (InpSlowPeriod < 1) ? 1 : InpSlowPeriod;
g_ExtSignalPeriod = (InpSignalPeriod < 1) ? 1 : InpSignalPeriod;
if(g_ExtFastPeriod > g_ExtSlowPeriod)
{
int temp = g_ExtFastPeriod;
g_ExtFastPeriod = g_ExtSlowPeriod;
g_ExtSlowPeriod = temp;
}
SetIndexBuffer(0, BufferMACD_Histogram, INDICATOR_DATA);
SetIndexBuffer(1, BufferMACDLine, INDICATOR_DATA);
SetIndexBuffer(2, BufferSignalLine, INDICATOR_DATA);
SetIndexBuffer(3, BufferFastMA, INDICATOR_CALCULATIONS);
SetIndexBuffer(4, BufferSlowMA, INDICATOR_CALCULATIONS);
ArraySetAsSeries(BufferMACD_Histogram, false);
ArraySetAsSeries(BufferMACDLine, false);
ArraySetAsSeries(BufferSignalLine, false);
ArraySetAsSeries(BufferFastMA, false);
ArraySetAsSeries(BufferSlowMA, false);
int macd_line_draw_begin = g_ExtSlowPeriod - 1;
int signal_draw_begin = g_ExtSlowPeriod + g_ExtSignalPeriod - 2;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, signal_draw_begin);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, macd_line_draw_begin);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, signal_draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_MACD_Pro(%d,%d,%d)", g_ExtFastPeriod, g_ExtSlowPeriod, g_ExtSignalPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
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;
}
}
//+------------------------------------------------------------------+
//| MACD Pro 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_ExtSlowPeriod + g_ExtSignalPeriod - 2;
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: Prepare the Heikin Ashi source price array
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 3: Calculate Fast MA on HA data
for(int i = g_ExtFastPeriod - 1; i < rates_total; i++)
{
switch(InpSourceMAType)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtFastPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=ha_price_source[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
else
{
if(InpSourceMAType == MODE_EMA)
{
double pr=2.0/(g_ExtFastPeriod+1.0);
BufferFastMA[i] = ha_price_source[i]*pr + BufferFastMA[i-1]*(1.0-pr);
}
else
BufferFastMA[i] = (BufferFastMA[i-1]*(g_ExtFastPeriod-1)+ha_price_source[i])/g_ExtFastPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
{
int weight=g_ExtFastPeriod-j;
lwma_sum+=ha_price_source[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferFastMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtFastPeriod; j++)
sum+=ha_price_source[i-j];
BufferFastMA[i] = sum/g_ExtFastPeriod;
}
break;
}
}
//--- STEP 4: Calculate Slow MA on HA data
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
switch(InpSourceMAType)
{
case MODE_EMA:
case MODE_SMMA:
if(i == g_ExtSlowPeriod - 1)
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=ha_price_source[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
else
{
if(InpSourceMAType == MODE_EMA)
{
double pr=2.0/(g_ExtSlowPeriod+1.0);
BufferSlowMA[i] = ha_price_source[i]*pr + BufferSlowMA[i-1]*(1.0-pr);
}
else
BufferSlowMA[i] = (BufferSlowMA[i-1]*(g_ExtSlowPeriod-1)+ha_price_source[i])/g_ExtSlowPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
{
int weight=g_ExtSlowPeriod-j;
lwma_sum+=ha_price_source[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSlowMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtSlowPeriod; j++)
sum+=ha_price_source[i-j];
BufferSlowMA[i] = sum/g_ExtSlowPeriod;
}
break;
}
}
//--- STEP 5: Calculate MACD Line
for(int i = g_ExtSlowPeriod - 1; i < rates_total; i++)
{
BufferMACDLine[i] = BufferFastMA[i] - BufferSlowMA[i];
}
//--- STEP 6: Calculate Signal Line and Histogram
for(int i = start_pos; i < rates_total; i++)
{
switch(InpSignalMAType)
{
case MODE_EMA:
case MODE_SMMA:
if(i == start_pos)
{
double sum=0;
for(int j=0; j<g_ExtSignalPeriod; j++)
sum+=BufferMACDLine[i-j];
BufferSignalLine[i] = sum/g_ExtSignalPeriod;
}
else
{
if(InpSignalMAType == MODE_EMA)
{
double pr=2.0/(g_ExtSignalPeriod+1.0);
BufferSignalLine[i] = BufferMACDLine[i]*pr + BufferSignalLine[i-1]*(1.0-pr);
}
else
BufferSignalLine[i] = (BufferSignalLine[i-1]*(g_ExtSignalPeriod-1)+BufferMACDLine[i])/g_ExtSignalPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtSignalPeriod; j++)
{
int weight=g_ExtSignalPeriod-j;
lwma_sum+=BufferMACDLine[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignalLine[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtSignalPeriod; j++)
sum+=BufferMACDLine[i-j];
BufferSignalLine[i] = sum/g_ExtSignalPeriod;
}
break;
}
BufferMACD_Histogram[i] = BufferMACDLine[i] - BufferSignalLine[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+```
//+------------------------------------------------------------------+
-69
View File
@@ -1,69 +0,0 @@
# MESA Adaptive Moving Average (MAMA & FAMA)
## 1. Summary (Introduction)
The MESA Adaptive Moving Average (MAMA) is a highly sophisticated, adaptive moving average developed by John F. Ehlers, a pioneer in applying Digital Signal Processing (DSP) techniques to financial markets. Unlike traditional averages that have a fixed lookback period, MAMA dynamically adjusts its smoothing factor based on the market's measured cyclicality.
The indicator's core function is to measure the dominant cycle period of the price action in real-time using the Hilbert Transform. It then uses this information to create a moving average that is extremely responsive in trending markets (with fast cycles) and very smooth in sideways markets (with slow cycles), effectively filtering out market noise while minimizing lag.
MAMA is almost always plotted with its companion line, **FAMA (Following Adaptive Moving Average)**. FAMA is a slightly delayed version of MAMA, and the crossover between the two lines provides clear, responsive trading signals. Our MQL5 suite includes two distinct, professionally coded implementations of this system.
## 2. Mathematical Foundations and Calculation Logic
The MAMA algorithm is a multi-stage process rooted in digital signal processing. It translates price movements into wave-like components to measure their cyclical properties.
### Required Components
- **Source Price:** The price series used for calculation (e.g., `PRICE_CLOSE`).
- **Fast Limit (`alpha_fast`):** The maximum allowable value for the adaptive smoothing constant, `alpha`. This corresponds to the alpha of a fast EMA (e.g., 0.5 corresponds to a ~4-period EMA).
- **Slow Limit (`alpha_slow`):** The minimum allowable value for `alpha`, corresponding to a slow EMA (e.g., 0.05 corresponds to a ~39-period EMA).
### Calculation Steps (Algorithm)
1. **Price Pre-processing:** The source price is first lightly smoothed (typically with a 4-period WMA) to remove minor noise.
2. **Hilbert Transform:** This is the core of the cycle measurement. The algorithm applies a series of digital filters to the smoothed price to decompose it into its **In-Phase (I)** and **Quadrature (Q)** components. These two components can be thought of as representing the price wave and a version of that same wave shifted by 90 degrees.
3. **Dominant Cycle Period Measurement:** By analyzing the relationship between the I and Q components (specifically, their arctangent), the algorithm calculates the **Dominant Cycle Period** for each bar. This is a real-time measurement of the market's "rhythm" or "heartbeat". The result is then smoothed and limited to a practical range (e.g., between 6 and 50 bars).
4. **Adaptive Alpha Calculation:** The algorithm calculates the rate of change of the **phase angle** between the I and Q components. This "delta phase" is a measure of the market's instantaneous velocity. The final adaptive smoothing constant, `alpha`, is calculated based on this delta phase, constrained by the `Fast Limit` and `Slow Limit` parameters.
- A rapid phase change (trending market) results in a larger `alpha` (faster average).
- A slow phase change (ranging market) results in a smaller `alpha` (smoother average).
5. **Final MAMA and FAMA Calculation:**
- The MAMA line is calculated using an EMA-like formula, but with the dynamic, adaptive `alpha` for each bar.
$\text{MAMA}_i = \alpha_i \times \text{Price}_i + (1 - \alpha_i) \times \text{MAMA}_{i-1}$
- The FAMA line is then calculated as a smoothed version of the MAMA line, using half of the adaptive `alpha`.
$\text{FAMA}_i = (\alpha_i/2) \times \text{MAMA}_i + (1 - \alpha_i/2) \times \text{FAMA}_{i-1}$
## 3. MQL5 Implementation Details
Our MQL5 suite provides two distinct, high-quality implementations of the MAMA/FAMA system, both built upon a shared, robust, object-oriented framework.
- **Modular, Reusable Calculation Engine (`MESA_Calculator.mqh`):** The entire complex MAMA/FAMA algorithm for both standard and Heikin Ashi data is encapsulated within a single, powerful include file. This file contains two main classes:
- **`CMESACalculator`**: This class implements the responsive, phase-change-based logic found in popular platforms like TradingView (popularized by LazyBear), which we have validated as being highly effective for generating timely crossover signals.
- **`CMESACalculator_HA`**: A child class that inherits from the base class. It overrides the data preparation step to first transform the input data into Heikin Ashi values before passing it to the main MAMA algorithm. This object-oriented approach eliminates code duplication and ensures both versions are always in sync.
- **Stability via Full Recalculation:** MAMA is a highly recursive and state-dependent indicator. To ensure perfect accuracy and prevent any risk of calculation errors, all our MESA indicators employ a "brute-force" **full recalculation** on every tick. This is our core principle of prioritizing stability over premature optimization.
- **Clear, Staged Calculation:** Inside the calculator classes, the algorithm is implemented in a clear, sequential manner. Each major component (`smooth_price`, `detrender`, `period`, `alpha`, etc.) is stored in its own internal array, which makes the code highly readable and significantly easier to debug and validate against the original pseudo-code.
- **The MESA Indicator Family:** Our modular engine allows for a complete family of indicators:
- **`MAMA_FAMA.mq5` / `MAMA_FAMA_HeikinAshi.mq5`**: The main, combined indicators that display both the MAMA and FAMA lines, intended for crossover-based strategies.
- **`MAMA.mq5` / `FAMA.mq5`**: Separate indicators for displaying only one of the lines, for traders who wish to use them individually.
## 4. Parameters
- **Source Price (`InpSourcePrice`):** The price data used for the calculation (Close, Open, High, Low, Median, etc.). Default is `PRICE_CLOSE`.
- **Fast Limit (`InpFastLimit`):** Sets the upper bound for the adaptive smoothing constant `alpha`. Corresponds to the alpha of the fastest desired EMA. Default is `0.5`.
- **Slow Limit (`InpSlowLimit`):** Sets the lower bound for `alpha`. Corresponds to the alpha of the slowest desired EMA. Default is `0.05`.
## 5. Usage and Interpretation
- **Crossover Signals:** The primary use of the MAMA/FAMA system is for generating trading signals based on the crossover of the two lines.
- A **Buy Signal** is generated when the MAMA (fast line, red) crosses **above** the FAMA (slow line, green).
- A **Sell Signal** is generated when the MAMA crosses **below** the FAMA.
- **Trend Identification:** The position of the lines relative to each other indicates the trend. When MAMA is above FAMA, the trend is considered bullish. When MAMA is below FAMA, the trend is considered bearish.
- **Adaptiveness in Action:** Observe how the lines spread apart and move quickly during strong trends, providing clear direction. In sideways, choppy markets, notice how the lines converge and flatten out, indicating a lack of trend and helping to avoid false signals.
- **Heikin Ashi Variant:** The `_HeikinAshi` version uses smoothed Heikin Ashi price data as its input. This results in even smoother MAMA/FAMA lines and can help to filter out additional market noise, potentially leading to fewer, but higher-quality, crossover signals.
- **Caution:** Like all moving average systems, MAMA/FAMA is a trend-following tool. It will produce its best results in trending markets and can generate false signals during strong, range-bound periods. It is always recommended to use it in conjunction with other forms of analysis.
-96
View File
@@ -1,96 +0,0 @@
//+------------------------------------------------------------------+
//| MAMA_FAMA.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "3.01"
#property description "MESA Adaptive Moving Average (MAMA) and FAMA by John Ehlers."
#property description "Based on the official MotiveWave pseudo-code."
#property indicator_chart_window
#property indicator_buffers 2 // MAMA and FAMA
#property indicator_plots 2
#include <MyIncludes\MESA_Calculator.mqh>
//--- Plot 1: MAMA Line
#property indicator_label1 "MAMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Plot 2: FAMA Line
#property indicator_label2 "FAMA"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrGreen
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Input Parameters ---
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE; // Source Price
input double InpFastLimit = 0.5; // Fast Limit
input double InpSlowLimit = 0.05; // Slow Limit
//--- Indicator Buffers ---
double BufferMAMA[];
double BufferFAMA[];
//--- Global calculator object ---
CMESACalculator *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferMAMA, INDICATOR_DATA);
SetIndexBuffer(1, BufferFAMA, INDICATOR_DATA);
ArraySetAsSeries(BufferMAMA, false);
ArraySetAsSeries(BufferFAMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 10);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 10);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("MAMA/FAMA(%.2f, %.2f)", InpFastLimit, InpSlowLimit));
g_calculator = new CMESACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpFastLimit, InpSlowLimit))
{
Print("Failed to initialize MESA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration 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(CheckPointer(g_calculator) != POINTER_INVALID)
{
//--- Corrected: Pass all required parameters to the Calculate method
g_calculator.Calculate(rates_total, InpSourcePrice, open, high, low, close, BufferMAMA, BufferFAMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,95 +0,0 @@
//+------------------------------------------------------------------+
//| MAMA_FAMA_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.01"
#property description "MESA Adaptive Moving Average (MAMA) and FAMA on Heikin Ashi data."
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#include <MyIncludes\MESA_Calculator.mqh>
//--- Plot 1: MAMA Line
#property indicator_label1 "MAMA (HA)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Plot 2: FAMA Line
#property indicator_label2 "FAMA (HA)"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrGreen
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Input Parameters ---
input double InpFastLimit = 0.5;
input double InpSlowLimit = 0.05;
//--- Indicator Buffers ---
double BufferMAMA[];
double BufferFAMA[];
//--- Global calculator object ---
CMESACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferMAMA, INDICATOR_DATA);
SetIndexBuffer(1, BufferFAMA, INDICATOR_DATA);
ArraySetAsSeries(BufferMAMA, false);
ArraySetAsSeries(BufferFAMA, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 10);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 10);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("MAMA/FAMA HA(%.2f, %.2f)", InpFastLimit, InpSlowLimit));
g_calculator = new CMESACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpFastLimit, InpSlowLimit))
{
Print("Failed to initialize MESA HA Calculator.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration 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(CheckPointer(g_calculator) != POINTER_INVALID)
{
//--- The HA calculator needs the original OHLC for conversion.
//--- The price_type parameter is ignored by the HA calculator.
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, BufferMAMA, BufferFAMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-71
View File
@@ -1,71 +0,0 @@
# Money Flow Index (MFI)
## 1. Summary (Introduction)
The Money Flow Index (MFI) is a momentum oscillator that measures the strength of money flowing into and out of a security. Developed as a "volume-weighted RSI," it combines both price and volume data to identify overbought or oversold conditions.
Unlike the standard RSI which only considers price, the MFI incorporates volume to provide a clearer picture of the conviction behind price moves. A strong price trend accompanied by high volume is considered more significant than one with low volume. This makes the MFI a powerful tool for gauging trend strength and spotting potential reversals, particularly through divergence signals.
## 2. Mathematical Foundations and Calculation Logic
The MFI calculation is similar to the RSI, but instead of using simple price changes, it uses "Money Flow," which is derived from the Typical Price and Volume.
### Required Components
- **Period (N):** The lookback period for the calculation (e.g., 14).
- **Price and Volume Data:** The `High`, `Low`, `Close`, and `Volume` of each bar.
### Calculation Steps (Algorithm)
1. **Calculate the Typical Price (TP):** For each bar, calculate the average of the high, low, and close.
$\text{TP}_i = \frac{\text{High}_i + \text{Low}_i + \text{Close}_i}{3}$
2. **Calculate the Raw Money Flow (RMF):** Multiply the Typical Price by the volume for that period.
$\text{Raw Money Flow}_i = \text{TP}_i \times \text{Volume}_i$
3. **Determine Positive and Negative Money Flow:** Compare the current bar's Typical Price to the previous bar's.
- If $\text{TP}_i > \text{TP}_{i-1}$, it is considered **Positive Money Flow** ($\text{PMF}_i = \text{RMF}_i$).
- If $\text{TP}_i < \text{TP}_{i-1}$, it is considered **Negative Money Flow** ($\text{NMF}_i = \text{RMF}_i$).
- If they are equal, the money flow is zero for that period.
4. **Calculate the Money Flow Ratio:** Sum the Positive and Negative Money Flows over the period `N` and calculate their ratio.
$\text{Money Flow Ratio} = \frac{\sum_{k=i-N+1}^{i} \text{PMF}_k}{\sum_{k=i-N+1}^{i} \text{NMF}_k}$
5. **Calculate the Money Flow Index (MFI):** Use the ratio to scale the value between 0 and 100.
$\text{MFI}_i = 100 - \frac{100}{1 + \text{Money Flow Ratio}}$
## 3. MQL5 Implementation Details
Our MQL5 implementation is a self-contained, robust, and mathematically correct representation of the classic MFI.
- **Stability via Full Recalculation:** We employ a "brute-force" full recalculation within the `OnCalculate` function to ensure maximum stability and prevent calculation errors.
- **Correct Algorithm:** Unlike the flawed example code provided with MetaTrader, our implementation strictly follows the correct, textbook definition of the MFI, ensuring its results are consistent with other professional charting platforms like TradingView.
- **Efficient Calculation:** The summation of Positive and Negative Money Flow over the lookback period is handled by an efficient **sliding window sum** technique. This avoids nested loops and provides excellent performance.
- **Optional Signal Line:** Our version is enhanced with an optional, user-configurable moving average signal line. The signal line calculation uses our standard, robust, and fully manual `switch` block, which correctly handles all MA types (SMA, EMA, SMMA, LWMA) and their initialization.
- **Heikin Ashi Variant (`MFI_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` to calculate the Typical Price.
- This results in a smoother MFI that filters out price noise. It is particularly effective at producing **clearer and more pronounced divergence signals**, as the Heikin Ashi smoothing helps to identify the true underlying momentum faster than standard price data.
## 4. Parameters
- **MFI Period (`InpMFIPeriod`):** The lookback period for summing the money flows. The standard is `14`.
- **Volume Type (`InpVolumeType`):** Allows the user to select between Tick Volume (`VOLUME_TICK`) and Real Volume (`VOLUME_REAL`).
- **Signal Line Settings:**
- `InpMAPeriod`: The lookback period for the optional signal line.
- `InpMAMethod`: The type of moving average for the signal line.
## 5. Usage and Interpretation
- **Overbought/Oversold Levels:** The primary use of the MFI is to identify extreme conditions.
- **Overbought:** Readings above **80** are considered overbought.
- **Oversold:** Readings below **20** are considered oversold.
- **Divergence:** This is the MFI's most powerful signal.
- **Bullish Divergence:** Price makes a lower low, but the MFI makes a higher low. This indicates that despite the lower price, selling pressure (volume) is weakening, which can foreshadow a bullish reversal.
- **Bearish Divergence:** Price makes a higher high, but the MFI makes a lower high. This indicates that the new high is not supported by strong money flow, and buying pressure is weakening, which can foreshadow a bearish reversal.
- **Signal Line Crossovers:** If the optional signal line is used, crossovers can provide entry and exit signals, similar to other oscillators like RSI or CCI.
- **Caution:** In a very strong trend, the MFI can remain in overbought or oversold territory for extended periods. Divergence signals are generally considered more reliable than simple overbought/oversold readings.
-198
View File
@@ -1,198 +0,0 @@
//+------------------------------------------------------------------+
//| MFI.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "2.00" // Added signal line and refactored for stability
#property description "Money Flow Index with a signal line."
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // MFI and Signal Line
#property indicator_plots 2
#property indicator_maximum 100.0
#property indicator_minimum 0.0
#property indicator_level1 20.0
#property indicator_level2 80.0
#property indicator_level3 50.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: MFI line
#property indicator_label1 "MFI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Input Parameters ---
input int InpMFIPeriod = 14;
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK;
input group "Signal Line Settings"
input int InpMAPeriod = 9;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferMFI[];
double BufferSignal[];
//--- Global Variables ---
int g_ExtMFIPeriod, g_ExtMAPeriod;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtMFIPeriod = (InpMFIPeriod < 1) ? 1 : InpMFIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferMFI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferMFI, false);
ArraySetAsSeries(BufferSignal, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtMFIPeriod);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtMFIPeriod + g_ExtMAPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("MFI(%d, %d)", g_ExtMFIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Money Flow 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_ExtMFIPeriod + g_ExtMAPeriod;
if(rates_total <= start_pos)
return(0);
//--- STEP 1: Calculate Typical Price
double typical_price[];
ArrayResize(typical_price, rates_total);
for(int i=0; i<rates_total; i++)
{
typical_price[i] = (high[i] + low[i] + close[i]) / 3.0;
}
//--- STEP 2: Calculate Positive and Negative Money Flow
double positive_mf[], negative_mf[];
ArrayResize(positive_mf, rates_total);
ArrayResize(negative_mf, rates_total);
for(int i=1; i<rates_total; i++)
{
double raw_money_flow = typical_price[i] * ((InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i]);
if(typical_price[i] > typical_price[i-1])
{
positive_mf[i] = raw_money_flow;
}
else
if(typical_price[i] < typical_price[i-1])
{
negative_mf[i] = raw_money_flow;
}
}
//--- STEP 3: Calculate Money Flow Ratio and MFI using a sliding window sum
double sum_pos = 0;
double sum_neg = 0;
for(int i = 1; i < rates_total; i++)
{
sum_pos += positive_mf[i];
sum_neg += negative_mf[i];
if(i > g_ExtMFIPeriod)
{
sum_pos -= positive_mf[i - g_ExtMFIPeriod];
sum_neg -= negative_mf[i - g_ExtMFIPeriod];
}
if(i >= g_ExtMFIPeriod)
{
if(sum_neg > 0)
{
double money_ratio = sum_pos / sum_neg;
BufferMFI[i] = 100.0 - (100.0 / (1.0 + money_ratio));
}
else
{
BufferMFI[i] = 100.0;
}
}
}
//--- STEP 4: Calculate the Signal Line (MA of MFI)
int ma_start_pos = g_ExtMFIPeriod + g_ExtMAPeriod - 1;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferMFI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferMFI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferMFI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferMFI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferMFI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-230
View File
@@ -1,230 +0,0 @@
//+------------------------------------------------------------------+
//| MFI_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "2.01" // Corrected volume source handling
#property description "Money Flow Index on Heikin Ashi data, with a signal line."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 2 // MFI and Signal Line
#property indicator_plots 2
#property indicator_maximum 100.0
#property indicator_minimum 0.0
#property indicator_level1 20.0
#property indicator_level2 80.0
#property indicator_level3 50.0
#property indicator_levelstyle STYLE_DOT
//--- Plot 1: MFI line
#property indicator_label1 "HA_MFI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Signal line
#property indicator_label2 "HA_Signal"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrangeRed
#property indicator_style2 STYLE_DOT
#property indicator_width2 1
//--- Input Parameters ---
input int InpMFIPeriod = 14;
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK;
input group "Signal Line Settings"
input int InpMAPeriod = 9;
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;
//--- Indicator Buffers ---
double BufferMFI[];
double BufferSignal[];
//--- Global Objects and Variables ---
int g_ExtMFIPeriod, g_ExtMAPeriod;
CHeikinAshi_Calculator *g_ha_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtMFIPeriod = (InpMFIPeriod < 1) ? 1 : InpMFIPeriod;
g_ExtMAPeriod = (InpMAPeriod < 1) ? 1 : InpMAPeriod;
SetIndexBuffer(0, BufferMFI, INDICATOR_DATA);
SetIndexBuffer(1, BufferSignal, INDICATOR_DATA);
ArraySetAsSeries(BufferMFI, false);
ArraySetAsSeries(BufferSignal, false);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_ExtMFIPeriod);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, g_ExtMFIPeriod + g_ExtMAPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_MFI(%d, %d)", g_ExtMFIPeriod, g_ExtMAPeriod));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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;
}
}
//+------------------------------------------------------------------+
//| MFI 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_ExtMFIPeriod + g_ExtMAPeriod;
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 HA Typical Price and Raw Money Flow
double ha_typical_price[], raw_money_flow[];
ArrayResize(ha_typical_price, rates_total);
ArrayResize(raw_money_flow, rates_total);
for(int i=0; i<rates_total; i++)
{
ha_typical_price[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0;
// --- FIX: Use ternary operator to select volume source ---
raw_money_flow[i] = ha_typical_price[i] * ((InpVolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i]);
}
//--- STEP 3: Calculate Positive and Negative Money Flow
double positive_mf[], negative_mf[];
ArrayResize(positive_mf, rates_total);
ArrayResize(negative_mf, rates_total);
for(int i=1; i<rates_total; i++)
{
if(ha_typical_price[i] > ha_typical_price[i-1])
{
positive_mf[i] = raw_money_flow[i];
}
else
if(ha_typical_price[i] < ha_typical_price[i-1])
{
negative_mf[i] = raw_money_flow[i];
}
}
//--- STEP 4: Calculate Money Flow Ratio and MFI using a sliding window sum
double sum_pos = 0;
double sum_neg = 0;
for(int i = 1; i < rates_total; i++)
{
sum_pos += positive_mf[i];
sum_neg += negative_mf[i];
if(i > g_ExtMFIPeriod)
{
sum_pos -= positive_mf[i - g_ExtMFIPeriod];
sum_neg -= negative_mf[i - g_ExtMFIPeriod];
}
if(i >= g_ExtMFIPeriod)
{
if(sum_neg > 0)
{
double money_ratio = sum_pos / sum_neg;
BufferMFI[i] = 100.0 - (100.0 / (1.0 + money_ratio));
}
else
{
BufferMFI[i] = 100.0;
}
}
}
//--- STEP 5: Calculate the Signal Line (MA of MFI)
int ma_start_pos = g_ExtMFIPeriod + g_ExtMAPeriod - 1;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMAMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferMFI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
else
{
if(InpMAMethod == MODE_EMA)
{
double pr=2.0/(g_ExtMAPeriod+1.0);
BufferSignal[i] = BufferMFI[i]*pr + BufferSignal[i-1]*(1.0-pr);
}
else
BufferSignal[i] = (BufferSignal[i-1]*(g_ExtMAPeriod-1)+BufferMFI[i])/g_ExtMAPeriod;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
{
int weight=g_ExtMAPeriod-j;
lwma_sum+=BufferMFI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferSignal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtMAPeriod; j++)
sum+=BufferMFI[i-j];
BufferSignal[i] = sum/g_ExtMAPeriod;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,62 +0,0 @@
# 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.
-142
View File
@@ -1,142 +0,0 @@
//+------------------------------------------------------------------+
//| 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<rates_total; i++)
{
switch(InpAppliedPrice)
{
case PRICE_MEDIAN:
price_source[i] = (high[i] + low[i]) / 2.0;
break;
case PRICE_TYPICAL:
price_source[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED:
price_source[i] = (high[i] + low[i] + 2*close[i]) / 4.0;
break;
}
}
break;
default: // PRICE_CLOSE
ArrayCopy(price_source, close, 0, 0, rates_total);
break;
}
//--- STEP 2: Main calculation loop for McGinley Dynamic
for(int i = 0; i < rates_total; i++)
{
// --- Initialization Step ---
if(i == 0)
{
BufferMcGinley[i] = price_source[i];
continue;
}
// --- Recursive Calculation Step ---
double prev_mg = BufferMcGinley[i-1];
if(prev_mg == 0)
{
BufferMcGinley[i] = price_source[i];
continue;
}
double denominator = g_ExtLength * MathPow(price_source[i] / prev_mg, 4);
if(denominator == 0)
{
BufferMcGinley[i] = prev_mg;
continue;
}
BufferMcGinley[i] = prev_mg + (price_source[i] - prev_mg) / denominator;
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,170 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-62
View File
@@ -1,62 +0,0 @@
# Pascal Weighted Moving Average (Pascal WMA)
## 1. Summary (Introduction)
The Pascal Weighted Moving Average (Pascal WMA) is a unique type of weighted moving average that derives its weights from the coefficients of Pascal's triangle. This mathematical structure, known from combinatorics, produces a set of weights that are perfectly symmetrical and follow a smooth, bell-shaped (Gaussian-like) curve.
Similar to the Sine WMA, the Pascal WMA is a **symmetrical, zero-lag smoothing filter**. Its primary purpose is not to follow trends with minimal lag, but to provide an exceptionally smooth and stable representation of the market's central tendency or "fair value". By assigning the heaviest weights to the price data in the middle of the lookback period, it effectively filters out market noise and reduces the impact of short-term, insignificant price spikes.
The result is a clean, aesthetically pleasing line that glides through the price action, making it a powerful tool for identifying the underlying smoothed trend and for mean-reversion analysis.
## 2. Mathematical Foundations and Calculation Logic
The Pascal WMA calculates a weighted average where the weights are the binomial coefficients found in a row of Pascal's triangle.
### Required Components
- **Period (N):** The lookback period for the moving average. This determines which row of Pascal's triangle is used.
- **Source Price:** The price series used for calculation (e.g., `PRICE_CLOSE`).
### Calculation Steps (Algorithm)
1. **Generate Pascal Weights:** For a given period `N`, the weights are the coefficients of the binomial expansion of $(x+y)^{N-1}$. These coefficients correspond to the `N`-th row of Pascal's triangle (starting the count from row 0). The `k`-th weight in the sequence (where `k` is from 0 to N-1) is calculated using the combination formula:
- $Weight_k = C(N-1, k) = \frac{(N-1)!}{k! \cdot (N-1-k)!}$
2. **Calculate the Weighted Sum:** For each bar `t`, multiply the last `N` prices by the corresponding Pascal coefficients.
- $\text{Weighted Sum}_t = \sum_{i=0}^{N-1} (\text{Price}_{t-i} \cdot Weight_i)$
3. **Calculate the Sum of Weights:** Sum all the generated Pascal weights. A known property of Pascal's triangle is that the sum of the `n`-th row is $2^n$. Therefore, the sum of weights is $2^{N-1}$.
- $\text{Sum of Weights} = \sum_{i=0}^{N-1} Weight_i = 2^{N-1}$
4. **Calculate the Final WMA Value:** Divide the weighted sum of prices by the sum of the weights.
- $\text{Pascal WMA}_t = \frac{\text{Weighted Sum}_t}{\text{Sum of Weights}}$
This process results in a symmetrically weighted average that is centered on the data, providing a very smooth output with a Gaussian-like response.
## 3. MQL5 Implementation Details
Our MQL5 implementation is a clean and robust indicator that accurately calculates the Pascal WMA using an efficient algorithm.
- **Self-Contained, Object-Oriented Design:** The entire logic is encapsulated within a single `.mq5` file but is internally structured into a dedicated `CPascalWMACalculator` class. This separates the calculation logic from the indicator's buffer management, ensuring the code is clean and maintainable.
- **Efficient Weight Generation:** The Pascal's triangle coefficients are calculated only once during the indicator's initialization in the `Init()` method of the calculator class. The algorithm for calculating combinations (`n C k`) is optimized to handle large numbers by using the multiplicative formula and symmetry (`C(n, k) = C(n, n-k)`), preventing unnecessary computations and potential overflows. The weights and their sum are stored in internal class members for efficient reuse.
- **Stability via Full Recalculation:** In line with our core principles, the indicator employs a "brute-force" full recalculation within the `OnCalculate` function. This is the most reliable method to ensure stability and prevent any potential glitches, while keeping the code simple and robust.
- **Correct Symmetrical Application:** The `Calculate` method applies the pre-calculated symmetrical weights directly to the price data. The weights are **not reversed**. The most recent price is multiplied by the first (and smallest) coefficient, and the price in the middle of the period is multiplied by the largest coefficient. This correctly implements the smoothing, centered nature of the filter. The inherent lag of `(Period-1)/2` bars is a mathematical property of the filter.
## 4. Parameters
- **Period (`InpPeriod`):** The lookback period for the moving average. A longer period results in a smoother, more heavily filtered line that is less sensitive to short-term price fluctuations. Default is `21`.
- **Source Price (`InpSourcePrice`):** The price data used for the calculation (Close, Open, High, Low, Median, etc.). Default is `PRICE_CLOSE`.
## 5. Usage and Interpretation
The Pascal WMA should be interpreted as a **high-quality smoothing filter and a "mean" or "center of gravity" line**, not as a traditional trend-following moving average.
- **Noise Reduction and Trend Clarity:** The primary use of the Pascal WMA is to filter out market noise and provide a much clearer picture of the underlying price movement. Its extremely smooth, bell-shaped response makes it highly effective at ignoring insignificant price spikes.
- **Mean Reversion Signals:** The line acts as a "magnet" for the price.
- When the price moves significantly **above** the Pascal WMA, it can be considered over-extended, increasing the probability of a reversion (downward correction) back towards the line.
- When the price moves significantly **below** the Pascal WMA, it can be considered oversold, increasing the probability of a reversion back up towards the line.
- **Confirmation of Trend Direction:** The slope of the Pascal WMA provides a very stable, albeit lagging, confirmation of the main trend direction. Because it is very slow to turn, a change in the slope's direction is a significant event, suggesting a potential major shift in the market.
- **Caution:** Due to its inherent nature as a centered, smoothing filter, the Pascal WMA will always lag the price. It should **not** be used for fast crossover signals in the same way as an EMA. Its strength lies in its exceptional smoothness and its ability to define the market's equilibrium point, making it an excellent tool for mean-reversion strategies or as a baseline in more complex systems.
-199
View File
@@ -1,199 +0,0 @@
//+------------------------------------------------------------------+
//| Pascal_WMA.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Pascal's Triangle Weighted Moving Average. A zero-lag smoothing filter."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
//--- Plot 1: Pascal WMA Line
#property indicator_label1 "Pascal WMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumPurple
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 21;
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferWMA[];
double BufferPrice[];
//+------------------------------------------------------------------+
//| CLASS: CPascalWMACalculator |
//| Encapsulates the logic for Pascal's Triangle weighting. |
//+------------------------------------------------------------------+
class CPascalWMACalculator
{
private:
int m_period;
double m_weights[];
double m_weight_sum;
public:
CPascalWMACalculator(void);
~CPascalWMACalculator(void) {};
bool Init(int period);
void Calculate(int rates_total, const double &price_src[], double &wma_out[]);
};
//+------------------------------------------------------------------+
//| CPascalWMACalculator: Constructor |
//+------------------------------------------------------------------+
CPascalWMACalculator::CPascalWMACalculator(void) : m_period(0), m_weight_sum(0)
{
}
//+------------------------------------------------------------------+
//| CPascalWMACalculator: Initialization and Weight Generation |
//+------------------------------------------------------------------+
bool CPascalWMACalculator::Init(int period)
{
m_period = (period < 2) ? 2 : period;
ArrayResize(m_weights, m_period);
m_weight_sum = 0;
//--- Generate weights from Pascal's triangle row (n C k)
//--- n = period - 1
for(int i = 0; i < m_period; i++)
{
long n = m_period - 1;
long k = i;
// Optimization for combinations: C(n, k) = C(n, n-k)
if(k > n / 2)
k = n - k;
long res = 1;
for(long j = 1; j <= k; j++)
{
// Defensive check to prevent division by zero, though j starts at 1
if(j == 0)
continue;
res = res * (n - j + 1) / j;
}
m_weights[i] = (double)res;
m_weight_sum += m_weights[i];
}
return (m_weight_sum != 0);
}
//+------------------------------------------------------------------+
//| CPascalWMACalculator: Main Calculation Method |
//+------------------------------------------------------------------+
void CPascalWMACalculator::Calculate(int rates_total, const double &price_src[], double &wma_out[])
{
if(rates_total < m_period)
return;
for(int i = m_period - 1; i < rates_total; i++)
{
double weighted_sum = 0;
for(int j = 0; j < m_period; j++)
{
// Symmetrical weighting, use weights as generated
weighted_sum += price_src[i - j] * m_weights[j];
}
wma_out[i] = weighted_sum / m_weight_sum;
}
}
//--- Global calculator object ---
CPascalWMACalculator *g_calculator;
//--- Forward declaration
int PriceSeries(ENUM_APPLIED_PRICE,int,const double&[],const double&[],const double&[],const double&[],double&[]);
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferWMA, INDICATOR_DATA);
ArraySetAsSeries(BufferWMA, false);
g_calculator = new CPascalWMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod))
{
Print("Failed to initialize Pascal WMA Calculator.");
return(INIT_FAILED);
}
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("PascalWMA(%d)", InpPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
ArrayResize(BufferPrice, rates_total);
if(PriceSeries(InpSourcePrice, rates_total, open, high, low, close, BufferPrice) <= 0)
return 0;
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
g_calculator.Calculate(rates_total, BufferPrice, BufferWMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper function to get the selected price series. |
//+------------------------------------------------------------------+
int PriceSeries(ENUM_APPLIED_PRICE type, int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], double &dest_buffer[])
{
switch(type)
{
case PRICE_CLOSE:
ArrayCopy(dest_buffer, close, 0, 0, rates_total);
break;
case PRICE_OPEN:
ArrayCopy(dest_buffer, open, 0, 0, rates_total);
break;
case PRICE_HIGH:
ArrayCopy(dest_buffer, high, 0, 0, rates_total);
break;
case PRICE_LOW:
ArrayCopy(dest_buffer, low, 0, 0, rates_total);
break;
case PRICE_MEDIAN:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i])/2.0;
break;
case PRICE_TYPICAL:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i]+close[i])/3.0;
break;
case PRICE_WEIGHTED:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i]+close[i]+close[i])/4.0;
break;
default:
return 0;
}
return rates_total;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-63
View File
@@ -1,63 +0,0 @@
# Moving Average of RSI (RSI with Signal Line)
## 1. Summary (Introduction)
This indicator plots the standard Relative Strength Index (RSI) and overlays a moving average of the RSI, which acts as a **signal line**. While the RSI is a powerful momentum oscillator, it can often be volatile. Applying a moving average filters out short-term noise, providing a smoother line that can make the underlying momentum trend easier to identify.
The **RSI Oscillator** is a supplementary indicator that displays the difference between the main RSI line and its signal line as a histogram. It provides a clearer visual representation of accelerating and decelerating momentum, similar to the MACD histogram.
## 2. Mathematical Foundations and Calculation Logic
The indicator is a two-stage process. 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.
### Calculation Steps (Algorithm)
1. **Calculate the RSI:** First, calculate the standard RSI for a given period (e.g., 14) on the source price. 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 (Signal Line):** Apply the selected moving average type with its specified period to the RSI data series calculated in the first step.
$\text{Signal Line}_i = \text{MA}(\text{RSI}, \text{MA Period})_i$
3. **Calculate the RSI Oscillator:** The oscillator is the difference between the RSI line and its Signal Line.
$\text{Oscillator}_i = \text{RSI}_i - \text{Signal Line}_i$
## 3. MQL5 Implementation Details
Our MQL5 implementations are designed for stability, clarity, and consistency.
- **Stability via Full Recalculation:** All versions employ a "brute-force" full recalculation within the `OnCalculate` function for maximum stability.
- **Self-Contained Logic:** All versions are completely self-contained. The standard version uses a handle to MQL5's built-in `iRSI` for efficiency, while the Heikin Ashi version uses our custom `CHeikinAshi_RSI_Calculator`.
- **Fully Manual MA Calculations:** To guarantee 100% accuracy and consistency, all moving average calculations for the signal line (**SMA, EMA, SMMA, LWMA**) are performed **manually**. This makes the indicators independent of the `<MovingAverages.mqh>` library and ensures robust behavior on `non-timeseries` arrays.
- **Indicator Family:**
- **Line Versions:** `RSIMA.mq5` and `RSI_HeikinAshi.mq5` plot the RSI line and its signal line.
- **Oscillator Versions:** `RSI_Oscillator.mq5` and `RSI_Oscillator_HeikinAshi.mq5` plot the difference between the two lines as a histogram.
- **Heikin Ashi Variant (`RSI_HeikinAshi.mq5`):**
- Our toolkit also includes a Heikin Ashi version. 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.
## 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 (standard version only).
- **Signal Line Settings:**
- `InpPeriodMA`: The lookback period for the moving average that smooths the RSI line.
- `InpMethodMA`: The type of moving average to use for smoothing (SMA, EMA, SMMA, LWMA).
## 5. Usage and Interpretation
- **Trend and Momentum Confirmation:** The primary use is to provide a clearer view of momentum. When the signal line is rising, it confirms bullish momentum; when it's falling, it confirms bearish momentum.
- **Signal Generation via Crossovers:**
- **RSI / Signal Line Crossover:** When the raw 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 signal line above the 50 level indicates that bulls are in control. A crossover below 50 indicates bears are in control.
- **Oscillator (Histogram):** The histogram provides a clear visual of the relationship between the RSI and its signal line, highlighting the acceleration and deceleration of momentum.
- **Caution:** The smoothing process introduces lag. The signal line will always react slower than the raw RSI. This filtering is its main advantage, but traders should be aware of the delay.
-177
View File
@@ -1,177 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MovingAverages.mqh>
//--- 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
int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1; // Correct start pos
for(int i = ma_start_pos; i < rates_total; i++)
{
// --- FIX: Full, robust switch block for all MA types ---
switch(InpMethod)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferRawRSI[i-j];
BufferRSIMA[i] = sum/g_ExtPeriodMA;
}
else
{
if(InpMethod == MODE_EMA)
{
double pr=2.0/(g_ExtPeriodMA+1.0);
BufferRSIMA[i] = BufferRawRSI[i]*pr + BufferRSIMA[i-1]*(1.0-pr);
}
else
BufferRSIMA[i] = (BufferRSIMA[i-1]*(g_ExtPeriodMA-1)+BufferRawRSI[i])/g_ExtPeriodMA;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=BufferRawRSI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferRSIMA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferRawRSI[i-j];
BufferRSIMA[i] = sum/g_ExtPeriodMA;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-171
View File
@@ -1,171 +0,0 @@
//+------------------------------------------------------------------+
//| RSI_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx (Based on MetaQuotes RSI) |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "4.01" // Harmonized with fully manual MA calculations
#property description "RSI on Heikin Ashi prices, with a Moving Average."
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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 // RSI and its MA
#property indicator_plots 2
//--- Plot 1: RSI MA line (smoothed)
#property indicator_label1 "HA_RSIMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_DOT
#property indicator_width1 1
//--- Plot 2: RSI line (raw)
#property indicator_label2 "HA_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;
input group "Signal Line Settings"
input int InpPeriodMA = 14;
input ENUM_MA_METHOD InpMethodMA = MODE_SMA;
//--- Indicator Buffers ---
double BufferHARSI_MA[];
double BufferHARSI[];
//--- Global Objects and Variables ---
int g_ExtPeriodRSI, g_ExtPeriodMA;
CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI;
g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA;
SetIndexBuffer(0, BufferHARSI_MA, INDICATOR_DATA);
SetIndexBuffer(1, BufferHARSI, INDICATOR_DATA);
ArraySetAsSeries(BufferHARSI_MA, false);
ArraySetAsSeries(BufferHARSI, 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("HA_RSI(%d, %d)", g_ExtPeriodRSI, g_ExtPeriodMA));
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)
{
if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID)
{
delete g_ha_rsi_calculator;
g_ha_rsi_calculator = NULL;
}
}
//+------------------------------------------------------------------+
//| 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[])
{
int start_pos = g_ExtPeriodRSI + g_ExtPeriodMA;
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_ExtPeriodRSI, open, high, low, close, BufferHARSI))
{
Print("Heikin Ashi RSI calculation failed.");
return(0);
}
//--- STEP 2: Calculate the Signal Line (MA of HA RSI)
int ma_start_pos = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
for(int i = ma_start_pos; i < rates_total; i++)
{
switch(InpMethodMA)
{
case MODE_EMA:
case MODE_SMMA:
if(i == ma_start_pos)
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferHARSI[i-j];
BufferHARSI_MA[i] = sum/g_ExtPeriodMA;
}
else
{
if(InpMethodMA == MODE_EMA)
{
double pr=2.0/(g_ExtPeriodMA+1.0);
BufferHARSI_MA[i] = BufferHARSI[i]*pr + BufferHARSI_MA[i-1]*(1.0-pr);
}
else
BufferHARSI_MA[i] = (BufferHARSI_MA[i-1]*(g_ExtPeriodMA-1)+BufferHARSI[i])/g_ExtPeriodMA;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=BufferHARSI[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
BufferHARSI_MA[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=BufferHARSI[i-j];
BufferHARSI_MA[i] = sum/g_ExtPeriodMA;
}
break;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,162 +0,0 @@
//+------------------------------------------------------------------+
//| RSI_Oscillator_HeikinAshi.mq5|
//| Copyright 2025, xxxxxxxx |
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property link ""
#property version "1.00"
#property description "RSI Oscillator on Heikin Ashi data"
#include <MyIncludes\HeikinAshi_Tools.mqh>
//--- Indicator Window and Plot Properties ---
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrSilver
#property indicator_width1 1
#property indicator_label1 "HA_RSI_Osc"
#property indicator_level1 0.0
#property indicator_levelstyle STYLE_DOT
//--- Input Parameters ---
input int InpPeriodRSI = 14;
input group "Signal Line Settings"
input int InpPeriodMA = 14;
input ENUM_MA_METHOD InpMethodMA = MODE_SMA;
//--- Indicator Buffers ---
double BufferOscillator[];
//--- Global Objects and Variables ---
int g_ExtPeriodRSI, g_ExtPeriodMA;
CHeikinAshi_RSI_Calculator *g_ha_rsi_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
g_ExtPeriodRSI = (InpPeriodRSI < 1) ? 1 : InpPeriodRSI;
g_ExtPeriodMA = (InpPeriodMA < 1) ? 1 : InpPeriodMA;
SetIndexBuffer(0, BufferOscillator, INDICATOR_DATA);
ArraySetAsSeries(BufferOscillator, false);
int draw_begin = g_ExtPeriodRSI + g_ExtPeriodMA - 1;
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, draw_begin);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HA_RSI_Osc(%d,%d)", g_ExtPeriodRSI, g_ExtPeriodMA));
IndicatorSetInteger(INDICATOR_DIGITS, 2);
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)
{
if(CheckPointer(g_ha_rsi_calculator) != POINTER_INVALID)
{
delete g_ha_rsi_calculator;
g_ha_rsi_calculator = NULL;
}
}
//+------------------------------------------------------------------+
//| RSI Oscillator 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_ExtPeriodRSI + g_ExtPeriodMA - 1;
if(rates_total <= start_pos)
return(0);
//--- Internal Buffers for calculation ---
double buffer_rsi[], buffer_signal[];
ArrayResize(buffer_rsi, rates_total);
ArrayResize(buffer_signal, rates_total);
//--- STEP 1: Calculate Heikin Ashi RSI internally
if(!g_ha_rsi_calculator.Calculate(rates_total, g_ExtPeriodRSI, open, high, low, close, buffer_rsi))
{
Print("Heikin Ashi RSI calculation failed.");
return(0);
}
//--- STEP 2: Calculate the Signal Line (MA of HA 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+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
else
{
if(InpMethodMA == MODE_EMA)
{
double pr=2.0/(g_ExtPeriodMA+1.0);
buffer_signal[i] = buffer_rsi[i]*pr + buffer_signal[i-1]*(1.0-pr);
}
else
buffer_signal[i] = (buffer_signal[i-1]*(g_ExtPeriodMA-1)+buffer_rsi[i])/g_ExtPeriodMA;
}
break;
case MODE_LWMA:
{
double lwma_sum=0, weight_sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
{
int weight=g_ExtPeriodMA-j;
lwma_sum+=buffer_rsi[i-j]*weight;
weight_sum+=weight;
}
if(weight_sum>0)
buffer_signal[i]=lwma_sum/weight_sum;
}
break;
default: // MODE_SMA
{
double sum=0;
for(int j=0; j<g_ExtPeriodMA; j++)
sum+=buffer_rsi[i-j];
buffer_signal[i] = sum/g_ExtPeriodMA;
}
break;
}
}
//--- STEP 3: Calculate the final Oscillator value
for(int i = start_pos; i < rates_total; i++)
{
BufferOscillator[i] = buffer_rsi[i] - buffer_signal[i];
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-71
View File
@@ -1,71 +0,0 @@
# 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.
-226
View File
@@ -1,226 +0,0 @@
//+------------------------------------------------------------------+
//| 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<g_ExtLengthD; j++)
{
sum_rel += BufferEma_Relative[i-j];
sum_ran += BufferEma_Range[i-j];
}
BufferEmaEma_Relative[i] = sum_rel / g_ExtLengthD;
BufferEmaEma_Range[i] = sum_ran / g_ExtLengthD;
}
else
if(i > 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<g_ExtLengthEMA; j++)
sum_smi += BufferSMI[i-j];
BufferSignal[i] = sum_smi / g_ExtLengthEMA;
}
else
if(i > 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-268
View File
@@ -1,268 +0,0 @@
//+------------------------------------------------------------------+
//| 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 <MyIncludes\HeikinAshi_Tools.mqh>
//--- 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<g_ExtLengthD; j++)
{
sum_rel += BufferEma_Relative[i-j];
sum_ran += BufferEma_Range[i-j];
}
BufferEmaEma_Relative[i] = sum_rel / g_ExtLengthD;
BufferEmaEma_Range[i] = sum_ran / g_ExtLengthD;
}
else
if(i > 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<g_ExtLengthEMA; j++)
sum_smi += BufferSMI[i-j];
BufferSignal[i] = sum_smi / g_ExtLengthEMA;
}
else
if(i > 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
-65
View File
@@ -1,65 +0,0 @@
# Sine Weighted Moving Average (Sine WMA)
## 1. Summary (Introduction)
The Sine Weighted Moving Average (Sine WMA) is a specialized type of weighted moving average that uses a sine wave function to assign weights to price data. It was developed as an advanced smoothing filter, designed to reduce lag and provide a cleaner representation of the market's underlying trend.
Unlike traditional moving averages that are inherently lagging, the Sine WMA is a **symmetrical, zero-lag filter**. It achieves this by assigning the heaviest weights to the price data in the middle of the lookback period, with weights tapering off towards the beginning and end of the period, mirroring the shape of a sine wave.
The result is an exceptionally smooth line that acts as the "center of gravity" for the price action. It is not a trend-following tool in the classic sense but rather a superior smoothing mechanism for identifying the true equilibrium price and filtering out market noise.
## 2. Mathematical Foundations and Calculation Logic
The Sine WMA calculates a weighted average where the weights are derived from a sine function, creating a smooth, bell-shaped weighting curve.
### Required Components
* **Period (N):** The lookback period for the moving average.
* **Source Price:** The price series used for calculation (e.g., `PRICE_CLOSE`).
### Calculation Steps (Algorithm)
1. **Generate Sine Weights:** For a given period `N`, the weight for each bar `i` (where `i` ranges from 0 to N-1) is calculated using the sine function.
* $Weight_i = \sin\left(\frac{\pi \cdot (i+1)}{N+1}\right)$
2. **Calculate the Weighted Sum:** For each bar `t`, multiply the last `N` prices by the corresponding sine-based weights.
* $\text{Weighted Sum}_t = \sum_{i=0}^{N-1} (\text{Price}_{t-i} \cdot Weight_i)$
3. **Calculate the Sum of Weights:** Sum all the generated sine weights.
* $\text{Sum of Weights} = \sum_{i=0}^{N-1} Weight_i$
4. **Calculate the Final WMA Value:** Divide the weighted sum of prices by the sum of the weights.
* $\text{Sine WMA}_t = \frac{\text{Weighted Sum}_t}{\text{Sum of Weights}}$
This process results in a symmetrically weighted average that is centered on the data.
## 3. MQL5 Implementation Details
Our MQL5 implementation is a clean and robust indicator that accurately reflects the mathematical definition of a symmetrical, centered filter.
* **Modular, Reusable Calculation Engine (`Sine_WMA_Calculator.mqh`):** The entire calculation logic for both standard and Heikin Ashi versions is encapsulated within a single, powerful include file. This file contains a base `CSineWMACalculator` class and an inherited `CSineWMACalculator_HA` child class, eliminating code duplication and ensuring both versions are always in sync.
* **Efficient Weight Generation:** The sine-based weights are calculated only once during the indicator's initialization in the `Init()` method of the calculator class. The weights and their sum are stored in internal class members for efficient reuse.
* **Stability via Full Recalculation:** In line with our core principles, the indicator employs a "brute-force" full recalculation within the `OnCalculate` function. This is the most reliable method to ensure stability and prevent any potential glitches.
* **Correct Symmetrical Application:** The `Calculate` method applies the pre-calculated symmetrical weights directly to the price data. The weights are **not reversed**. This correctly implements the smoothing, centered nature of the filter. The inherent lag of `(Period-1)/2` bars is a mathematical property of the filter, not an implementation error.
* **Heikin Ashi Variant (`Sine_WMA_HeikinAshi.mq5`):**
* **As an experiment, a Heikin Ashi version of this indicator was also developed. However, testing revealed that the practical benefit is minimal.**
* **The "Double Smoothing" Effect:** The Heikin Ashi transformation is, in itself, a powerful smoothing algorithm. Applying a second, strong smoothing filter (the Sine WMA) to an already smoothed data series (HA Close) results in an extremely smooth line, but one that shows negligible difference from the standard version while potentially increasing lag.
* **Conclusion:** For symmetrical, smoothing-type filters like the Sine WMA, the standard version is recommended as it already provides excellent noise reduction. The Heikin Ashi variant remains in the toolkit as a technical demonstration of our modular calculation engine.
## 4. Parameters
* **Period (`InpPeriod`):** The lookback period for the moving average. A longer period results in a smoother line that is less sensitive to short-term price fluctuations. Default is `21`.
* **Source Price (`InpSourcePrice`):** The price data used for the calculation. **Note: This parameter is ignored by the Heikin Ashi version**, which always uses the HA Close price. Default is `PRICE_CLOSE`.
## 5. Usage and Interpretation
The Sine WMA should be interpreted as a **smoothing filter and a "mean" or "center of gravity" line**, not as a traditional trend-following moving average.
* **Noise Reduction and Trend Clarity:** The primary use of the Sine WMA is to filter out market noise and provide a much clearer picture of the underlying price movement.
* **Mean Reversion Signals:** The line acts as a "magnet" for the price. When the price moves significantly away from the Sine WMA, it can be considered over-extended, increasing the probability of a reversion back towards the line.
* **Confirmation of Trend Direction:** The slope of the Sine WMA provides a very stable, albeit lagging, confirmation of the main trend direction. A change in the slope's direction is a significant event.
* **Caution:** Due to its inherent nature as a centered, smoothing filter, the Sine WMA will always lag the price. It should **not** be used for fast crossover signals. Its strength lies in its smoothness and its ability to define the market's equilibrium point.
-185
View File
@@ -1,185 +0,0 @@
//+------------------------------------------------------------------+
//| Sine_WMA.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.01"
#property description "Sine Weighted Moving Average. A zero-lag smoothing filter."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
//--- Plot 1: Sine WMA Line
#property indicator_label1 "Sine WMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrAqua
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 21;
input ENUM_APPLIED_PRICE InpSourcePrice = PRICE_CLOSE;
//--- Indicator Buffers ---
double BufferWMA[];
double BufferPrice[];
//+------------------------------------------------------------------+
//| CLASS: CSineWMACalculator |
//| Encapsulates the logic for Sine weighting. |
//+------------------------------------------------------------------+
class CSineWMACalculator
{
private:
int m_period;
double m_weights[];
double m_weight_sum;
public:
CSineWMACalculator(void);
~CSineWMACalculator(void) {};
bool Init(int period);
void Calculate(int rates_total, const double &price_src[], double &wma_out[]);
};
//+------------------------------------------------------------------+
//| CSineWMACalculator: Constructor |
//+------------------------------------------------------------------+
CSineWMACalculator::CSineWMACalculator(void) : m_period(0), m_weight_sum(0)
{
}
//+------------------------------------------------------------------+
//| CSineWMACalculator: Initialization and Weight Generation |
//+------------------------------------------------------------------+
bool CSineWMACalculator::Init(int period)
{
m_period = (period < 2) ? 2 : period;
ArrayResize(m_weights, m_period);
m_weight_sum = 0;
for(int i = 0; i < m_period; i++)
{
m_weights[i] = MathSin(M_PI * (i + 1.0) / (m_period + 1.0));
m_weight_sum += m_weights[i];
}
return (m_weight_sum != 0);
}
//+------------------------------------------------------------------+
//| CSineWMACalculator: Main Calculation Method (No phase shift) |
//+------------------------------------------------------------------+
void CSineWMACalculator::Calculate(int rates_total, const double &price_src[], double &wma_out[])
{
if(rates_total < m_period)
return;
for(int i = m_period - 1; i < rates_total; i++)
{
double weighted_sum = 0;
for(int j = 0; j < m_period; j++)
{
//--- Symmetrical weighting, use weights as generated
weighted_sum += price_src[i - j] * m_weights[j];
}
//--- No displacement, result is plotted at the current bar
wma_out[i] = weighted_sum / m_weight_sum;
}
}
//--- Global calculator object ---
CSineWMACalculator *g_calculator;
//--- Forward declaration
int PriceSeries(ENUM_APPLIED_PRICE,int,const double&[],const double&[],const double&[],const double&[],double&[]);
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferWMA, INDICATOR_DATA);
ArraySetAsSeries(BufferWMA, false);
g_calculator = new CSineWMACalculator();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod))
{
Print("Failed to initialize Sine WMA Calculator.");
return(INIT_FAILED);
}
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SineWMA(%d)", InpPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
ArrayResize(BufferPrice, rates_total);
if(PriceSeries(InpSourcePrice, rates_total, open, high, low, close, BufferPrice) <= 0)
return 0;
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
g_calculator.Calculate(rates_total, BufferPrice, BufferWMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper function to get the selected price series. |
//+------------------------------------------------------------------+
int PriceSeries(ENUM_APPLIED_PRICE type, int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], double &dest_buffer[])
{
switch(type)
{
case PRICE_CLOSE:
ArrayCopy(dest_buffer, close, 0, 0, rates_total);
break;
case PRICE_OPEN:
ArrayCopy(dest_buffer, open, 0, 0, rates_total);
break;
case PRICE_HIGH:
ArrayCopy(dest_buffer, high, 0, 0, rates_total);
break;
case PRICE_LOW:
ArrayCopy(dest_buffer, low, 0, 0, rates_total);
break;
case PRICE_MEDIAN:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i])/2.0;
break;
case PRICE_TYPICAL:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i]+close[i])/3.0;
break;
case PRICE_WEIGHTED:
for(int i=0; i<rates_total; i++)
dest_buffer[i] = (high[i]+low[i]+close[i]+close[i])/4.0;
break;
default:
return 0;
}
return rates_total;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
@@ -1,75 +0,0 @@
//+------------------------------------------------------------------+
//| Sine_WMA_HeikinAshi.mq5 |
//| Copyright 2025, xxxxxxxx|
//| |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, xxxxxxxx"
#property version "1.00"
#property description "Sine Weighted Moving Average on Heikin Ashi data."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#include <MyIncludes\Sine_WMA_Calculator.mqh>
//--- Plot 1: Sine WMA Line
#property indicator_label1 "Sine WMA (HA)"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrAqua
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
//--- Input Parameters ---
input int InpPeriod = 21;
//--- Indicator Buffers ---
double BufferWMA[];
//--- Global calculator object ---
CSineWMACalculator_HA *g_calculator;
//+------------------------------------------------------------------+
//| Custom indicator initialization function. |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufferWMA, INDICATOR_DATA);
ArraySetAsSeries(BufferWMA, false);
g_calculator = new CSineWMACalculator_HA();
if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod))
{
Print("Failed to initialize Sine WMA HA Calculator.");
return(INIT_FAILED);
}
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SineWMA_HA(%d)", InpPeriod));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
delete g_calculator;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function. |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int, const datetime&[], const double &open[], const double &high[], const double &low[], const double &close[], const long&[], const long&[], const int&[])
{
if(CheckPointer(g_calculator) != POINTER_INVALID)
{
//--- The price_type parameter is ignored by the HA calculator, so we can pass a default
g_calculator.Calculate(rates_total, PRICE_CLOSE, open, high, low, close, BufferWMA);
}
return(rates_total);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+