├── ex5 ├── ehlers_adaptive_cci.ex5 ├── ehlers_adaptive_rsi.ex5 ├── ehlers_roofing_filter.ex5 ├── ehlers_supersmoother.ex5 ├── ehlers_adaptive_bandpass.ex5 ├── ehlers_adaptive_stochastic.ex5 ├── ehlers_decycler_oscillator.ex5 ├── ehlers_adaptive_bandpass_cube.ex5 ├── ehlers_adaptive_rsi_fischer.ex5 ├── ehlers_even_better_sinewave.ex5 ├── ehlers_autocorrelation_reversals.ex5 └── ehlers_adaptive_stochastic_invfischer.ex5 ├── LICENSE.md ├── README.md └── mq5 ├── ehlers_supersmoother.mq5 ├── ehlers_decycler_oscillator.mq5 ├── ehlers_roofing_filter.mq5 ├── ehlers_even_better_sinewave.mq5 ├── ehlers_autocorrelation_reversals.mq5 ├── ehlers_adaptive_stochastic.mq5 ├── ehlers_adaptive_bandpass.mq5 ├── ehlers_adaptive_bandpass_cube.mq5 ├── ehlers_adaptive_cci.mq5 ├── ehlers_adaptive_rsi.mq5 ├── ehlers_adaptive_stochastic_invfischer.mq5 └── ehlers_adaptive_rsi_fischer.mq5 /ex5/ehlers_adaptive_cci.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_cci.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_rsi.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_rsi.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_roofing_filter.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_roofing_filter.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_supersmoother.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_supersmoother.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_bandpass.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_bandpass.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_stochastic.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_stochastic.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_decycler_oscillator.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_decycler_oscillator.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_bandpass_cube.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_bandpass_cube.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_rsi_fischer.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_rsi_fischer.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_even_better_sinewave.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_even_better_sinewave.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_autocorrelation_reversals.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_autocorrelation_reversals.ex5 -------------------------------------------------------------------------------- /ex5/ehlers_adaptive_stochastic_invfischer.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thetestspecimen/ehlers-indicators-mql5/HEAD/ex5/ehlers_adaptive_stochastic_invfischer.ex5 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 thetestspecimen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is in this repository? 2 | 3 | This repository contains (MetaQuotes Language 5) MQL5 code (used in MetaTrader 5) for some of the indicators featured in "Cycle Analytics for Traders - Advanced Technical Trading Concepts" by John F. Ehlers. 4 | 5 | # Why are the files in this repository necessary? 6 | 7 | The aforementioned book contains full coded versions of the indicators discussed within the book. However, the book contains code written in EasyLanguage (which 8 | is typically used with TradeStation). MQL5 on the other hand is based on the object oriented language C++. They are very different. 9 | 10 | This repository is just a basic translation of the EasyLanguage code featured in the book into MQL5 code required for MetaTrader 5. 11 | 12 | # Can I use these indicators? 13 | 14 | The translation to MQL5 is free to use, however, the original EasyLanguage code is copyrighted by John F. Ehlers. 15 | 16 | Therefore, as far as I am aware it would be required to own a copy of the original book before usage of the files in this repository: 17 | 18 | Cycle Analytics for Traders - Advanced Technical Trading Concepts by John F. Ehlers\ 19 | Copyright © 2013 by John F. Ehlers. All rights reserved. 20 | 21 | [Wiley Publishing](https://www.wiley.com/en-us/Cycle+Analytics+for+Traders%3A+Advanced+Technical+Trading+Concepts%2C+%2B+Downloadable+Software-p-9781118728604)\ 22 | [Amazon UK](https://www.amazon.co.uk/Cycle-Analytics-Traders-Technical-Downloadable/dp/1118728513/)\ 23 | [Amazon US](https://www.amazon.com/Cycle-Analytics-Traders-Downloadable-Software/dp/1118728513) 24 | 25 | Furthermore, it is recommended to purhase the book, as without it you will have no insight into the applicability of each of the indicators. 26 | 27 | # Indicators 28 | 29 | ## What is included? 30 | 31 | There are two directories included in this repository. The first (mq5) includes the raw MQL5 code for each indicator. The second 32 | (ex5) contains the compiled version of each indicator. 33 | 34 | In terms of indicators the following are included: 35 | 36 | - Supersmoother 37 | - Roofing Filter 38 | - Even Better Sinewave 39 | - Decycler Oscillator 40 | - Autocorrelation Reversals 41 | - Adaptive Bandpass 42 | - Adaptive Bandpass Cube 43 | - Adaptive CCI 44 | - Adaptive RSI 45 | - Adaptive RSI Fischer 46 | - Adaptive Stochastic 47 | - Adaptive Stochastic Inverse Fischer 48 | 49 | ## How to use them? 50 | 51 | If you just want to use the indicators directly, then all you need is the .ex5 files (i.e. the compiled versions). Which can be 52 | loaded directly into MetaTrader 5 as with any other indicator file. 53 | 54 | If you want to make some tweaks to the files, you need the .mq5 code files, and then you will need to re-compile them once your 55 | adjustments have been made. 56 | 57 | ## Can I use these files with MetaTrader 4? 58 | 59 | No. 60 | 61 | MetaQuotes (the company who make the MetaTrader program) made some major changes to the coding language between MetaTrader 4 62 | and MetaTrader 5. They are completely different, and not compatible. 63 | 64 | # Disclaimer 65 | 66 | I will not be held responsible for any financial losses you may incur as a result of the usage of the files in this repository, 67 | regardless of whether there are errors in either the translated code, or the original code. 68 | 69 | If you choose to download and/or use the code in this repository, you do so at your own risk. If unsure, do not download or use it. 70 | 71 | # License 72 | 73 | As stated above, the copyright to the original EasyLanguage code is held by John F. Ehlers.\ 74 | Copyright © 2013 by John F. Ehlers. All rights reserved. 75 | 76 | The translation from EasyLanguage to MQL5 is licensed under the [MIT License](LICENSE.md) 77 | 78 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z7G2C89) 79 | -------------------------------------------------------------------------------- /mq5/ehlers_supersmoother.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_supersmoother.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_chart_window 16 | #property indicator_buffers 1 17 | #property indicator_plots 1 18 | 19 | // Plot Line 20 | #property indicator_label1 "SuperSmoother" 21 | #property indicator_type1 DRAW_LINE 22 | #property indicator_color1 clrOrange 23 | #property indicator_style1 STYLE_SOLID 24 | #property indicator_width1 2 25 | 26 | // set enumerated values for prices 27 | enum enPrices { 28 | pr_close, // Close 29 | pr_open, // Open 30 | pr_high, // High 31 | pr_low, // Low 32 | pr_median, // Median 33 | pr_typical, // Typical 34 | pr_weighted, // Weighted 35 | pr_average, // Average (high+low+open+close)/4 36 | pr_medianbody, // Average median body (open+close)/2 37 | pr_trendbiased, // Trend biased price 38 | pr_ha_close, // Heiken ashi close 39 | pr_ha_open, // Heiken ashi open 40 | pr_ha_high, // Heiken ashi high 41 | pr_ha_low, // Heiken ashi low 42 | pr_ha_median, // Heiken ashi median 43 | pr_ha_typical, // Heiken ashi typical 44 | pr_ha_weighted, // Heiken ashi weighted 45 | pr_ha_average, // Heiken ashi average 46 | pr_ha_medianbody, // Heiken ashi median body 47 | pr_ha_trendbiased // Heiken ashi trend biased price 48 | }; 49 | 50 | // inputs 51 | input enPrices Price = pr_close; // Price Type 52 | input int lpPeriod = 10; // Low Pass Period 53 | 54 | // buffers 55 | double result[]; // result buffer 56 | 57 | ////////////////////////////////////////////////////////////////// 58 | 59 | #define pi 3.14159265358979323846264338327950288 60 | double a1,b1,c1,c2,c3; 61 | 62 | int OnInit() { 63 | SetIndexBuffer(0, result, INDICATOR_DATA); 64 | a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lpPeriod); 65 | b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lpPeriod); 66 | c2 = b1; 67 | c3 = -a1 * a1; 68 | c1 = 1.0 - c2 - c3; 69 | return (0); 70 | } 71 | 72 | ////////////////////////////////////////////////////////////////// 73 | 74 | double work[]; 75 | 76 | int OnCalculate( 77 | const int rates_total, 78 | const int prev_calculated, 79 | const datetime& time[], 80 | const double& open[], 81 | const double& high[], 82 | const double& low[], 83 | const double& close[], 84 | const long& tick_volume[], 85 | const long& volume[], 86 | const int& spread[]) { 87 | 88 | if (Bars(_Symbol, _Period) < rates_total) { 89 | return (-1); 90 | } 91 | 92 | // resize the working array to fit the added data on each new bar 93 | if (ArrayRange(work, 0) != rates_total) { 94 | ArrayResize(work, rates_total); 95 | } 96 | 97 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 98 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 99 | to reset to 0, and hence cause a loop through all previous bars */ 100 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 101 | 102 | // get the price of the current bar based on the Price variable input 103 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 104 | 105 | if(i<3){ 106 | result[i] = c1 * work[i]; 107 | } else { 108 | result[i] = c1 * (work[i] + work[i - 1]) / 2.0 + c2 * result[i - 1] + c3 * result[i - 2]; 109 | } 110 | } 111 | 112 | // return value of prev_calculated for next call 113 | return (rates_total); 114 | } 115 | 116 | // this method gets allows the selection of which price to use 117 | // it also includes Heiken Ashi price outputs 118 | double workHa[][4]; 119 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 120 | 121 | if (priceType >= pr_ha_close) { 122 | 123 | if (ArrayRange(workHa, 0) != bars) { 124 | ArrayResize(workHa, bars); 125 | } 126 | 127 | double haOpen; 128 | if (i > 0) { 129 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 130 | } else { 131 | haOpen = (open[i] + close[i]) / 2; 132 | } 133 | 134 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 135 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 136 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 137 | 138 | workHa[i][0] = haLow; 139 | workHa[i][1] = haHigh; 140 | workHa[i][2] = haOpen; 141 | workHa[i][3] = haClose; 142 | 143 | switch (priceType) { 144 | case pr_ha_close: return (haClose); 145 | case pr_ha_open: return (haOpen); 146 | case pr_ha_high: return (haHigh); 147 | case pr_ha_low: return (haLow); 148 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 149 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 150 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 151 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 152 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 153 | case pr_ha_trendbiased: 154 | if (haClose > haOpen) { 155 | return ((haHigh + haClose) / 2.0); 156 | } else { 157 | return ((haLow + haClose) / 2.0); 158 | } 159 | } 160 | 161 | } else { 162 | 163 | switch (priceType) { 164 | case pr_close: return (close[i]); 165 | case pr_open: return (open[i]); 166 | case pr_high: return (high[i]); 167 | case pr_low: return (low[i]); 168 | case pr_median: return ((high[i] + low[i]) / 2.0); 169 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 170 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 171 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 172 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 173 | case pr_trendbiased: 174 | if (close[i] > open[i]) { 175 | return ((high[i] + close[i]) / 2.0); 176 | } else { 177 | return ((low[i] + close[i]) / 2.0); 178 | } 179 | } 180 | } 181 | return (0); 182 | } 183 | -------------------------------------------------------------------------------- /mq5/ehlers_decycler_oscillator.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_decycler_oscillator.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 3 17 | #property indicator_plots 1 18 | #property indicator_level1 0 19 | 20 | //Plot Line 21 | #property indicator_label1 "Decycler Oscillator" 22 | #property indicator_type1 DRAW_LINE 23 | #property indicator_color1 clrLightBlue 24 | #property indicator_style1 STYLE_SOLID 25 | #property indicator_width1 2 26 | 27 | // set enumerated values for prices 28 | enum enPrices { 29 | pr_close, // Close 30 | pr_open, // Open 31 | pr_high, // High 32 | pr_low, // Low 33 | pr_median, // Median 34 | pr_typical, // Typical 35 | pr_weighted, // Weighted 36 | pr_average, // Average (high+low+open+close)/4 37 | pr_medianbody, // Average median body (open+close)/2 38 | pr_trendbiased, // Trend biased price 39 | pr_ha_close, // Heiken ashi close 40 | pr_ha_open, // Heiken ashi open 41 | pr_ha_high, // Heiken ashi high 42 | pr_ha_low, // Heiken ashi low 43 | pr_ha_median, // Heiken ashi median 44 | pr_ha_typical, // Heiken ashi typical 45 | pr_ha_weighted, // Heiken ashi weighted 46 | pr_ha_average, // Heiken ashi average 47 | pr_ha_medianbody, // Heiken ashi median body 48 | pr_ha_trendbiased // Heiken ashi trend biased price 49 | }; 50 | 51 | // inputs 52 | input enPrices Price = pr_close; // Price Type 53 | input int lpPeriod = 30; // Low Pass Period 54 | input int hpPeriod = 60; // High Pass Period 55 | 56 | // buffers 57 | double result[]; // result buffer 58 | double highPass[]; // highpass buffer 59 | double lowPass[]; // lowpass buffer 60 | 61 | ////////////////////////////////////////////////////////////////// 62 | 63 | int OnInit() { 64 | SetIndexBuffer(0, result, INDICATOR_DATA); 65 | SetIndexBuffer(1, highPass, INDICATOR_CALCULATIONS); 66 | SetIndexBuffer(2, lowPass, INDICATOR_CALCULATIONS); 67 | return (0); 68 | } 69 | 70 | ////////////////////////////////////////////////////////////////// 71 | 72 | double work[]; 73 | 74 | int OnCalculate( 75 | const int rates_total, 76 | const int prev_calculated, 77 | const datetime& time[], 78 | const double& open[], 79 | const double& high[], 80 | const double& low[], 81 | const double& close[], 82 | const long& tick_volume[], 83 | const long& volume[], 84 | const int& spread[]) { 85 | 86 | if (Bars(_Symbol, _Period) < rates_total) { 87 | return (-1); 88 | } 89 | 90 | // resize the working array to fit the added data on each new bar 91 | if (ArrayRange(work, 0) != rates_total) { 92 | ArrayResize(work, rates_total); 93 | } 94 | 95 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 96 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 97 | to reset to 0, and hence cause a loop through all previous bars */ 98 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 99 | 100 | // get the price of the current bar based on the Price variable input 101 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 102 | 103 | // highpass filter 104 | lowPass[i] = processHighPass(lpPeriod, work, lowPass, i); 105 | 106 | // second highpass filter 107 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 108 | 109 | if(i<3){ 110 | result[i] = 0; 111 | } else { 112 | result[i] = highPass[i] - lowPass[i]; 113 | } 114 | } 115 | 116 | // return value of prev_calculated for next call 117 | return (rates_total); 118 | } 119 | 120 | 121 | // high pass filter 122 | double processHighPass(int highPassPeriod, double& inputArr[], double& outputArr[], int i) { 123 | 124 | double pi = 3.14159265358979323846264338327950288; 125 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 126 | if (i < 3) { 127 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 128 | } else { 129 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 130 | } 131 | } 132 | 133 | // this method gets allows the selection of which price to use 134 | // it also includes Heiken Ashi price outputs 135 | double workHa[][4]; 136 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 137 | 138 | if (priceType >= pr_ha_close) { 139 | 140 | if (ArrayRange(workHa, 0) != bars) { 141 | ArrayResize(workHa, bars); 142 | } 143 | 144 | double haOpen; 145 | if (i > 0) { 146 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 147 | } else { 148 | haOpen = (open[i] + close[i]) / 2; 149 | } 150 | 151 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 152 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 153 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 154 | 155 | workHa[i][0] = haLow; 156 | workHa[i][1] = haHigh; 157 | workHa[i][2] = haOpen; 158 | workHa[i][3] = haClose; 159 | 160 | switch (priceType) { 161 | case pr_ha_close: return (haClose); 162 | case pr_ha_open: return (haOpen); 163 | case pr_ha_high: return (haHigh); 164 | case pr_ha_low: return (haLow); 165 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 166 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 167 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 168 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 169 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 170 | case pr_ha_trendbiased: 171 | if (haClose > haOpen) { 172 | return ((haHigh + haClose) / 2.0); 173 | } else { 174 | return ((haLow + haClose) / 2.0); 175 | } 176 | } 177 | 178 | } else { 179 | 180 | switch (priceType) { 181 | case pr_close: return (close[i]); 182 | case pr_open: return (open[i]); 183 | case pr_high: return (high[i]); 184 | case pr_low: return (low[i]); 185 | case pr_median: return ((high[i] + low[i]) / 2.0); 186 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 187 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 188 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 189 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 190 | case pr_trendbiased: 191 | if (close[i] > open[i]) { 192 | return ((high[i] + close[i]) / 2.0); 193 | } else { 194 | return ((low[i] + close[i]) / 2.0); 195 | } 196 | } 197 | } 198 | return (0); 199 | } 200 | -------------------------------------------------------------------------------- /mq5/ehlers_roofing_filter.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_roofing_filter.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 3 17 | #property indicator_plots 2 18 | 19 | //Plot Line 20 | #property indicator_label1 "Roofing Filter" 21 | #property indicator_type1 DRAW_LINE 22 | #property indicator_color1 clrGoldenrod 23 | #property indicator_style1 STYLE_SOLID 24 | #property indicator_width1 2 25 | 26 | //Plot Signal Line 27 | #property indicator_label2 "Roofing Filter Signal" 28 | #property indicator_type2 DRAW_LINE 29 | #property indicator_color2 clrDodgerBlue 30 | #property indicator_style2 STYLE_DASH 31 | #property indicator_width2 2 32 | 33 | // set enumerated values for prices 34 | enum enPrices { 35 | pr_close, // Close 36 | pr_open, // Open 37 | pr_high, // High 38 | pr_low, // Low 39 | pr_median, // Median 40 | pr_typical, // Typical 41 | pr_weighted, // Weighted 42 | pr_average, // Average (high+low+open+close)/4 43 | pr_medianbody, // Average median body (open+close)/2 44 | pr_trendbiased, // Trend biased price 45 | pr_ha_close, // Heiken ashi close 46 | pr_ha_open, // Heiken ashi open 47 | pr_ha_high, // Heiken ashi high 48 | pr_ha_low, // Heiken ashi low 49 | pr_ha_median, // Heiken ashi median 50 | pr_ha_typical, // Heiken ashi typical 51 | pr_ha_weighted, // Heiken ashi weighted 52 | pr_ha_average, // Heiken ashi average 53 | pr_ha_medianbody, // Heiken ashi median body 54 | pr_ha_trendbiased // Heiken ashi trend biased price 55 | }; 56 | 57 | // inputs 58 | input enPrices Price = pr_close; // Price Type 59 | input int hpPeriod = 80; // High Pass Period 60 | input int lpPeriod = 40; // Low Pass Period 61 | 62 | // buffers 63 | double result[]; // result buffer 64 | double signal[]; // signal line buffer 65 | double highPass[]; // highpass buffer 66 | 67 | ////////////////////////////////////////////////////////////////// 68 | 69 | int OnInit() { 70 | SetIndexBuffer(0, result, INDICATOR_DATA); 71 | SetIndexBuffer(1, signal, INDICATOR_DATA); 72 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 73 | return (0); 74 | } 75 | 76 | ////////////////////////////////////////////////////////////////// 77 | 78 | double work[]; 79 | 80 | int OnCalculate( 81 | const int rates_total, 82 | const int prev_calculated, 83 | const datetime& time[], 84 | const double& open[], 85 | const double& high[], 86 | const double& low[], 87 | const double& close[], 88 | const long& tick_volume[], 89 | const long& volume[], 90 | const int& spread[]) { 91 | 92 | if (Bars(_Symbol, _Period) < rates_total) { 93 | return (-1); 94 | } 95 | 96 | // resize the working array to fit the added data on each new bar 97 | if (ArrayRange(work, 0) != rates_total) { 98 | ArrayResize(work, rates_total); 99 | } 100 | 101 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 102 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 103 | to reset to 0, and hence cause a loop through all previous bars */ 104 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 105 | 106 | // get the price of the current bar based on the Price variable input 107 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 108 | 109 | // highpass filter 110 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 111 | 112 | // lowpass filter (supersmoother) 113 | result[i] = processLowPass(lpPeriod, highPass, result, i); 114 | 115 | if(i>3){ 116 | signal[i] = result[i-2]; 117 | } 118 | 119 | } 120 | 121 | // return value of prev_calculated for next call 122 | return (rates_total); 123 | } 124 | 125 | 126 | // high pass filter 127 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 128 | 129 | double pi = 3.14159265358979323846264338327950288; 130 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 131 | if (i < 3) { 132 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 133 | } else { 134 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 135 | } 136 | } 137 | 138 | // low pass filter (supersmoother) 139 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 140 | 141 | double pi = 3.14159265358979323846264338327950288; 142 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 143 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 144 | double c2 = b1; 145 | double c3 = -a1 * a1; 146 | double c1 = 1.0 - c2 - c3; 147 | if (i < 3) { 148 | return (c1 * inputArr[i]); 149 | } else { 150 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 151 | } 152 | } 153 | 154 | // this method gets allows the selection of which price to use 155 | // it also includes Heiken Ashi price outputs 156 | double workHa[][4]; 157 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 158 | 159 | if (priceType >= pr_ha_close) { 160 | 161 | if (ArrayRange(workHa, 0) != bars) { 162 | ArrayResize(workHa, bars); 163 | } 164 | 165 | double haOpen; 166 | if (i > 0) { 167 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 168 | } else { 169 | haOpen = (open[i] + close[i]) / 2; 170 | } 171 | 172 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 173 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 174 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 175 | 176 | workHa[i][0] = haLow; 177 | workHa[i][1] = haHigh; 178 | workHa[i][2] = haOpen; 179 | workHa[i][3] = haClose; 180 | 181 | switch (priceType) { 182 | case pr_ha_close: return (haClose); 183 | case pr_ha_open: return (haOpen); 184 | case pr_ha_high: return (haHigh); 185 | case pr_ha_low: return (haLow); 186 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 187 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 188 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 189 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 190 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 191 | case pr_ha_trendbiased: 192 | if (haClose > haOpen) { 193 | return ((haHigh + haClose) / 2.0); 194 | } else { 195 | return ((haLow + haClose) / 2.0); 196 | } 197 | } 198 | 199 | } else { 200 | 201 | switch (priceType) { 202 | case pr_close: return (close[i]); 203 | case pr_open: return (open[i]); 204 | case pr_high: return (high[i]); 205 | case pr_low: return (low[i]); 206 | case pr_median: return ((high[i] + low[i]) / 2.0); 207 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 208 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 209 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 210 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 211 | case pr_trendbiased: 212 | if (close[i] > open[i]) { 213 | return ((high[i] + close[i]) / 2.0); 214 | } else { 215 | return ((low[i] + close[i]) / 2.0); 216 | } 217 | } 218 | } 219 | return (0); 220 | } 221 | -------------------------------------------------------------------------------- /mq5/ehlers_even_better_sinewave.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_even_better_sinewave.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 3 17 | #property indicator_plots 1 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -1.2 20 | #property indicator_level1 -1 21 | #property indicator_level2 1 22 | 23 | //Plot Line 24 | #property indicator_label1 "Even Better Sinewave" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrLightBlue 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | // set enumerated values for prices 31 | enum enPrices { 32 | pr_close, // Close 33 | pr_open, // Open 34 | pr_high, // High 35 | pr_low, // Low 36 | pr_median, // Median 37 | pr_typical, // Typical 38 | pr_weighted, // Weighted 39 | pr_average, // Average (high+low+open+close)/4 40 | pr_medianbody, // Average median body (open+close)/2 41 | pr_trendbiased, // Trend biased price 42 | pr_ha_close, // Heiken ashi close 43 | pr_ha_open, // Heiken ashi open 44 | pr_ha_high, // Heiken ashi high 45 | pr_ha_low, // Heiken ashi low 46 | pr_ha_median, // Heiken ashi median 47 | pr_ha_typical, // Heiken ashi typical 48 | pr_ha_weighted, // Heiken ashi weighted 49 | pr_ha_average, // Heiken ashi average 50 | pr_ha_medianbody, // Heiken ashi median body 51 | pr_ha_trendbiased // Heiken ashi trend biased price 52 | }; 53 | 54 | // inputs 55 | input enPrices Price = pr_close; // Price Type 56 | input int lpPeriod = 10; // Low Pass Period 57 | input int hpPeriod = 40; 58 | 59 | // buffers 60 | double result[]; // result buffer 61 | double highPass[]; // highpass buffer 62 | double filt[]; // lowpass buffer 63 | 64 | ////////////////////////////////////////////////////////////////// 65 | 66 | int OnInit() { 67 | SetIndexBuffer(0, result, INDICATOR_DATA); 68 | SetIndexBuffer(1, highPass, INDICATOR_CALCULATIONS); 69 | SetIndexBuffer(2, filt, INDICATOR_CALCULATIONS); 70 | return (0); 71 | } 72 | 73 | ////////////////////////////////////////////////////////////////// 74 | 75 | double work[]; 76 | 77 | int OnCalculate( 78 | const int rates_total, 79 | const int prev_calculated, 80 | const datetime& time[], 81 | const double& open[], 82 | const double& high[], 83 | const double& low[], 84 | const double& close[], 85 | const long& tick_volume[], 86 | const long& volume[], 87 | const int& spread[]) { 88 | 89 | if (Bars(_Symbol, _Period) < rates_total) { 90 | return (-1); 91 | } 92 | 93 | // resize the working array to fit the added data on each new bar 94 | if (ArrayRange(work, 0) != rates_total) { 95 | ArrayResize(work, rates_total); 96 | } 97 | 98 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 99 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 100 | to reset to 0, and hence cause a loop through all previous bars */ 101 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 102 | 103 | // get the price of the current bar based on the Price variable input 104 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 105 | 106 | // highpass filter 107 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 108 | 109 | // lowpass filter (supersmoother) 110 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 111 | 112 | 113 | double wave = 0; 114 | double pwr = 0; 115 | if(i<3){ 116 | result[i] = 0; 117 | } else { 118 | 119 | wave = (filt[i] + filt[i-1] + filt[i-2]) / 3; 120 | pwr = (pow(filt[i],2) + pow(filt[i-1],2) + pow(filt[i-2],2)) / 3; 121 | wave = wave / MathSqrt(pwr); 122 | result[i] = wave; 123 | } 124 | } 125 | 126 | // return value of prev_calculated for next call 127 | return (rates_total); 128 | } 129 | 130 | 131 | // high pass filter 132 | double processHighPass(int highPassPeriod, double& inputArr[], double& outputArr[], int i) { 133 | 134 | double pi = 3.14159265358979323846264338327950288; 135 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 136 | if (i < 3) { 137 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 138 | } else { 139 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 140 | } 141 | } 142 | 143 | // low pass filter (supersmoother) 144 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 145 | 146 | double pi = 3.14159265358979323846264338327950288; 147 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 148 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 149 | double c2 = b1; 150 | double c3 = -a1 * a1; 151 | double c1 = 1.0 - c2 - c3; 152 | if (i < 3) { 153 | return (c1 * inputArr[i]); 154 | } else { 155 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 156 | } 157 | } 158 | 159 | // this method gets allows the selection of which price to use 160 | // it also includes Heiken Ashi price outputs 161 | double workHa[][4]; 162 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 163 | 164 | if (priceType >= pr_ha_close) { 165 | 166 | if (ArrayRange(workHa, 0) != bars) { 167 | ArrayResize(workHa, bars); 168 | } 169 | 170 | double haOpen; 171 | if (i > 0) { 172 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 173 | } else { 174 | haOpen = (open[i] + close[i]) / 2; 175 | } 176 | 177 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 178 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 179 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 180 | 181 | workHa[i][0] = haLow; 182 | workHa[i][1] = haHigh; 183 | workHa[i][2] = haOpen; 184 | workHa[i][3] = haClose; 185 | 186 | switch (priceType) { 187 | case pr_ha_close: return (haClose); 188 | case pr_ha_open: return (haOpen); 189 | case pr_ha_high: return (haHigh); 190 | case pr_ha_low: return (haLow); 191 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 192 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 193 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 194 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 195 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 196 | case pr_ha_trendbiased: 197 | if (haClose > haOpen) { 198 | return ((haHigh + haClose) / 2.0); 199 | } else { 200 | return ((haLow + haClose) / 2.0); 201 | } 202 | } 203 | 204 | } else { 205 | 206 | switch (priceType) { 207 | case pr_close: return (close[i]); 208 | case pr_open: return (open[i]); 209 | case pr_high: return (high[i]); 210 | case pr_low: return (low[i]); 211 | case pr_median: return ((high[i] + low[i]) / 2.0); 212 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 213 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 214 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 215 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 216 | case pr_trendbiased: 217 | if (close[i] > open[i]) { 218 | return ((high[i] + close[i]) / 2.0); 219 | } else { 220 | return ((low[i] + close[i]) / 2.0); 221 | } 222 | } 223 | } 224 | return (0); 225 | } 226 | -------------------------------------------------------------------------------- /mq5/ehlers_autocorrelation_reversals.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_autocorrelation_reversals.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 4 17 | #property indicator_plots 1 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum 0 20 | 21 | //Plot Line 22 | #property indicator_label1 "Autocorrelation Reversals" 23 | #property indicator_type1 DRAW_LINE 24 | #property indicator_color1 clrFireBrick 25 | #property indicator_style1 STYLE_SOLID 26 | #property indicator_width1 2 27 | 28 | // set enumerated values for prices 29 | enum enPrices { 30 | pr_close, // Close 31 | pr_open, // Open 32 | pr_high, // High 33 | pr_low, // Low 34 | pr_median, // Median 35 | pr_typical, // Typical 36 | pr_weighted, // Weighted 37 | pr_average, // Average (high+low+open+close)/4 38 | pr_medianbody, // Average median body (open+close)/2 39 | pr_trendbiased, // Trend biased price 40 | pr_ha_close, // Heiken ashi close 41 | pr_ha_open, // Heiken ashi open 42 | pr_ha_high, // Heiken ashi high 43 | pr_ha_low, // Heiken ashi low 44 | pr_ha_median, // Heiken ashi median 45 | pr_ha_typical, // Heiken ashi typical 46 | pr_ha_weighted, // Heiken ashi weighted 47 | pr_ha_average, // Heiken ashi average 48 | pr_ha_medianbody, // Heiken ashi median body 49 | pr_ha_trendbiased // Heiken ashi trend biased price 50 | }; 51 | 52 | // inputs 53 | input enPrices Price = pr_close; // Price Type 54 | input int avgLength = 3; // Averaging Length 55 | input int hpPeriod = 48; // High Pass Period 56 | input int lpPeriod = 10; // Low Pass Period 57 | 58 | // buffers 59 | double result[]; // result buffer 60 | double filt[]; // filter buffer 61 | double highPass[]; // highpass buffer 62 | double corr2[]; 63 | 64 | ////////////////////////////////////////////////////////////////// 65 | 66 | int OnInit() { 67 | SetIndexBuffer(0, result, INDICATOR_DATA); 68 | SetIndexBuffer(1, filt, INDICATOR_CALCULATIONS); 69 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 70 | SetIndexBuffer(3, corr2, INDICATOR_CALCULATIONS); 71 | return (0); 72 | } 73 | 74 | ////////////////////////////////////////////////////////////////// 75 | 76 | double work[]; 77 | double corr[]; 78 | 79 | int OnCalculate( 80 | const int rates_total, 81 | const int prev_calculated, 82 | const datetime& time[], 83 | const double& open[], 84 | const double& high[], 85 | const double& low[], 86 | const double& close[], 87 | const long& tick_volume[], 88 | const long& volume[], 89 | const int& spread[]) { 90 | 91 | if (Bars(_Symbol, _Period) < rates_total) { 92 | return (-1); 93 | } 94 | 95 | // resize the working array to fit the added data on each new bar 96 | if (ArrayRange(work, 0) != rates_total) { 97 | ArrayResize(work, rates_total); 98 | } 99 | // if the default array size if not chosen then adjust the array sizes 100 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 101 | ArrayResize(corr, (hpPeriod + 1)); 102 | } 103 | 104 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 105 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 106 | to reset to 0, and hence cause a loop through all previous bars */ 107 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 108 | 109 | // get the price of the current bar based on the Price variable input 110 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 111 | 112 | // highpass filter 113 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 114 | 115 | // lowpass filter (supersmoother) 116 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 117 | 118 | // pearson correlation on lagged values 119 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr, corr2); 120 | 121 | // compute the result 122 | computeResult(hpPeriod, corr, corr2, result, i); 123 | 124 | } 125 | 126 | // return value of prev_calculated for next call 127 | return (rates_total); 128 | } 129 | 130 | void computeResult(double highPassPeriod, double& inputArr[], double& inputArrLagged[], double& finalResult[], int i){ 131 | 132 | if(i > highPassPeriod){ 133 | int sumDeltas = 0; 134 | for(int lag = 3; lag <= highPassPeriod; lag++){ 135 | if(((inputArr[lag] > 0.5) && (inputArrLagged[lag] < 0.5)) || ((inputArr[lag] < 0.5) && (inputArrLagged[lag] > 0.5))){ 136 | sumDeltas = sumDeltas + 1; 137 | } 138 | } 139 | 140 | int reversals = 0; 141 | if(sumDeltas > 24){ 142 | reversals = 1; 143 | } 144 | finalResult[i] = reversals; 145 | } else { 146 | finalResult[i] = 0; 147 | } 148 | } 149 | 150 | // high pass filter 151 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 152 | 153 | double pi = 3.14159265358979323846264338327950288; 154 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 155 | if (i < 3) { 156 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 157 | } else { 158 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 159 | } 160 | } 161 | 162 | // low pass filter (supersmoother) 163 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 164 | 165 | double pi = 3.14159265358979323846264338327950288; 166 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 167 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 168 | double c2 = b1; 169 | double c3 = -a1 * a1; 170 | double c1 = 1.0 - c2 - c3; 171 | if (i < 3) { 172 | return (c1 * inputArr[i]); 173 | } else { 174 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 175 | } 176 | } 177 | 178 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[], double& outputArrLagged[]) { 179 | 180 | int avLength = averageLength; 181 | if(averageLength == 0){ 182 | avLength = barsLag; 183 | } 184 | 185 | if (i > (barsLag + 1 + avLength)) { 186 | 187 | for (int lag = 0; lag <= barsLag; lag++) { 188 | outputArrLagged[lag] = outputArr[lag]; 189 | double m = (double)averageLength; 190 | if (averageLength == 0) { 191 | m = (double)lag; 192 | } 193 | 194 | double Sx = 0; 195 | double Sy = 0; 196 | double Sxx = 0; 197 | double Syy = 0; 198 | double Sxy = 0; 199 | 200 | for (int count = 0; count < m; count++) { 201 | double X = inputArr[i - count]; 202 | double Y = inputArr[i - lag - count]; 203 | Sx = Sx + X; 204 | Sy = Sy + Y; 205 | Sxx = Sxx + X * X; 206 | Sxy = Sxy + X * Y; 207 | Syy = Syy + Y * Y; 208 | } 209 | 210 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 211 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 212 | outputArr[lag] = 0.5 * (outputArr[lag] + 1); 213 | } 214 | 215 | } 216 | } 217 | } 218 | 219 | // this method gets allows the selection of which price to use 220 | // it also includes Heiken Ashi price outputs 221 | double workHa[][4]; 222 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 223 | 224 | if (priceType >= pr_ha_close) { 225 | 226 | if (ArrayRange(workHa, 0) != bars) { 227 | ArrayResize(workHa, bars); 228 | } 229 | 230 | double haOpen; 231 | if (i > 0) { 232 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 233 | } else { 234 | haOpen = (open[i] + close[i]) / 2; 235 | } 236 | 237 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 238 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 239 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 240 | 241 | workHa[i][0] = haLow; 242 | workHa[i][1] = haHigh; 243 | workHa[i][2] = haOpen; 244 | workHa[i][3] = haClose; 245 | 246 | switch (priceType) { 247 | case pr_ha_close: return (haClose); 248 | case pr_ha_open: return (haOpen); 249 | case pr_ha_high: return (haHigh); 250 | case pr_ha_low: return (haLow); 251 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 252 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 253 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 254 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 255 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 256 | case pr_ha_trendbiased: 257 | if (haClose > haOpen) { 258 | return ((haHigh + haClose) / 2.0); 259 | } else { 260 | return ((haLow + haClose) / 2.0); 261 | } 262 | } 263 | 264 | } else { 265 | 266 | switch (priceType) { 267 | case pr_close: return (close[i]); 268 | case pr_open: return (open[i]); 269 | case pr_high: return (high[i]); 270 | case pr_low: return (low[i]); 271 | case pr_median: return ((high[i] + low[i]) / 2.0); 272 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 273 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 274 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 275 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 276 | case pr_trendbiased: 277 | if (close[i] > open[i]) { 278 | return ((high[i] + close[i]) / 2.0); 279 | } else { 280 | return ((low[i] + close[i]) / 2.0); 281 | } 282 | } 283 | } 284 | return (0); 285 | } 286 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_stochastic.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_stochastic.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 4 17 | #property indicator_plots 1 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -0.2 20 | #property indicator_level1 0.3 21 | #property indicator_level2 0.7 22 | 23 | // Plot Line 24 | #property indicator_label1 "Adaptive Stochastic" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrPurple 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | // set enumerated values for prices 31 | enum enPrices { 32 | pr_close, // Close 33 | pr_open, // Open 34 | pr_high, // High 35 | pr_low, // Low 36 | pr_median, // Median 37 | pr_typical, // Typical 38 | pr_weighted, // Weighted 39 | pr_average, // Average (high+low+open+close)/4 40 | pr_medianbody, // Average median body (open+close)/2 41 | pr_trendbiased, // Trend biased price 42 | pr_ha_close, // Heiken ashi close 43 | pr_ha_open, // Heiken ashi open 44 | pr_ha_high, // Heiken ashi high 45 | pr_ha_low, // Heiken ashi low 46 | pr_ha_median, // Heiken ashi median 47 | pr_ha_typical, // Heiken ashi typical 48 | pr_ha_weighted, // Heiken ashi weighted 49 | pr_ha_average, // Heiken ashi average 50 | pr_ha_medianbody, // Heiken ashi median body 51 | pr_ha_trendbiased // Heiken ashi trend biased price 52 | }; 53 | 54 | // inputs 55 | input enPrices Price = pr_close; // Price Type 56 | input int avgLength = 3; // Averaging Length 57 | input int hpPeriod = 48; // High Pass Period 58 | input int lpPeriod = 10; // Low Pass Period 59 | 60 | // buffers 61 | double result[]; // result buffer 62 | double filt[]; // filter buffer 63 | double highPass[]; // highpass buffer 64 | double stoch[]; // stochastic buffer 65 | 66 | ////////////////////////////////////////////////////////////////// 67 | 68 | int OnInit() { 69 | SetIndexBuffer(0, result, INDICATOR_DATA); 70 | SetIndexBuffer(1, filt, INDICATOR_CALCULATIONS); 71 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 72 | SetIndexBuffer(3, stoch, INDICATOR_CALCULATIONS); 73 | return (0); 74 | } 75 | 76 | ////////////////////////////////////////////////////////////////// 77 | 78 | double work[]; 79 | double corr[]; 80 | double sqSum[]; 81 | double r[][2]; 82 | double maxPwr = 0.0; 83 | double pwr[]; 84 | 85 | int OnCalculate( 86 | const int rates_total, 87 | const int prev_calculated, 88 | const datetime& time[], 89 | const double& open[], 90 | const double& high[], 91 | const double& low[], 92 | const double& close[], 93 | const long& tick_volume[], 94 | const long& volume[], 95 | const int& spread[]) { 96 | 97 | if (Bars(_Symbol, _Period) < rates_total) { 98 | return (-1); 99 | } 100 | 101 | // resize the working array to fit the added data on each new bar 102 | if (ArrayRange(work, 0) != rates_total) { 103 | ArrayResize(work, rates_total); 104 | } 105 | // if the default array size if not chosen then adjust the array sizes 106 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 107 | ArrayResize(corr, (hpPeriod + 1)); 108 | ArrayResize(sqSum, (hpPeriod + 1)); 109 | ArrayResize(r, (hpPeriod + 1)); 110 | ArrayResize(pwr, (hpPeriod + 1)); 111 | } 112 | 113 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 114 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 115 | to reset to 0, and hence cause a loop through all previous bars */ 116 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 117 | 118 | // get the price of the current bar based on the Price variable input 119 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 120 | 121 | // highpass filter 122 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 123 | 124 | // lowpass filter (supersmoother) 125 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 126 | 127 | // pearson correlation on lagged values 128 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 129 | 130 | // Discrete Fourier Transform 131 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 132 | 133 | // EMA to smooth power measurement at each period 134 | processEMA(lpPeriod, hpPeriod, sqSum, r); 135 | 136 | // fast attack slow decay Auto Gain Control (AGC) 137 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 138 | 139 | // dominant cycle using the CG of the spectrum 140 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 141 | 142 | // stochastic (Result) 143 | computeStochastic(lpPeriod, hpPeriod, filt, (int)dominantCycle, stoch, result, i); 144 | } 145 | 146 | // return value of prev_calculated for next call 147 | return (rates_total); 148 | } 149 | 150 | // high pass filter 151 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 152 | 153 | double pi = 3.14159265358979323846264338327950288; 154 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 155 | if (i < 3) { 156 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 157 | } else { 158 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 159 | } 160 | } 161 | 162 | // low pass filter (supersmoother) 163 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 164 | 165 | double pi = 3.14159265358979323846264338327950288; 166 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 167 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 168 | double c2 = b1; 169 | double c3 = -a1 * a1; 170 | double c1 = 1.0 - c2 - c3; 171 | if (i < 3) { 172 | return (c1 * inputArr[i]); 173 | } else { 174 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 175 | } 176 | } 177 | 178 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 179 | 180 | int avLength = averageLength; 181 | if(averageLength == 0){ 182 | avLength = barsLag; 183 | } 184 | 185 | if (i > (barsLag + 1 + avLength)) { 186 | 187 | for (int lag = 0; lag <= barsLag; lag++) { 188 | 189 | double m = (double)averageLength; 190 | if (averageLength == 0) { 191 | m = (double)lag; 192 | } 193 | 194 | double Sx = 0; 195 | double Sy = 0; 196 | double Sxx = 0; 197 | double Syy = 0; 198 | double Sxy = 0; 199 | 200 | for (int count = 0; count < m; count++) { 201 | double X = inputArr[i - count]; 202 | double Y = inputArr[i - lag - count]; 203 | Sx = Sx + X; 204 | Sy = Sy + Y; 205 | Sxx = Sxx + X * X; 206 | Sxy = Sxy + X * Y; 207 | Syy = Syy + Y * Y; 208 | } 209 | 210 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 211 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 212 | } 213 | 214 | } 215 | } 216 | } 217 | 218 | // Discrete Fourier Transform (DFT) 219 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 220 | 221 | double pi = 3.14159265358979323846264338327950288; 222 | double cosPart[]; 223 | double sinPart[]; 224 | ArrayResize(cosPart, highPassPeriod + 1); 225 | ArrayResize(sinPart, highPassPeriod + 1); 226 | 227 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 228 | 229 | cosPart[period] = 0.0; 230 | sinPart[period] = 0.0; 231 | 232 | // Find Cosine and Sine correlated components 233 | for (int n = 3; n <= highPassPeriod; n++) { 234 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 235 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 236 | } 237 | 238 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 239 | } 240 | } 241 | 242 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 243 | 244 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 245 | outputArr[period, 1] = outputArr[period, 0]; 246 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 247 | } 248 | 249 | } 250 | 251 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 252 | 253 | maxPower = 0.991 * maxPower; 254 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 255 | if (inputArr[period, 0] > maxPower) { 256 | maxPower = inputArr[period, 0]; 257 | } 258 | } 259 | if (maxPower != 0) { 260 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 261 | outputArr[period1] = inputArr[period1, 0] / maxPower; 262 | } 263 | } else { 264 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 265 | outputArr[period1] = 0; 266 | } 267 | } 268 | } 269 | 270 | // dominant cycle using the CG of the spectrum 271 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 272 | 273 | double Spx = 0; 274 | double Sp = 0; 275 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 276 | if (inputArr[period] >= 0.5) { 277 | Spx = Spx + period * inputArr[period]; 278 | Sp = Sp + inputArr[period]; 279 | } 280 | } 281 | 282 | double domCycle = 0; 283 | if (Sp != 0) { 284 | domCycle = Spx / Sp; 285 | } 286 | if (domCycle < lowPassPeriod) { 287 | domCycle = (double)lowPassPeriod; 288 | } 289 | if (domCycle > highPassPeriod) { 290 | domCycle = (double)highPassPeriod; 291 | } 292 | return domCycle; 293 | } 294 | 295 | void computeStochastic(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& stochastic[], double& finalResult[], int i){ 296 | 297 | if (i > (highPassPeriod + 1)){ 298 | 299 | double pi = 3.14159265358979323846264338327950288; 300 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / lowPassPeriod); 301 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / lowPassPeriod); 302 | double c2 = b1; 303 | double c3 = -a1 * a1; 304 | double c1 = 1.0 - c2 - c3; 305 | 306 | double highestC = inputArr[i]; 307 | double lowestC = inputArr[i]; 308 | 309 | for (int cycle = 0; cycle < domCycle; cycle++) { 310 | if (inputArr[i - cycle] > highestC) { 311 | highestC = inputArr[i - cycle]; 312 | } 313 | if (inputArr[i - cycle] < lowestC) { 314 | lowestC = inputArr[i - cycle]; 315 | } 316 | } 317 | 318 | stochastic[i] = (inputArr[i] - lowestC) / (highestC - lowestC); 319 | finalResult[i] = c1 * (stochastic[i] + stochastic[i - 1]) / 2.0 + c2 * finalResult[i - 1] + c3 * finalResult[i - 2]; 320 | 321 | } else { 322 | 323 | stochastic[i] = 0; 324 | finalResult[i] = 0; 325 | 326 | } 327 | } 328 | 329 | // this method gets allows the selection of which price to use 330 | // it also includes Heiken Ashi price outputs 331 | 332 | double workHa[][4]; 333 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 334 | 335 | if (priceType >= pr_ha_close) { 336 | 337 | if (ArrayRange(workHa, 0) != bars) { 338 | ArrayResize(workHa, bars); 339 | } 340 | 341 | double haOpen; 342 | if (i > 0) { 343 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 344 | } else { 345 | haOpen = (open[i] + close[i]) / 2; 346 | } 347 | 348 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 349 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 350 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 351 | 352 | workHa[i][0] = haLow; 353 | workHa[i][1] = haHigh; 354 | workHa[i][2] = haOpen; 355 | workHa[i][3] = haClose; 356 | 357 | switch (priceType) { 358 | case pr_ha_close: return (haClose); 359 | case pr_ha_open: return (haOpen); 360 | case pr_ha_high: return (haHigh); 361 | case pr_ha_low: return (haLow); 362 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 363 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 364 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 365 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 366 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 367 | case pr_ha_trendbiased: 368 | if (haClose > haOpen) { 369 | return ((haHigh + haClose) / 2.0); 370 | } else { 371 | return ((haLow + haClose) / 2.0); 372 | } 373 | } 374 | 375 | } else { 376 | 377 | switch (priceType) { 378 | case pr_close: return (close[i]); 379 | case pr_open: return (open[i]); 380 | case pr_high: return (high[i]); 381 | case pr_low: return (low[i]); 382 | case pr_median: return ((high[i] + low[i]) / 2.0); 383 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 384 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 385 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 386 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 387 | case pr_trendbiased: 388 | if (close[i] > open[i]) { 389 | return ((high[i] + close[i]) / 2.0); 390 | } else { 391 | return ((low[i] + close[i]) / 2.0); 392 | } 393 | } 394 | } 395 | return (0); 396 | } 397 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_bandpass.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_bandpass.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 5 17 | #property indicator_plots 2 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -1.2 20 | #property indicator_level1 -0.707 21 | #property indicator_level2 0.707 22 | 23 | //Plot Line 24 | #property indicator_label1 "Adaptive BandPass" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrGoldenrod 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | //Plot Signal Line 31 | #property indicator_label2 "Bandpass Signal" 32 | #property indicator_type2 DRAW_LINE 33 | #property indicator_color2 clrDodgerBlue 34 | #property indicator_style2 STYLE_DASH 35 | #property indicator_width2 2 36 | 37 | // set enumerated values for prices 38 | enum enPrices { 39 | pr_close, // Close 40 | pr_open, // Open 41 | pr_high, // High 42 | pr_low, // Low 43 | pr_median, // Median 44 | pr_typical, // Typical 45 | pr_weighted, // Weighted 46 | pr_average, // Average (high+low+open+close)/4 47 | pr_medianbody, // Average median body (open+close)/2 48 | pr_trendbiased, // Trend biased price 49 | pr_ha_close, // Heiken ashi close 50 | pr_ha_open, // Heiken ashi open 51 | pr_ha_high, // Heiken ashi high 52 | pr_ha_low, // Heiken ashi low 53 | pr_ha_median, // Heiken ashi median 54 | pr_ha_typical, // Heiken ashi typical 55 | pr_ha_weighted, // Heiken ashi weighted 56 | pr_ha_average, // Heiken ashi average 57 | pr_ha_medianbody, // Heiken ashi median body 58 | pr_ha_trendbiased // Heiken ashi trend biased price 59 | }; 60 | 61 | // inputs 62 | input enPrices Price = pr_close; // Price Type 63 | input double bandwidth = 0.3; 64 | input int avgLength = 3; // Averaging Length 65 | input int hpPeriod = 48; // High Pass Period 66 | input int lpPeriod = 10; // Low Pass Period 67 | 68 | // buffers 69 | double result[]; // result buffer 70 | double signal[]; // signal line buffer 71 | double filt[]; // filter buffer 72 | double highPass[]; // highpass buffer 73 | double bandPass[]; // bandPass buffer 74 | 75 | ////////////////////////////////////////////////////////////////// 76 | 77 | int OnInit() { 78 | SetIndexBuffer(0, result, INDICATOR_DATA); 79 | SetIndexBuffer(1, signal, INDICATOR_DATA); 80 | SetIndexBuffer(2, filt, INDICATOR_CALCULATIONS); 81 | SetIndexBuffer(3, highPass, INDICATOR_CALCULATIONS); 82 | SetIndexBuffer(4, bandPass, INDICATOR_CALCULATIONS); 83 | return (0); 84 | } 85 | 86 | ////////////////////////////////////////////////////////////////// 87 | 88 | double work[]; 89 | double corr[]; 90 | double sqSum[]; 91 | double r[][2]; 92 | double maxPwr = 0.0; 93 | double pwr[]; 94 | double peak = 0.0; 95 | 96 | int OnCalculate( 97 | const int rates_total, 98 | const int prev_calculated, 99 | const datetime& time[], 100 | const double& open[], 101 | const double& high[], 102 | const double& low[], 103 | const double& close[], 104 | const long& tick_volume[], 105 | const long& volume[], 106 | const int& spread[]) { 107 | 108 | if (Bars(_Symbol, _Period) < rates_total) { 109 | return (-1); 110 | } 111 | 112 | // resize the working array to fit the added data on each new bar 113 | if (ArrayRange(work, 0) != rates_total) { 114 | ArrayResize(work, rates_total); 115 | } 116 | 117 | // if the default array size if not chosen then adjust the array sizes 118 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 119 | ArrayResize(corr, (hpPeriod + 1)); 120 | ArrayResize(sqSum, (hpPeriod + 1)); 121 | ArrayResize(r, (hpPeriod + 1)); 122 | ArrayResize(pwr, (hpPeriod + 1)); 123 | } 124 | 125 | /* Most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 126 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 127 | to reset to 0, and hence cause a loop through all previous bars */ 128 | 129 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 130 | 131 | // get the price of the current bar based on the Price variable input 132 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 133 | 134 | // highpass filter 135 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 136 | 137 | // lowpass filter (supersmoother) 138 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 139 | 140 | // pearson correlation on lagged values 141 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 142 | 143 | // discrete fourier transform 144 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 145 | 146 | // EMA to smooth power measurement at each period 147 | processEMA(lpPeriod, hpPeriod, sqSum, r); 148 | 149 | // fast attack slow decay Auto Gain Control (AGC) 150 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 151 | 152 | // dominant cycle using the CG of the spectrum 153 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 154 | 155 | // stochastic (Result) 156 | computeBandPass(lpPeriod, hpPeriod, filt, (int)dominantCycle, bandPass, result, i, bandwidth, peak, signal); 157 | } 158 | 159 | // return value of prev_calculated for next call 160 | return (rates_total); 161 | } 162 | 163 | void computeBandPass(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& bandpass[], double& finalResult[], int i, double bandWidth, double& peakVal, double& signalLine[]){ 164 | 165 | if (i > (highPassPeriod + 1)){ 166 | 167 | double pi = 3.14159265358979323846264338327950288; 168 | double beta1 = MathCos((2*pi) / (0.9 * domCycle)); 169 | double gamma1 = 1 / MathCos((2*pi) * bandWidth / (0.9*domCycle)); 170 | double alpha2 = gamma1 - MathSqrt(pow(gamma1,2) - 1); 171 | 172 | bandpass[i] = 0.5*(1 - alpha2)*(inputArr[i] - inputArr[i-2]) + beta1*(1 + alpha2)*bandpass[i-1] - alpha2*bandpass[i-2]; 173 | 174 | peakVal = 0.991*peakVal; 175 | if(MathAbs(bandpass[i]) > peakVal){ 176 | peakVal = MathAbs(bandpass[i]); 177 | } 178 | if(peakVal != 0){ 179 | finalResult[i] = bandpass[i] / peakVal; 180 | } else { 181 | finalResult[i] = 0; 182 | } 183 | 184 | signalLine[i] = 0.9 * finalResult[i-1]; 185 | 186 | 187 | } else { 188 | 189 | finalResult[i] = 0; 190 | 191 | } 192 | } 193 | 194 | // pass input data through a high pass filter 195 | 196 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 197 | 198 | double pi = 3.14159265358979323846264338327950288; 199 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 200 | if (i < 3) { 201 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 202 | } else { 203 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 204 | } 205 | } 206 | 207 | // pass input data through a low pass filter (supersmoother) 208 | 209 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 210 | 211 | double pi = 3.14159265358979323846264338327950288; 212 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 213 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 214 | double c2 = b1; 215 | double c3 = -a1 * a1; 216 | double c1 = 1.0 - c2 - c3; 217 | if (i < 3) { 218 | return (c1 * inputArr[i]); 219 | } else { 220 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 221 | } 222 | } 223 | 224 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 225 | 226 | int avLength = averageLength; 227 | if(averageLength == 0){ 228 | avLength = barsLag; 229 | } 230 | 231 | if (i > (barsLag + 1 + avLength)) { 232 | 233 | for (int lag = 0; lag <= barsLag; lag++) { 234 | 235 | double m = (double)averageLength; 236 | if (averageLength == 0) { 237 | m = (double)lag; 238 | } 239 | 240 | double Sx = 0; 241 | double Sy = 0; 242 | double Sxx = 0; 243 | double Syy = 0; 244 | double Sxy = 0; 245 | 246 | for (int count = 0; count < m; count++) { 247 | double X = inputArr[i - count]; 248 | double Y = inputArr[i - lag - count]; 249 | Sx = Sx + X; 250 | Sy = Sy + Y; 251 | Sxx = Sxx + X * X; 252 | Sxy = Sxy + X * Y; 253 | Syy = Syy + Y * Y; 254 | } 255 | 256 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 257 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 258 | } 259 | 260 | } 261 | } 262 | } 263 | 264 | // Discrete Fourier Transform (DFT) 265 | 266 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 267 | 268 | double pi = 3.14159265358979323846264338327950288; 269 | double cosPart[]; 270 | double sinPart[]; 271 | ArrayResize(cosPart, highPassPeriod + 1); 272 | ArrayResize(sinPart, highPassPeriod + 1); 273 | 274 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 275 | 276 | cosPart[period] = 0.0; 277 | sinPart[period] = 0.0; 278 | 279 | // find cosine and sine correlated components 280 | for (int n = 3; n <= highPassPeriod; n++) { 281 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 282 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 283 | } 284 | 285 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 286 | } 287 | } 288 | 289 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 290 | 291 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 292 | outputArr[period, 1] = outputArr[period, 0]; 293 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 294 | } 295 | 296 | } 297 | 298 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 299 | 300 | maxPower = 0.991 * maxPower; 301 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 302 | if (inputArr[period, 0] > maxPower) { 303 | maxPower = inputArr[period, 0]; 304 | } 305 | } 306 | if (maxPower != 0) { 307 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 308 | outputArr[period1] = inputArr[period1, 0] / maxPower; 309 | } 310 | } else { 311 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 312 | outputArr[period1] = 0; 313 | } 314 | } 315 | } 316 | 317 | //Compute the dominant cycle using the CG of the spectrum 318 | 319 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 320 | 321 | double Spx = 0; 322 | double Sp = 0; 323 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 324 | if (inputArr[period] >= 0.5) { 325 | Spx = Spx + period * inputArr[period]; 326 | Sp = Sp + inputArr[period]; 327 | } 328 | } 329 | 330 | double domCycle = 0; 331 | if (Sp != 0) { 332 | domCycle = Spx / Sp; 333 | } 334 | if (domCycle < lowPassPeriod) { 335 | domCycle = (double)lowPassPeriod; 336 | } 337 | 338 | return domCycle; 339 | } 340 | 341 | // this method gets allows the selection of which price to use 342 | // it also includes Heiken Ashi price outputs 343 | 344 | double workHa[][4]; 345 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 346 | 347 | if (priceType >= pr_ha_close) { 348 | 349 | if (ArrayRange(workHa, 0) != bars) { 350 | ArrayResize(workHa, bars); 351 | } 352 | 353 | double haOpen; 354 | if (i > 0) { 355 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 356 | } else { 357 | haOpen = (open[i] + close[i]) / 2; 358 | } 359 | 360 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 361 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 362 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 363 | 364 | workHa[i][0] = haLow; 365 | workHa[i][1] = haHigh; 366 | workHa[i][2] = haOpen; 367 | workHa[i][3] = haClose; 368 | 369 | switch (priceType) { 370 | case pr_ha_close: return (haClose); 371 | case pr_ha_open: return (haOpen); 372 | case pr_ha_high: return (haHigh); 373 | case pr_ha_low: return (haLow); 374 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 375 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 376 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 377 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 378 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 379 | case pr_ha_trendbiased: 380 | if (haClose > haOpen) { 381 | return ((haHigh + haClose) / 2.0); 382 | } else { 383 | return ((haLow + haClose) / 2.0); 384 | } 385 | } 386 | 387 | } else { 388 | 389 | switch (priceType) { 390 | case pr_close: return (close[i]); 391 | case pr_open: return (open[i]); 392 | case pr_high: return (high[i]); 393 | case pr_low: return (low[i]); 394 | case pr_median: return ((high[i] + low[i]) / 2.0); 395 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 396 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 397 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 398 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 399 | case pr_trendbiased: 400 | if (close[i] > open[i]) { 401 | return ((high[i] + close[i]) / 2.0); 402 | } else { 403 | return ((low[i] + close[i]) / 2.0); 404 | } 405 | } 406 | } 407 | return (0); 408 | } 409 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_bandpass_cube.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_bandpass_cube.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 5 17 | #property indicator_plots 2 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -1.2 20 | #property indicator_level1 -0.707 21 | #property indicator_level2 0.707 22 | 23 | //Plot Line 24 | #property indicator_label1 "Adaptive BandPass" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrGoldenrod 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | //Plot Signal Line 31 | #property indicator_label2 "Bandpass Signal" 32 | #property indicator_type2 DRAW_LINE 33 | #property indicator_color2 clrDodgerBlue 34 | #property indicator_style2 STYLE_DASH 35 | #property indicator_width2 2 36 | 37 | // set enumerated values for prices 38 | enum enPrices { 39 | pr_close, // Close 40 | pr_open, // Open 41 | pr_high, // High 42 | pr_low, // Low 43 | pr_median, // Median 44 | pr_typical, // Typical 45 | pr_weighted, // Weighted 46 | pr_average, // Average (high+low+open+close)/4 47 | pr_medianbody, // Average median body (open+close)/2 48 | pr_trendbiased, // Trend biased price 49 | pr_ha_close, // Heiken ashi close 50 | pr_ha_open, // Heiken ashi open 51 | pr_ha_high, // Heiken ashi high 52 | pr_ha_low, // Heiken ashi low 53 | pr_ha_median, // Heiken ashi median 54 | pr_ha_typical, // Heiken ashi typical 55 | pr_ha_weighted, // Heiken ashi weighted 56 | pr_ha_average, // Heiken ashi average 57 | pr_ha_medianbody, // Heiken ashi median body 58 | pr_ha_trendbiased // Heiken ashi trend biased price 59 | }; 60 | 61 | // inputs 62 | input enPrices Price = pr_close; // Price Type 63 | input double bandwidth = 0.3; 64 | input int avgLength = 3; // Averaging Length 65 | input int hpPeriod = 48; // High Pass Period 66 | input int lpPeriod = 10; // Low Pass Period 67 | 68 | // buffers 69 | double result[]; // result buffer 70 | double signal[]; // signal line buffer 71 | double filt[]; // filter buffer 72 | double highPass[]; // highpass buffer 73 | double bandPass[]; // bandPass buffer 74 | 75 | ////////////////////////////////////////////////////////////////// 76 | 77 | int OnInit() { 78 | SetIndexBuffer(0, result, INDICATOR_DATA); 79 | SetIndexBuffer(1, signal, INDICATOR_DATA); 80 | SetIndexBuffer(2, filt, INDICATOR_CALCULATIONS); 81 | SetIndexBuffer(3, highPass, INDICATOR_CALCULATIONS); 82 | SetIndexBuffer(4, bandPass, INDICATOR_CALCULATIONS); 83 | return (0); 84 | } 85 | 86 | ////////////////////////////////////////////////////////////////// 87 | 88 | double work[]; 89 | double corr[]; 90 | double sqSum[]; 91 | double r[][2]; 92 | double maxPwr = 0.0; 93 | double pwr[]; 94 | double peak = 0.0; 95 | 96 | int OnCalculate( 97 | const int rates_total, 98 | const int prev_calculated, 99 | const datetime& time[], 100 | const double& open[], 101 | const double& high[], 102 | const double& low[], 103 | const double& close[], 104 | const long& tick_volume[], 105 | const long& volume[], 106 | const int& spread[]) { 107 | 108 | if (Bars(_Symbol, _Period) < rates_total) { 109 | return (-1); 110 | } 111 | 112 | // resize the working array to fit the added data on each new bar 113 | if (ArrayRange(work, 0) != rates_total) { 114 | ArrayResize(work, rates_total); 115 | } 116 | 117 | // if the default array size if not chosen then adjust the array sizes 118 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 119 | ArrayResize(corr, (hpPeriod + 1)); 120 | ArrayResize(sqSum, (hpPeriod + 1)); 121 | ArrayResize(r, (hpPeriod + 1)); 122 | ArrayResize(pwr, (hpPeriod + 1)); 123 | } 124 | 125 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 126 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 127 | to reset to 0, and hence cause a loop through all previous bars */ 128 | 129 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 130 | 131 | // get the price of the current bar based on the Price variable input 132 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 133 | 134 | // highpass filter 135 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 136 | 137 | // lowpass filter (supersmoother) 138 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 139 | 140 | // pearson correlation on lagged values 141 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 142 | 143 | // Discrete Fourier Transform 144 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 145 | 146 | // EMA to smooth power measurement at each period 147 | processEMA(lpPeriod, hpPeriod, sqSum, r); 148 | 149 | // fast attack slow decay Auto Gain Control (AGC) 150 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 151 | 152 | // dominant cycle using the CG of the spectrum 153 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 154 | 155 | // stochastic (Result) 156 | computeBandPass(lpPeriod, hpPeriod, filt, (int)dominantCycle, bandPass, result, i, bandwidth, peak, signal); 157 | } 158 | 159 | // return value of prev_calculated for next call 160 | return (rates_total); 161 | } 162 | 163 | void computeBandPass(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& bandpass[], double& finalResult[], int i, double bandWidth, double& peakVal, double& signalLine[]){ 164 | 165 | if (i > (highPassPeriod + 1)){ 166 | 167 | double pi = 3.14159265358979323846264338327950288; 168 | double beta1 = MathCos((2*pi) / (0.9 * domCycle)); 169 | double gamma1 = 1 / MathCos((2*pi) * bandWidth / (0.9*domCycle)); 170 | double alpha2 = gamma1 - MathSqrt(pow(gamma1,2) - 1); 171 | 172 | bandpass[i] = 0.5*(1 - alpha2)*(inputArr[i] - inputArr[i-2]) + beta1*(1 + alpha2)*bandpass[i-1] - alpha2*bandpass[i-2]; 173 | 174 | peakVal = 0.991*peakVal; 175 | if(MathAbs(bandpass[i]) > peakVal){ 176 | peakVal = MathAbs(bandpass[i]); 177 | } 178 | if(peakVal != 0){ 179 | finalResult[i] = pow((bandpass[i] / peakVal),3); 180 | } else { 181 | finalResult[i] = 0; 182 | } 183 | 184 | signalLine[i] = 0.9 * finalResult[i-1]; 185 | 186 | 187 | } else { 188 | 189 | finalResult[i] = 0; 190 | 191 | } 192 | } 193 | 194 | // pass input data through a high pass filter 195 | 196 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 197 | 198 | double pi = 3.14159265358979323846264338327950288; 199 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 200 | if (i < 3) { 201 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 202 | } else { 203 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 204 | } 205 | } 206 | 207 | // pass input data through a low pass filter (supersmoother) 208 | 209 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 210 | 211 | double pi = 3.14159265358979323846264338327950288; 212 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 213 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 214 | double c2 = b1; 215 | double c3 = -a1 * a1; 216 | double c1 = 1.0 - c2 - c3; 217 | if (i < 3) { 218 | return (c1 * inputArr[i]); 219 | } else { 220 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 221 | } 222 | } 223 | 224 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 225 | 226 | int avLength = averageLength; 227 | if(averageLength == 0){ 228 | avLength = barsLag; 229 | } 230 | 231 | if (i > (barsLag + 1 + avLength)) { 232 | 233 | for (int lag = 0; lag <= barsLag; lag++) { 234 | 235 | double m = (double)averageLength; 236 | if (averageLength == 0) { 237 | m = (double)lag; 238 | } 239 | 240 | double Sx = 0; 241 | double Sy = 0; 242 | double Sxx = 0; 243 | double Syy = 0; 244 | double Sxy = 0; 245 | 246 | for (int count = 0; count < m; count++) { 247 | double X = inputArr[i - count]; 248 | double Y = inputArr[i - lag - count]; 249 | Sx = Sx + X; 250 | Sy = Sy + Y; 251 | Sxx = Sxx + X * X; 252 | Sxy = Sxy + X * Y; 253 | Syy = Syy + Y * Y; 254 | } 255 | 256 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 257 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 258 | } 259 | 260 | } 261 | } 262 | } 263 | 264 | // Discrete Fourier Transform (DFT) 265 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 266 | 267 | double pi = 3.14159265358979323846264338327950288; 268 | double cosPart[]; 269 | double sinPart[]; 270 | ArrayResize(cosPart, highPassPeriod + 1); 271 | ArrayResize(sinPart, highPassPeriod + 1); 272 | 273 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 274 | 275 | cosPart[period] = 0.0; 276 | sinPart[period] = 0.0; 277 | 278 | // find cosine and sine correlated components 279 | for (int n = 3; n <= highPassPeriod; n++) { 280 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 281 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 282 | } 283 | 284 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 285 | } 286 | } 287 | 288 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 289 | 290 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 291 | outputArr[period, 1] = outputArr[period, 0]; 292 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 293 | } 294 | 295 | } 296 | 297 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 298 | 299 | maxPower = 0.991 * maxPower; 300 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 301 | if (inputArr[period, 0] > maxPower) { 302 | maxPower = inputArr[period, 0]; 303 | } 304 | } 305 | if (maxPower != 0) { 306 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 307 | outputArr[period1] = inputArr[period1, 0] / maxPower; 308 | } 309 | } else { 310 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 311 | outputArr[period1] = 0; 312 | } 313 | } 314 | } 315 | 316 | // dominant cycle using the CG of the spectrum 317 | 318 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 319 | 320 | double Spx = 0; 321 | double Sp = 0; 322 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 323 | if (inputArr[period] >= 0.5) { 324 | Spx = Spx + period * inputArr[period]; 325 | Sp = Sp + inputArr[period]; 326 | } 327 | } 328 | 329 | double domCycle = 0; 330 | if (Sp != 0) { 331 | domCycle = Spx / Sp; 332 | } 333 | if (domCycle < lowPassPeriod) { 334 | domCycle = (double)lowPassPeriod; 335 | } 336 | 337 | return domCycle; 338 | } 339 | 340 | // this method gets allows the selection of which price to use 341 | // it also includes Heiken Ashi price outputs 342 | 343 | double workHa[][4]; 344 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 345 | 346 | if (priceType >= pr_ha_close) { 347 | 348 | if (ArrayRange(workHa, 0) != bars) { 349 | ArrayResize(workHa, bars); 350 | } 351 | 352 | double haOpen; 353 | if (i > 0) { 354 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 355 | } else { 356 | haOpen = (open[i] + close[i]) / 2; 357 | } 358 | 359 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 360 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 361 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 362 | 363 | workHa[i][0] = haLow; 364 | workHa[i][1] = haHigh; 365 | workHa[i][2] = haOpen; 366 | workHa[i][3] = haClose; 367 | 368 | switch (priceType) { 369 | case pr_ha_close: return (haClose); 370 | case pr_ha_open: return (haOpen); 371 | case pr_ha_high: return (haHigh); 372 | case pr_ha_low: return (haLow); 373 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 374 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 375 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 376 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 377 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 378 | case pr_ha_trendbiased: 379 | if (haClose > haOpen) { 380 | return ((haHigh + haClose) / 2.0); 381 | } else { 382 | return ((haLow + haClose) / 2.0); 383 | } 384 | } 385 | 386 | } else { 387 | 388 | switch (priceType) { 389 | case pr_close: return (close[i]); 390 | case pr_open: return (open[i]); 391 | case pr_high: return (high[i]); 392 | case pr_low: return (low[i]); 393 | case pr_median: return ((high[i] + low[i]) / 2.0); 394 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 395 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 396 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 397 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 398 | case pr_trendbiased: 399 | if (close[i] > open[i]) { 400 | return ((high[i] + close[i]) / 2.0); 401 | } else { 402 | return ((low[i] + close[i]) / 2.0); 403 | } 404 | } 405 | } 406 | return (0); 407 | } 408 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_cci.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_cci.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 5 17 | #property indicator_plots 1 18 | #property indicator_maximum 200 19 | #property indicator_minimum -200 20 | #property indicator_level1 -100 21 | #property indicator_level2 100 22 | 23 | //Plot Line 24 | #property indicator_label1 "Adaptive CCI" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrDeepSkyBlue 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | // set enumerated values for prices 31 | enum enPrices { 32 | pr_close, // Close 33 | pr_open, // Open 34 | pr_high, // High 35 | pr_low, // Low 36 | pr_median, // Median 37 | pr_typical, // Typical 38 | pr_weighted, // Weighted 39 | pr_average, // Average (high+low+open+close)/4 40 | pr_medianbody, // Average median body (open+close)/2 41 | pr_trendbiased, // Trend biased price 42 | pr_ha_close, // Heiken ashi close 43 | pr_ha_open, // Heiken ashi open 44 | pr_ha_high, // Heiken ashi high 45 | pr_ha_low, // Heiken ashi low 46 | pr_ha_median, // Heiken ashi median 47 | pr_ha_typical, // Heiken ashi typical 48 | pr_ha_weighted, // Heiken ashi weighted 49 | pr_ha_average, // Heiken ashi average 50 | pr_ha_medianbody, // Heiken ashi median body 51 | pr_ha_trendbiased // Heiken ashi trend biased price 52 | }; 53 | 54 | // inputs 55 | input enPrices Price = pr_close; // Price Type 56 | input int avgLength = 3; // Averaging Length 57 | input int hpPeriod = 48; // High Pass Period 58 | input int lpPeriod = 10; // Low Pass Period 59 | 60 | // buffers 61 | double result[]; // result buffer 62 | double filt[]; // filter buffer 63 | double highPass[]; // highpass buffer 64 | double ratio[]; // ratio buffer 65 | double avePrices[]; // average prices buffer 66 | 67 | ////////////////////////////////////////////////////////////////// 68 | 69 | int OnInit() { 70 | SetIndexBuffer(0, result, INDICATOR_DATA); 71 | SetIndexBuffer(1, filt, INDICATOR_CALCULATIONS); 72 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 73 | SetIndexBuffer(3, ratio, INDICATOR_CALCULATIONS); 74 | SetIndexBuffer(4, avePrices, INDICATOR_CALCULATIONS); 75 | return (0); 76 | } 77 | 78 | ////////////////////////////////////////////////////////////////// 79 | 80 | double work[]; 81 | double corr[]; 82 | double sqSum[]; 83 | double r[][2]; 84 | double maxPwr = 0.0; 85 | double pwr[]; 86 | double closesUpOld = 0.0; 87 | 88 | int OnCalculate( 89 | const int rates_total, 90 | const int prev_calculated, 91 | const datetime& time[], 92 | const double& open[], 93 | const double& high[], 94 | const double& low[], 95 | const double& close[], 96 | const long& tick_volume[], 97 | const long& volume[], 98 | const int& spread[]) { 99 | 100 | if (Bars(_Symbol, _Period) < rates_total) { 101 | return (-1); 102 | } 103 | 104 | // resize the working array to fit the added data on each new bar 105 | if (ArrayRange(work, 0) != rates_total) { 106 | ArrayResize(work, rates_total); 107 | } 108 | 109 | // if the default array size if not chosen then adjust the array sizes 110 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 111 | ArrayResize(corr, (hpPeriod + 1)); 112 | ArrayResize(sqSum, (hpPeriod + 1)); 113 | ArrayResize(r, (hpPeriod + 1)); 114 | ArrayResize(pwr, (hpPeriod + 1)); 115 | } 116 | 117 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 118 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 119 | to reset to 0, and hence cause a loop through all previous bars */ 120 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 121 | 122 | // get the price of the current bar based on the Price variable input 123 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 124 | 125 | // highpass filter 126 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 127 | 128 | // lowpass filter (supersmoother) 129 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 130 | 131 | // pearson correlation on lagged values 132 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 133 | 134 | // Discrete Fourier Transform 135 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 136 | 137 | // EMA to smooth power measurement at each period 138 | processEMA(lpPeriod, hpPeriod, sqSum, r); 139 | 140 | // fast attack slow decay Auto Gain Control (AGC) 141 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 142 | 143 | // dominant cycle using the CG of the spectrum 144 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 145 | 146 | // compute the CCI (Result) 147 | computeCCI(lpPeriod, hpPeriod, filt, (int)dominantCycle, ratio, result, i, avePrices); 148 | } 149 | 150 | // return value of prev_calculated for next call 151 | return (rates_total); 152 | } 153 | 154 | void computeCCI(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& ratioArr[], double& finalResult[], int i, double& avePriceArr[]){ 155 | 156 | if (i > (highPassPeriod + 1)){ 157 | 158 | double pi = 3.14159265358979323846264338327950288; 159 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / lowPassPeriod); 160 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / lowPassPeriod); 161 | double c2 = b1; 162 | double c3 = -a1 * a1; 163 | double c1 = 1.0 - c2 - c3; 164 | double avePrice = 0; 165 | 166 | for(int count = 0; count < domCycle; count++){ 167 | avePrice = avePrice + inputArr[i - count]; 168 | } 169 | 170 | avePrice = avePrice / (double)domCycle; 171 | avePriceArr[i] = avePrice; 172 | 173 | double rms = 0; 174 | 175 | for(int count1 = 0; count1 < domCycle; count1++){ 176 | rms = rms + pow((inputArr[i - count1] - avePriceArr[i - count1]), 2); 177 | } 178 | 179 | rms = MathSqrt(rms / (double)domCycle); 180 | double num = inputArr[i] - avePrice; 181 | double denom = 0.015 * rms; 182 | ratioArr[i] = num / denom; 183 | 184 | finalResult[i] = c1 * (ratioArr[i] + ratioArr[i-1]) / 2 + c2 * finalResult[i - 1] + c3 * finalResult[i - 2]; 185 | 186 | } else { 187 | 188 | ratioArr[i] = 0; 189 | finalResult[i] = 0; 190 | 191 | } 192 | } 193 | 194 | // pass input data through a high pass filter 195 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 196 | 197 | double pi = 3.14159265358979323846264338327950288; 198 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 199 | if (i < 3) { 200 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 201 | } else { 202 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 203 | } 204 | } 205 | 206 | // pass input data through a low pass filter (supersmoother) 207 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 208 | 209 | double pi = 3.14159265358979323846264338327950288; 210 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 211 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 212 | double c2 = b1; 213 | double c3 = -a1 * a1; 214 | double c1 = 1.0 - c2 - c3; 215 | if (i < 3) { 216 | return (c1 * inputArr[i]); 217 | } else { 218 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 219 | } 220 | } 221 | 222 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 223 | 224 | int avLength = averageLength; 225 | if(averageLength == 0){ 226 | avLength = barsLag; 227 | } 228 | 229 | if (i > (barsLag + 1 + avLength)) { 230 | 231 | for (int lag = 0; lag <= barsLag; lag++) { 232 | 233 | double m = (double)averageLength; 234 | if (averageLength == 0) { 235 | m = (double)lag; 236 | } 237 | 238 | double Sx = 0; 239 | double Sy = 0; 240 | double Sxx = 0; 241 | double Syy = 0; 242 | double Sxy = 0; 243 | 244 | for (int count = 0; count < m; count++) { 245 | double X = inputArr[i - count]; 246 | double Y = inputArr[i - lag - count]; 247 | Sx = Sx + X; 248 | Sy = Sy + Y; 249 | Sxx = Sxx + X * X; 250 | Sxy = Sxy + X * Y; 251 | Syy = Syy + Y * Y; 252 | } 253 | 254 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 255 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 256 | } 257 | 258 | } 259 | } 260 | } 261 | 262 | // Discrete Fourier Transform (DFT) 263 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 264 | 265 | double pi = 3.14159265358979323846264338327950288; 266 | double cosPart[]; 267 | double sinPart[]; 268 | ArrayResize(cosPart, highPassPeriod + 1); 269 | ArrayResize(sinPart, highPassPeriod + 1); 270 | 271 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 272 | 273 | cosPart[period] = 0.0; 274 | sinPart[period] = 0.0; 275 | 276 | // Find Cosine and Sine correlated components 277 | for (int n = 3; n <= highPassPeriod; n++) { 278 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 279 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 280 | } 281 | 282 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 283 | } 284 | } 285 | 286 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 287 | 288 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 289 | outputArr[period, 1] = outputArr[period, 0]; 290 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 291 | } 292 | 293 | } 294 | 295 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 296 | 297 | maxPower = 0.991 * maxPower; 298 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 299 | if (inputArr[period, 0] > maxPower) { 300 | maxPower = inputArr[period, 0]; 301 | } 302 | } 303 | if (maxPower != 0) { 304 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 305 | outputArr[period1] = inputArr[period1, 0] / maxPower; 306 | } 307 | } else { 308 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 309 | outputArr[period1] = 0; 310 | } 311 | } 312 | } 313 | 314 | // dominant cycle using the CG of the spectrum 315 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 316 | 317 | double Spx = 0; 318 | double Sp = 0; 319 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 320 | if (inputArr[period] >= 0.5) { 321 | Spx = Spx + period * inputArr[period]; 322 | Sp = Sp + inputArr[period]; 323 | } 324 | } 325 | 326 | double domCycle = 0; 327 | if (Sp != 0) { 328 | domCycle = Spx / Sp; 329 | } 330 | if (domCycle < lowPassPeriod) { 331 | domCycle = (double)lowPassPeriod; 332 | } 333 | if (domCycle > highPassPeriod) { 334 | domCycle = (double)highPassPeriod; 335 | } 336 | return domCycle; 337 | } 338 | 339 | // this method gets allows the selection of which price to use 340 | // it also includes Heiken Ashi price outputs 341 | double workHa[][4]; 342 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 343 | 344 | if (priceType >= pr_ha_close) { 345 | 346 | if (ArrayRange(workHa, 0) != bars) { 347 | ArrayResize(workHa, bars); 348 | } 349 | 350 | double haOpen; 351 | if (i > 0) { 352 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 353 | } else { 354 | haOpen = (open[i] + close[i]) / 2; 355 | } 356 | 357 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 358 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 359 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 360 | 361 | workHa[i][0] = haLow; 362 | workHa[i][1] = haHigh; 363 | workHa[i][2] = haOpen; 364 | workHa[i][3] = haClose; 365 | 366 | switch (priceType) { 367 | case pr_ha_close: return (haClose); 368 | case pr_ha_open: return (haOpen); 369 | case pr_ha_high: return (haHigh); 370 | case pr_ha_low: return (haLow); 371 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 372 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 373 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 374 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 375 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 376 | case pr_ha_trendbiased: 377 | if (haClose > haOpen) { 378 | return ((haHigh + haClose) / 2.0); 379 | } else { 380 | return ((haLow + haClose) / 2.0); 381 | } 382 | } 383 | 384 | } else { 385 | 386 | switch (priceType) { 387 | case pr_close: return (close[i]); 388 | case pr_open: return (open[i]); 389 | case pr_high: return (high[i]); 390 | case pr_low: return (low[i]); 391 | case pr_median: return ((high[i] + low[i]) / 2.0); 392 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 393 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 394 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 395 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 396 | case pr_trendbiased: 397 | if (close[i] > open[i]) { 398 | return ((high[i] + close[i]) / 2.0); 399 | } else { 400 | return ((low[i] + close[i]) / 2.0); 401 | } 402 | } 403 | } 404 | return (0); 405 | } 406 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_rsi.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_rsi.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 4 17 | #property indicator_plots 1 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -0.2 20 | #property indicator_level1 0.3 21 | #property indicator_level2 0.7 22 | 23 | //Plot Line 24 | #property indicator_label1 "Adaptive RSI" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrFireBrick 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | // set enumerated values for prices 31 | enum enPrices { 32 | pr_close, // Close 33 | pr_open, // Open 34 | pr_high, // High 35 | pr_low, // Low 36 | pr_median, // Median 37 | pr_typical, // Typical 38 | pr_weighted, // Weighted 39 | pr_average, // Average (high+low+open+close)/4 40 | pr_medianbody, // Average median body (open+close)/2 41 | pr_trendbiased, // Trend biased price 42 | pr_ha_close, // Heiken ashi close 43 | pr_ha_open, // Heiken ashi open 44 | pr_ha_high, // Heiken ashi high 45 | pr_ha_low, // Heiken ashi low 46 | pr_ha_median, // Heiken ashi median 47 | pr_ha_typical, // Heiken ashi typical 48 | pr_ha_weighted, // Heiken ashi weighted 49 | pr_ha_average, // Heiken ashi average 50 | pr_ha_medianbody, // Heiken ashi median body 51 | pr_ha_trendbiased // Heiken ashi trend biased price 52 | }; 53 | 54 | // inputs 55 | input enPrices Price = pr_close; // Price Type 56 | input int avgLength = 3; // Averaging Length 57 | input int hpPeriod = 48; // High Pass Period 58 | input int lpPeriod = 10; // Low Pass Period 59 | 60 | // buffers 61 | double result[]; // result buffer 62 | double filt[]; // filter buffer 63 | double highPass[]; // highpass buffer 64 | double denom[]; // denominator buffer 65 | 66 | ////////////////////////////////////////////////////////////////// 67 | 68 | int OnInit() { 69 | SetIndexBuffer(0, result, INDICATOR_DATA); 70 | SetIndexBuffer(1, filt, INDICATOR_CALCULATIONS); 71 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 72 | SetIndexBuffer(3, denom, INDICATOR_CALCULATIONS); 73 | return (0); 74 | } 75 | 76 | ////////////////////////////////////////////////////////////////// 77 | 78 | double work[]; 79 | double corr[]; 80 | double sqSum[]; 81 | double r[][2]; 82 | double maxPwr = 0.0; 83 | double pwr[]; 84 | double closesUpOld = 0.0; 85 | 86 | int OnCalculate( 87 | const int rates_total, 88 | const int prev_calculated, 89 | const datetime& time[], 90 | const double& open[], 91 | const double& high[], 92 | const double& low[], 93 | const double& close[], 94 | const long& tick_volume[], 95 | const long& volume[], 96 | const int& spread[]) { 97 | 98 | if (Bars(_Symbol, _Period) < rates_total) { 99 | return (-1); 100 | } 101 | 102 | // resize the working array to fit the added data on each new bar 103 | if (ArrayRange(work, 0) != rates_total) { 104 | ArrayResize(work, rates_total); 105 | } 106 | // if the default array size if not chosen then adjust the array sizes 107 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 108 | ArrayResize(corr, (hpPeriod + 1)); 109 | ArrayResize(sqSum, (hpPeriod + 1)); 110 | ArrayResize(r, (hpPeriod + 1)); 111 | ArrayResize(pwr, (hpPeriod + 1)); 112 | } 113 | 114 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 115 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 116 | to reset to 0, and hence cause a loop through all previous bars */ 117 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 118 | 119 | // get the price of the current bar based on the Price variable input 120 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 121 | 122 | // highpass filter 123 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 124 | 125 | // lowpass filter (supersmoother) 126 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 127 | 128 | // pearson correlation on lagged values 129 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 130 | 131 | // Discrete Fourier Transform 132 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 133 | 134 | // EMA to smooth power measurement at each period 135 | processEMA(lpPeriod, hpPeriod, sqSum, r); 136 | 137 | // fast attack slow decay Auto Gain Control (AGC) 138 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 139 | 140 | // dominant cycle using the CG of the spectrum 141 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 142 | 143 | // compute the RSI (Result) 144 | computeRSI(lpPeriod, hpPeriod, filt, (int)dominantCycle, denom, result, i, closesUpOld); 145 | } 146 | 147 | // return value of prev_calculated for next call 148 | return (rates_total); 149 | } 150 | 151 | void computeRSI(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& denominator[], double& finalResult[], int i, double& closeUp1){ 152 | 153 | if (i > (highPassPeriod + 1)){ 154 | 155 | double pi = 3.14159265358979323846264338327950288; 156 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / lowPassPeriod); 157 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / lowPassPeriod); 158 | double c2 = b1; 159 | double c3 = -a1 * a1; 160 | double c1 = 1.0 - c2 - c3; 161 | double closeUp = 0; 162 | double closeDown = 0; 163 | int rsiDomCycle = (int)(domCycle / 2 - 1); 164 | 165 | for(int count = 0; count <= rsiDomCycle; count++){ 166 | if(inputArr[i - count] > inputArr[i - count - 1]){ 167 | closeUp = closeUp + (inputArr[i - count] - inputArr[i - count - 1]); 168 | } 169 | if(inputArr[i - count] < inputArr[i - count - 1]){ 170 | closeDown = closeDown + (inputArr[i - count - 1] - inputArr[i - count]); 171 | } 172 | } 173 | 174 | denom[i] = closeUp + closeDown; 175 | 176 | 177 | if(denominator[i] != 0 && denominator[i-1] != 0){ 178 | finalResult[i] = c1 * (closeUp / denominator[i] + closeUp1 / denominator[i-1]) / 2 + c2 * finalResult[i - 1] + c3 * finalResult[i - 2]; 179 | } else { 180 | finalResult[i] = 0; 181 | } 182 | 183 | closeUp1 = closeUp; 184 | 185 | } else { 186 | 187 | denominator[i] = 0; 188 | finalResult[i] = 0; 189 | 190 | } 191 | } 192 | 193 | // high pass filter 194 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 195 | 196 | double pi = 3.14159265358979323846264338327950288; 197 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 198 | if (i < 3) { 199 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 200 | } else { 201 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 202 | } 203 | } 204 | 205 | // low pass filter (supersmoother) 206 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 207 | 208 | double pi = 3.14159265358979323846264338327950288; 209 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 210 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 211 | double c2 = b1; 212 | double c3 = -a1 * a1; 213 | double c1 = 1.0 - c2 - c3; 214 | if (i < 3) { 215 | return (c1 * inputArr[i]); 216 | } else { 217 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 218 | } 219 | } 220 | 221 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 222 | 223 | int avLength = averageLength; 224 | if(averageLength == 0){ 225 | avLength = barsLag; 226 | } 227 | 228 | if (i > (barsLag + 1 + avLength)) { 229 | 230 | for (int lag = 0; lag <= barsLag; lag++) { 231 | 232 | double m = (double)averageLength; 233 | if (averageLength == 0) { 234 | m = (double)lag; 235 | } 236 | 237 | double Sx = 0; 238 | double Sy = 0; 239 | double Sxx = 0; 240 | double Syy = 0; 241 | double Sxy = 0; 242 | 243 | for (int count = 0; count < m; count++) { 244 | double X = inputArr[i - count]; 245 | double Y = inputArr[i - lag - count]; 246 | Sx = Sx + X; 247 | Sy = Sy + Y; 248 | Sxx = Sxx + X * X; 249 | Sxy = Sxy + X * Y; 250 | Syy = Syy + Y * Y; 251 | } 252 | 253 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 254 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 255 | } 256 | 257 | } 258 | } 259 | } 260 | 261 | // Discrete Fourier Transform (DFT) 262 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 263 | 264 | double pi = 3.14159265358979323846264338327950288; 265 | double cosPart[]; 266 | double sinPart[]; 267 | ArrayResize(cosPart, highPassPeriod + 1); 268 | ArrayResize(sinPart, highPassPeriod + 1); 269 | 270 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 271 | 272 | cosPart[period] = 0.0; 273 | sinPart[period] = 0.0; 274 | 275 | // Find Cosine and Sine correlated components 276 | for (int n = 3; n <= highPassPeriod; n++) { 277 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 278 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 279 | } 280 | 281 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 282 | } 283 | } 284 | 285 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 286 | 287 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 288 | outputArr[period, 1] = outputArr[period, 0]; 289 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 290 | } 291 | 292 | } 293 | 294 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 295 | 296 | maxPower = 0.991 * maxPower; 297 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 298 | if (inputArr[period, 0] > maxPower) { 299 | maxPower = inputArr[period, 0]; 300 | } 301 | } 302 | if (maxPower != 0) { 303 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 304 | outputArr[period1] = inputArr[period1, 0] / maxPower; 305 | } 306 | } else { 307 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 308 | outputArr[period1] = 0; 309 | } 310 | } 311 | } 312 | 313 | // dominant cycle using the CG of the spectrum 314 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 315 | 316 | double Spx = 0; 317 | double Sp = 0; 318 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 319 | if (inputArr[period] >= 0.5) { 320 | Spx = Spx + period * inputArr[period]; 321 | Sp = Sp + inputArr[period]; 322 | } 323 | } 324 | 325 | double domCycle = 0; 326 | if (Sp != 0) { 327 | domCycle = Spx / Sp; 328 | } 329 | if (domCycle < lowPassPeriod) { 330 | domCycle = (double)lowPassPeriod; 331 | } 332 | if (domCycle > highPassPeriod) { 333 | domCycle = (double)highPassPeriod; 334 | } 335 | return domCycle; 336 | } 337 | 338 | // this method gets allows the selection of which price to use 339 | // it also includes Heiken Ashi price outputs 340 | double workHa[][4]; 341 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 342 | 343 | if (priceType >= pr_ha_close) { 344 | 345 | if (ArrayRange(workHa, 0) != bars) { 346 | ArrayResize(workHa, bars); 347 | } 348 | 349 | double haOpen; 350 | if (i > 0) { 351 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 352 | } else { 353 | haOpen = (open[i] + close[i]) / 2; 354 | } 355 | 356 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 357 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 358 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 359 | 360 | workHa[i][0] = haLow; 361 | workHa[i][1] = haHigh; 362 | workHa[i][2] = haOpen; 363 | workHa[i][3] = haClose; 364 | 365 | switch (priceType) { 366 | case pr_ha_close: return (haClose); 367 | case pr_ha_open: return (haOpen); 368 | case pr_ha_high: return (haHigh); 369 | case pr_ha_low: return (haLow); 370 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 371 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 372 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 373 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 374 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 375 | case pr_ha_trendbiased: 376 | if (haClose > haOpen) { 377 | return ((haHigh + haClose) / 2.0); 378 | } else { 379 | return ((haLow + haClose) / 2.0); 380 | } 381 | } 382 | 383 | } else { 384 | 385 | switch (priceType) { 386 | case pr_close: return (close[i]); 387 | case pr_open: return (open[i]); 388 | case pr_high: return (high[i]); 389 | case pr_low: return (low[i]); 390 | case pr_median: return ((high[i] + low[i]) / 2.0); 391 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 392 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 393 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 394 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 395 | case pr_trendbiased: 396 | if (close[i] > open[i]) { 397 | return ((high[i] + close[i]) / 2.0); 398 | } else { 399 | return ((low[i] + close[i]) / 2.0); 400 | } 401 | } 402 | } 403 | return (0); 404 | } 405 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_stochastic_invfischer.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_stochastic_invfischer.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 6 17 | #property indicator_plots 2 18 | #property indicator_maximum 1.2 19 | #property indicator_minimum -1.2 20 | #property indicator_level1 0 21 | 22 | //Plot Line 23 | #property indicator_label1 "Adaptive Stochastic Inv Fischer" 24 | #property indicator_type1 DRAW_LINE 25 | #property indicator_color1 clrPurple 26 | #property indicator_style1 STYLE_SOLID 27 | #property indicator_width1 2 28 | 29 | //Plot Signal Line 30 | #property indicator_label2 "Signal" 31 | #property indicator_type2 DRAW_LINE 32 | #property indicator_color2 clrDodgerBlue 33 | #property indicator_style2 STYLE_DASH 34 | #property indicator_width2 2 35 | 36 | // set enumerated values for prices 37 | enum enPrices { 38 | pr_close, // Close 39 | pr_open, // Open 40 | pr_high, // High 41 | pr_low, // Low 42 | pr_median, // Median 43 | pr_typical, // Typical 44 | pr_weighted, // Weighted 45 | pr_average, // Average (high+low+open+close)/4 46 | pr_medianbody, // Average median body (open+close)/2 47 | pr_trendbiased, // Trend biased price 48 | pr_ha_close, // Heiken ashi close 49 | pr_ha_open, // Heiken ashi open 50 | pr_ha_high, // Heiken ashi high 51 | pr_ha_low, // Heiken ashi low 52 | pr_ha_median, // Heiken ashi median 53 | pr_ha_typical, // Heiken ashi typical 54 | pr_ha_weighted, // Heiken ashi weighted 55 | pr_ha_average, // Heiken ashi average 56 | pr_ha_medianbody, // Heiken ashi median body 57 | pr_ha_trendbiased // Heiken ashi trend biased price 58 | }; 59 | 60 | // inputs 61 | input enPrices Price = pr_close; // Price Type 62 | input int avgLength = 3; // Averaging Length 63 | input int hpPeriod = 48; // High Pass Period 64 | input int lpPeriod = 10; // Low Pass Period 65 | 66 | // buffers 67 | double result[]; // result buffer 68 | double signalLine[];// signal line buffer 69 | double filt[]; // filter buffer 70 | double highPass[]; // highpass buffer 71 | double stoch[]; // stochastic buffer 72 | double adapStoch[]; // adaptive stochastic result buffer 73 | 74 | ////////////////////////////////////////////////////////////////// 75 | 76 | int OnInit() { 77 | SetIndexBuffer(0, result, INDICATOR_DATA); 78 | SetIndexBuffer(1, signalLine, INDICATOR_DATA); 79 | SetIndexBuffer(2, filt, INDICATOR_CALCULATIONS); 80 | SetIndexBuffer(3, highPass, INDICATOR_CALCULATIONS); 81 | SetIndexBuffer(4, stoch, INDICATOR_CALCULATIONS); 82 | SetIndexBuffer(5, adapStoch, INDICATOR_CALCULATIONS); 83 | 84 | return (0); 85 | } 86 | 87 | ////////////////////////////////////////////////////////////////// 88 | 89 | double work[]; 90 | double corr[]; 91 | double sqSum[]; 92 | double r[][2]; 93 | double maxPwr = 0.0; 94 | double pwr[]; 95 | 96 | int OnCalculate( 97 | const int rates_total, 98 | const int prev_calculated, 99 | const datetime& time[], 100 | const double& open[], 101 | const double& high[], 102 | const double& low[], 103 | const double& close[], 104 | const long& tick_volume[], 105 | const long& volume[], 106 | const int& spread[]) { 107 | 108 | if (Bars(_Symbol, _Period) < rates_total) { 109 | return (-1); 110 | } 111 | 112 | // resize the working array to fit the added data on each new bar 113 | if (ArrayRange(work, 0) != rates_total) { 114 | ArrayResize(work, rates_total); 115 | } 116 | // if the default array size if not chosen then adjust the array sizes 117 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 118 | ArrayResize(corr, (hpPeriod + 1)); 119 | ArrayResize(sqSum, (hpPeriod + 1)); 120 | ArrayResize(r, (hpPeriod + 1)); 121 | ArrayResize(pwr, (hpPeriod + 1)); 122 | } 123 | 124 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 125 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 126 | to reset to 0, and hence cause a loop through all previous bars */ 127 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 128 | 129 | // get the price of the current bar based on the Price variable input 130 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 131 | 132 | // highpass filter 133 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 134 | 135 | // lowpass filter (supersmoother) 136 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 137 | 138 | // pearson correlation on lagged values 139 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 140 | 141 | // Discrete Fourier Transform 142 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 143 | 144 | // EMA to smooth power measurement at each period 145 | processEMA(lpPeriod, hpPeriod, sqSum, r); 146 | 147 | // fast attack slow decay Auto Gain Control (AGC) 148 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 149 | 150 | // dominant cycle using the CG of the spectrum 151 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 152 | 153 | // stochastic (Result) 154 | computeStochastic(lpPeriod, hpPeriod, filt, (int)dominantCycle, stoch, result, i, adapStoch, signalLine); 155 | } 156 | 157 | // return value of prev_calculated for next call 158 | return (rates_total); 159 | } 160 | 161 | // high pass filter 162 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 163 | 164 | double pi = 3.14159265358979323846264338327950288; 165 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 166 | if (i < 3) { 167 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 168 | } else { 169 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 170 | } 171 | } 172 | 173 | // low pass filter (supersmoother) 174 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 175 | 176 | double pi = 3.14159265358979323846264338327950288; 177 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 178 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 179 | double c2 = b1; 180 | double c3 = -a1 * a1; 181 | double c1 = 1.0 - c2 - c3; 182 | if (i < 3) { 183 | return (c1 * inputArr[i]); 184 | } else { 185 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 186 | } 187 | } 188 | 189 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 190 | 191 | int avLength = averageLength; 192 | if(averageLength == 0){ 193 | avLength = barsLag; 194 | } 195 | 196 | if (i > (barsLag + 1 + avLength)) { 197 | 198 | for (int lag = 0; lag <= barsLag; lag++) { 199 | 200 | double m = (double)averageLength; 201 | if (averageLength == 0) { 202 | m = (double)lag; 203 | } 204 | 205 | double Sx = 0; 206 | double Sy = 0; 207 | double Sxx = 0; 208 | double Syy = 0; 209 | double Sxy = 0; 210 | 211 | for (int count = 0; count < m; count++) { 212 | double X = inputArr[i - count]; 213 | double Y = inputArr[i - lag - count]; 214 | Sx = Sx + X; 215 | Sy = Sy + Y; 216 | Sxx = Sxx + X * X; 217 | Sxy = Sxy + X * Y; 218 | Syy = Syy + Y * Y; 219 | } 220 | 221 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 222 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 223 | } 224 | 225 | } 226 | } 227 | } 228 | 229 | // Discrete Fourier Transform (DFT) 230 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 231 | 232 | double pi = 3.14159265358979323846264338327950288; 233 | double cosPart[]; 234 | double sinPart[]; 235 | ArrayResize(cosPart, highPassPeriod + 1); 236 | ArrayResize(sinPart, highPassPeriod + 1); 237 | 238 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 239 | 240 | cosPart[period] = 0.0; 241 | sinPart[period] = 0.0; 242 | 243 | // Find Cosine and Sine correlated components 244 | for (int n = 3; n <= highPassPeriod; n++) { 245 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 246 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 247 | } 248 | 249 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 250 | } 251 | } 252 | 253 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 254 | 255 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 256 | outputArr[period, 1] = outputArr[period, 0]; 257 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 258 | } 259 | 260 | } 261 | 262 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 263 | 264 | maxPower = 0.991 * maxPower; 265 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 266 | if (inputArr[period, 0] > maxPower) { 267 | maxPower = inputArr[period, 0]; 268 | } 269 | } 270 | if (maxPower != 0) { 271 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 272 | outputArr[period1] = inputArr[period1, 0] / maxPower; 273 | } 274 | } else { 275 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 276 | outputArr[period1] = 0; 277 | } 278 | } 279 | } 280 | 281 | // dominant cycle using the CG of the spectrum 282 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 283 | 284 | double Spx = 0; 285 | double Sp = 0; 286 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 287 | if (inputArr[period] >= 0.5) { 288 | Spx = Spx + period * inputArr[period]; 289 | Sp = Sp + inputArr[period]; 290 | } 291 | } 292 | 293 | double domCycle = 0; 294 | if (Sp != 0) { 295 | domCycle = Spx / Sp; 296 | } 297 | if (domCycle < lowPassPeriod) { 298 | domCycle = (double)lowPassPeriod; 299 | } 300 | if (domCycle > highPassPeriod) { 301 | domCycle = (double)highPassPeriod; 302 | } 303 | return domCycle; 304 | } 305 | 306 | void computeStochastic(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& stochastic[], double& finalResult[], int i, double& adapStochastic[], double& signal[]){ 307 | 308 | if (i > (highPassPeriod + 1)){ 309 | 310 | double pi = 3.14159265358979323846264338327950288; 311 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / lowPassPeriod); 312 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / lowPassPeriod); 313 | double c2 = b1; 314 | double c3 = -a1 * a1; 315 | double c1 = 1.0 - c2 - c3; 316 | 317 | double highestC = inputArr[i]; 318 | double lowestC = inputArr[i]; 319 | 320 | for (int cycle = 0; cycle < domCycle; cycle++) { 321 | if (inputArr[i - cycle] > highestC) { 322 | highestC = inputArr[i - cycle]; 323 | } 324 | if (inputArr[i - cycle] < lowestC) { 325 | lowestC = inputArr[i - cycle]; 326 | } 327 | } 328 | 329 | stochastic[i] = (inputArr[i] - lowestC) / (highestC - lowestC); 330 | adapStochastic[i] = c1 * (stochastic[i] + stochastic[i - 1]) / 2.0 + c2 * adapStochastic[i - 1] + c3 * adapStochastic[i - 2]; 331 | double value1 = 2 * (adapStochastic[i] - 0.5); 332 | finalResult[i] = (MathExp(2*3*value1) - 1) / (MathExp(2*3*value1) + 1); 333 | signal[i] = 0.9 * finalResult[i-1]; 334 | 335 | } else { 336 | 337 | stochastic[i] = 0; 338 | finalResult[i] = 0; 339 | adapStochastic[i] = 0; 340 | signal[i] = 0; 341 | 342 | } 343 | } 344 | 345 | // this method gets allows the selection of which price to use 346 | // it also includes Heiken Ashi price outputs 347 | double workHa[][4]; 348 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 349 | 350 | if (priceType >= pr_ha_close) { 351 | 352 | if (ArrayRange(workHa, 0) != bars) { 353 | ArrayResize(workHa, bars); 354 | } 355 | 356 | double haOpen; 357 | if (i > 0) { 358 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 359 | } else { 360 | haOpen = (open[i] + close[i]) / 2; 361 | } 362 | 363 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 364 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 365 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 366 | 367 | workHa[i][0] = haLow; 368 | workHa[i][1] = haHigh; 369 | workHa[i][2] = haOpen; 370 | workHa[i][3] = haClose; 371 | 372 | switch (priceType) { 373 | case pr_ha_close: return (haClose); 374 | case pr_ha_open: return (haOpen); 375 | case pr_ha_high: return (haHigh); 376 | case pr_ha_low: return (haLow); 377 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 378 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 379 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 380 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 381 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 382 | case pr_ha_trendbiased: 383 | if (haClose > haOpen) { 384 | return ((haHigh + haClose) / 2.0); 385 | } else { 386 | return ((haLow + haClose) / 2.0); 387 | } 388 | } 389 | 390 | } else { 391 | 392 | switch (priceType) { 393 | case pr_close: return (close[i]); 394 | case pr_open: return (open[i]); 395 | case pr_high: return (high[i]); 396 | case pr_low: return (low[i]); 397 | case pr_median: return ((high[i] + low[i]) / 2.0); 398 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 399 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 400 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 401 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 402 | case pr_trendbiased: 403 | if (close[i] > open[i]) { 404 | return ((high[i] + close[i]) / 2.0); 405 | } else { 406 | return ((low[i] + close[i]) / 2.0); 407 | } 408 | } 409 | } 410 | return (0); 411 | } 412 | -------------------------------------------------------------------------------- /mq5/ehlers_adaptive_rsi_fischer.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ehlers_adaptive_rsi_fischer.mq5 | 3 | //| Copyright © 2013 by John F. Ehlers. All rights reserved. | 4 | //| Converted to MQL5 by thetestspecimen | 5 | //| https://www.thetestspecimen.com | 6 | //| https://github.com/thetestspecimen | 7 | //+------------------------------------------------------------------+ 8 | 9 | #property copyright "2013, John F. Ehlers" 10 | #property link "https://github.com/thetestspecimen" 11 | #property version "1.00" 12 | 13 | //-------------------------------------------------------------------- 14 | 15 | #property indicator_separate_window 16 | #property indicator_buffers 5 17 | #property indicator_plots 1 18 | #property indicator_maximum 5 19 | #property indicator_minimum -5 20 | #property indicator_level1 -2 21 | #property indicator_level2 2 22 | 23 | // plot Line 24 | #property indicator_label1 "Adaptive RSI Fischer" 25 | #property indicator_type1 DRAW_LINE 26 | #property indicator_color1 clrFireBrick 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_width1 2 29 | 30 | // set enumerated values for prices 31 | enum enPrices { 32 | pr_close, // Close 33 | pr_open, // Open 34 | pr_high, // High 35 | pr_low, // Low 36 | pr_median, // Median 37 | pr_typical, // Typical 38 | pr_weighted, // Weighted 39 | pr_average, // Average (high+low+open+close)/4 40 | pr_medianbody, // Average median body (open+close)/2 41 | pr_trendbiased, // Trend biased price 42 | pr_ha_close, // Heiken ashi close 43 | pr_ha_open, // Heiken ashi open 44 | pr_ha_high, // Heiken ashi high 45 | pr_ha_low, // Heiken ashi low 46 | pr_ha_median, // Heiken ashi median 47 | pr_ha_typical, // Heiken ashi typical 48 | pr_ha_weighted, // Heiken ashi weighted 49 | pr_ha_average, // Heiken ashi average 50 | pr_ha_medianbody, // Heiken ashi median body 51 | pr_ha_trendbiased // Heiken ashi trend biased price 52 | }; 53 | 54 | // inputs 55 | input enPrices Price = pr_close; // Price Type 56 | input int avgLength = 3; // Averaging Length 57 | input int hpPeriod = 48; // High Pass Period 58 | input int lpPeriod = 10; // Low Pass Period 59 | 60 | // buffers 61 | double result[]; // result buffer 62 | double filt[]; // filter buffer 63 | double highPass[]; // highpass buffer 64 | double denom[]; // denominator buffer 65 | double rsiResult[]; // rsi buffer 66 | 67 | ////////////////////////////////////////////////////////////////// 68 | 69 | int OnInit() { 70 | SetIndexBuffer(0, result, INDICATOR_DATA); 71 | SetIndexBuffer(1, filt, INDICATOR_CALCULATIONS); 72 | SetIndexBuffer(2, highPass, INDICATOR_CALCULATIONS); 73 | SetIndexBuffer(3, denom, INDICATOR_CALCULATIONS); 74 | SetIndexBuffer(4, rsiResult, INDICATOR_CALCULATIONS); 75 | return (0); 76 | } 77 | 78 | ////////////////////////////////////////////////////////////////// 79 | 80 | double work[]; 81 | double corr[]; 82 | double sqSum[]; 83 | double r[][2]; 84 | double maxPwr = 0.0; 85 | double pwr[]; 86 | double closesUpOld = 0.0; 87 | 88 | int OnCalculate( 89 | const int rates_total, 90 | const int prev_calculated, 91 | const datetime& time[], 92 | const double& open[], 93 | const double& high[], 94 | const double& low[], 95 | const double& close[], 96 | const long& tick_volume[], 97 | const long& volume[], 98 | const int& spread[]) { 99 | 100 | if (Bars(_Symbol, _Period) < rates_total) { 101 | return (-1); 102 | } 103 | 104 | // resize the working array to fit the added data on each new bar 105 | if (ArrayRange(work, 0) != rates_total) { 106 | ArrayResize(work, rates_total); 107 | } 108 | // if the default array size if not chosen then adjust the array sizes 109 | if(ArrayRange(corr, 0) != (hpPeriod + 1)){ 110 | ArrayResize(corr, (hpPeriod + 1)); 111 | ArrayResize(sqSum, (hpPeriod + 1)); 112 | ArrayResize(r, (hpPeriod + 1)); 113 | ArrayResize(pwr, (hpPeriod + 1)); 114 | } 115 | 116 | /* most of the time prev_calculated == rates_total == _bars in which case this loop only calculates 117 | one bar of data. However, additional history or a refresh of data will cause prev_calculated 118 | to reset to 0, and hence cause a loop through all previous bars */ 119 | for (int i = (int) MathMax(prev_calculated - 1, 0); i < rates_total; i++) { 120 | 121 | // get the price of the current bar based on the Price variable input 122 | work[i] = getPrice(Price, open, close, high, low, i, rates_total); 123 | 124 | // highpass filter 125 | highPass[i] = processHighPass(hpPeriod, work, highPass, i); 126 | 127 | // lowpass filter (supersmoother) 128 | filt[i] = processLowPass(lpPeriod, highPass, filt, i); 129 | 130 | // pearson correlation on lagged values 131 | processPearsonCorr(hpPeriod, avgLength, filt, i, corr); 132 | 133 | // Discrete Fourier Transform 134 | processDFT(lpPeriod, hpPeriod, corr, i, sqSum); 135 | 136 | // EMA to smooth power measurement at each period 137 | processEMA(lpPeriod, hpPeriod, sqSum, r); 138 | 139 | // fast attack slow decay Auto Gain Control (AGC) 140 | processAGC(lpPeriod, hpPeriod, maxPwr, r, pwr, i); 141 | 142 | // dominant cycle using the CG of the spectrum 143 | double dominantCycle = computeDominantCycle(lpPeriod, hpPeriod, pwr); 144 | 145 | // RSI (Result) 146 | computeRSI(lpPeriod, hpPeriod, filt, (int)dominantCycle, denom, result, i, closesUpOld, rsiResult); 147 | } 148 | 149 | // return value of prev_calculated for next call 150 | return (rates_total); 151 | } 152 | 153 | void computeRSI(int lowPassPeriod, int highPassPeriod, double& inputArr[], int domCycle, double& denominator[], double& finalResult[], int i, double& closeUp1, double& rsi[]){ 154 | 155 | if (i > (highPassPeriod + 1)){ 156 | 157 | double pi = 3.14159265358979323846264338327950288; 158 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / lowPassPeriod); 159 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / lowPassPeriod); 160 | double c2 = b1; 161 | double c3 = -a1 * a1; 162 | double c1 = 1.0 - c2 - c3; 163 | double closeUp = 0; 164 | double closeDown = 0; 165 | int rsiDomCycle = (int)(domCycle / 2 - 1); 166 | 167 | for(int count = 0; count <= rsiDomCycle; count++){ 168 | if(inputArr[i - count] > inputArr[i - count - 1]){ 169 | closeUp = closeUp + (inputArr[i - count] - inputArr[i - count - 1]); 170 | } 171 | if(inputArr[i - count] < inputArr[i - count - 1]){ 172 | closeDown = closeDown + (inputArr[i - count - 1] - inputArr[i - count]); 173 | } 174 | } 175 | 176 | denom[i] = closeUp + closeDown; 177 | 178 | 179 | if(denominator[i] != 0 && denominator[i-1] != 0){ 180 | rsi[i] = c1 * (closeUp / denominator[i] + closeUp1 / denominator[i-1]) / 2 + c2 * rsi[i - 1] + c3 * rsi[i - 2]; 181 | double translatedRSI = 2*(rsi[i] - 0.5); 182 | double amplifiedRSI = 1.5 * translatedRSI; 183 | if(amplifiedRSI > 0.999){ 184 | amplifiedRSI = 0.999; 185 | } 186 | if(amplifiedRSI < -0.999){ 187 | amplifiedRSI = -0.999; 188 | } 189 | finalResult[i] = 0.5*log((1 + amplifiedRSI) / (1 - amplifiedRSI)); 190 | 191 | } else { 192 | finalResult[i] = 0; 193 | rsi[i] = 0; 194 | } 195 | 196 | closeUp1 = closeUp; 197 | 198 | 199 | } else { 200 | 201 | denominator[i] = 0; 202 | rsi[i] = 0; 203 | finalResult[i] = 0; 204 | 205 | } 206 | } 207 | 208 | // high pass filter 209 | double processHighPass(double highPassPeriod, double& inputArr[], double& outputArr[], int i) { 210 | 211 | double pi = 3.14159265358979323846264338327950288; 212 | double alpha1 = (MathCos(MathSqrt(2.0) * pi / highPassPeriod) + MathSin(MathSqrt(2.0) * pi / (double)highPassPeriod) - 1.0) / MathCos(MathSqrt(2.0) * pi / (double)highPassPeriod); 213 | if (i < 3) { 214 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i])); 215 | } else { 216 | return ((1.0 - alpha1 / 2.0) * (1.0 - alpha1 / 2.0) * (inputArr[i] - 2.0 * inputArr[i - 1] + inputArr[i - 2]) + 2.0 * (1.0 - alpha1) * outputArr[i - 1] - (1.0 - alpha1) * (1.0 - alpha1) * outputArr[i - 2]); 217 | } 218 | } 219 | 220 | // low pass filter (supersmoother) 221 | double processLowPass(int lowPassPeriod, double& inputArr[], double& outputArr[], int i) { 222 | 223 | double pi = 3.14159265358979323846264338327950288; 224 | double a1 = MathExp(-1.0 * MathSqrt(2.0) * pi / (double)lowPassPeriod); 225 | double b1 = 2.0 * a1 * MathCos(MathSqrt(2.0) * pi / (double)lowPassPeriod); 226 | double c2 = b1; 227 | double c3 = -a1 * a1; 228 | double c1 = 1.0 - c2 - c3; 229 | if (i < 3) { 230 | return (c1 * inputArr[i]); 231 | } else { 232 | return (c1 * (inputArr[i] + inputArr[i - 1]) / 2.0 + c2 * outputArr[i - 1] + c3 * outputArr[i - 2]); 233 | } 234 | } 235 | 236 | void processPearsonCorr(int barsLag, int averageLength, double& inputArr[], int i, double& outputArr[]) { 237 | 238 | int avLength = averageLength; 239 | if(averageLength == 0){ 240 | avLength = barsLag; 241 | } 242 | 243 | if (i > (barsLag + 1 + avLength)) { 244 | 245 | for (int lag = 0; lag <= barsLag; lag++) { 246 | 247 | double m = (double)averageLength; 248 | if (averageLength == 0) { 249 | m = (double)lag; 250 | } 251 | 252 | double Sx = 0; 253 | double Sy = 0; 254 | double Sxx = 0; 255 | double Syy = 0; 256 | double Sxy = 0; 257 | 258 | for (int count = 0; count < m; count++) { 259 | double X = inputArr[i - count]; 260 | double Y = inputArr[i - lag - count]; 261 | Sx = Sx + X; 262 | Sy = Sy + Y; 263 | Sxx = Sxx + X * X; 264 | Sxy = Sxy + X * Y; 265 | Syy = Syy + Y * Y; 266 | } 267 | 268 | if (((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)) > 0) { 269 | outputArr[lag] = (m * Sxy - Sx * Sy) / MathSqrt((m * Sxx - Sx * Sx) * (m * Syy - Sy * Sy)); 270 | } 271 | 272 | } 273 | } 274 | } 275 | 276 | // Discrete Fourier Transform (DFT) 277 | void processDFT(int lowPassPeriod, int highPassPeriod, double& inputArr[], int i, double& outputArr[]) { 278 | 279 | double pi = 3.14159265358979323846264338327950288; 280 | double cosPart[]; 281 | double sinPart[]; 282 | ArrayResize(cosPart, highPassPeriod + 1); 283 | ArrayResize(sinPart, highPassPeriod + 1); 284 | 285 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 286 | 287 | cosPart[period] = 0.0; 288 | sinPart[period] = 0.0; 289 | 290 | // Find Cosine and Sine correlated components 291 | for (int n = 3; n <= highPassPeriod; n++) { 292 | cosPart[period] = cosPart[period] + inputArr[n] * MathCos(2 * pi * (double) n / (double) period); 293 | sinPart[period] = sinPart[period] + inputArr[n] * MathSin(2 * pi * (double) n / (double) period); 294 | } 295 | 296 | outputArr[period] = pow(cosPart[period], 2.0) + pow(sinPart[period], 2.0); 297 | } 298 | } 299 | 300 | void processEMA(int lowPassPeriod, int highPassPeriod, double& inputArr[], double& outputArr[][2]) { 301 | 302 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 303 | outputArr[period, 1] = outputArr[period, 0]; 304 | outputArr[period, 0] = 0.2 * pow(inputArr[period], 2.0) + 0.8 * outputArr[period, 1]; 305 | } 306 | 307 | } 308 | 309 | void processAGC(int lowPassPeriod, int highPassPeriod, double& maxPower, double& inputArr[][2], double& outputArr[], int i) { 310 | 311 | maxPower = 0.991 * maxPower; 312 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 313 | if (inputArr[period, 0] > maxPower) { 314 | maxPower = inputArr[period, 0]; 315 | } 316 | } 317 | if (maxPower != 0) { 318 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 319 | outputArr[period1] = inputArr[period1, 0] / maxPower; 320 | } 321 | } else { 322 | for (int period1 = 3; period1 <= highPassPeriod; period1++) { 323 | outputArr[period1] = 0; 324 | } 325 | } 326 | } 327 | 328 | // dominant cycle using the CG of the spectrum 329 | 330 | double computeDominantCycle(int lowPassPeriod, int highPassPeriod, double& inputArr[]){ 331 | 332 | double Spx = 0; 333 | double Sp = 0; 334 | for (int period = lowPassPeriod; period <= highPassPeriod; period++) { 335 | if (inputArr[period] >= 0.5) { 336 | Spx = Spx + period * inputArr[period]; 337 | Sp = Sp + inputArr[period]; 338 | } 339 | } 340 | 341 | double domCycle = 0; 342 | if (Sp != 0) { 343 | domCycle = Spx / Sp; 344 | } 345 | if (domCycle < lowPassPeriod) { 346 | domCycle = (double)lowPassPeriod; 347 | } 348 | if (domCycle > highPassPeriod) { 349 | domCycle = (double)highPassPeriod; 350 | } 351 | return domCycle; 352 | } 353 | 354 | // this method gets allows the selection of which price to use 355 | // it also includes Heiken Ashi price outputs 356 | 357 | double workHa[][4]; 358 | double getPrice(int priceType, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars) { 359 | 360 | if (priceType >= pr_ha_close) { 361 | 362 | if (ArrayRange(workHa, 0) != bars) { 363 | ArrayResize(workHa, bars); 364 | } 365 | 366 | double haOpen; 367 | if (i > 0) { 368 | haOpen = (workHa[i - 1][2] + workHa[i - 1][3]) / 2.0; 369 | } else { 370 | haOpen = (open[i] + close[i]) / 2; 371 | } 372 | 373 | double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0; 374 | double haHigh = MathMax(high[i], MathMax(haOpen, haClose)); 375 | double haLow = MathMin(low[i], MathMin(haOpen, haClose)); 376 | 377 | workHa[i][0] = haLow; 378 | workHa[i][1] = haHigh; 379 | workHa[i][2] = haOpen; 380 | workHa[i][3] = haClose; 381 | 382 | switch (priceType) { 383 | case pr_ha_close: return (haClose); 384 | case pr_ha_open: return (haOpen); 385 | case pr_ha_high: return (haHigh); 386 | case pr_ha_low: return (haLow); 387 | case pr_ha_median: return ((haHigh + haLow) / 2.0); 388 | case pr_ha_medianbody: return ((haOpen + haClose) / 2.0); 389 | case pr_ha_typical: return ((haHigh + haLow + haClose) / 3.0); 390 | case pr_ha_weighted: return ((haHigh + haLow + haClose + haClose) / 4.0); 391 | case pr_ha_average: return ((haHigh + haLow + haClose + haOpen) / 4.0); 392 | case pr_ha_trendbiased: 393 | if (haClose > haOpen) { 394 | return ((haHigh + haClose) / 2.0); 395 | } else { 396 | return ((haLow + haClose) / 2.0); 397 | } 398 | } 399 | 400 | } else { 401 | 402 | switch (priceType) { 403 | case pr_close: return (close[i]); 404 | case pr_open: return (open[i]); 405 | case pr_high: return (high[i]); 406 | case pr_low: return (low[i]); 407 | case pr_median: return ((high[i] + low[i]) / 2.0); 408 | case pr_medianbody: return ((open[i] + close[i]) / 2.0); 409 | case pr_typical: return ((high[i] + low[i] + close[i]) / 3.0); 410 | case pr_weighted: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); 411 | case pr_average: return ((high[i] + low[i] + close[i] + open[i]) / 4.0); 412 | case pr_trendbiased: 413 | if (close[i] > open[i]) { 414 | return ((high[i] + close[i]) / 2.0); 415 | } else { 416 | return ((low[i] + close[i]) / 2.0); 417 | } 418 | } 419 | } 420 | return (0); 421 | } 422 | --------------------------------------------------------------------------------