//--------------------------------------------------------------------------------------------------------------------- #define MName "Exponential Commodity Channel Index" #define MVersion "1.0" #define MBuild "2023-04-15 15:47 WEST" #define MCopyright "Copyright \x00A9 2023, Fernando M. I. Carreiro, All rights reserved" #define MProfile "https://www.mql5.com/en/users/FMIC" //--------------------------------------------------------------------------------------------------------------------- #property strict #property version MVersion #property description MName #property description "An implementation of the Commodity Channel Index using exponential moving averages," #property description "instead of simple moving averages as implemented by the its creator, Donald Lambert." #property description "MetaTrader Indicator (Build "MBuild")" #property copyright MCopyright #property link MProfile //--------------------------------------------------------------------------------------------------------------------- //--- Setup #property indicator_separate_window // Define number of buffers and plots #define MPlots 1 #define MBuffers 3 #ifdef __MQL4__ #property indicator_buffers MPlots #else #property indicator_buffers MBuffers #property indicator_plots MPlots #property indicator_applied_price PRICE_TYPICAL #endif // Display properties for plots #property indicator_label1 "ECCI" #property indicator_type1 DRAW_LINE #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #property indicator_color1 C'38,166,154' // Display levels for plots #property indicator_level1 100 #property indicator_level2 -100 #property indicator_levelstyle STYLE_DOT //--- Parameter settings input double i_dbAveragingPeriod = 14; // Averaging period input double i_dbAdjustmentConstant = 0.015; // Adjustment constant #ifdef __MQL4__ input ENUM_APPLIED_PRICE i_ePriceApplied = PRICE_TYPICAL; // Applied price #endif //--- Macro definitions // Define OnCalculate loop sequencing macros #define MOnCalcPrevTest ( prev_calculated < 1 || prev_calculated > rates_total ) #ifdef __MQL4__ // for MQL4 (as series) #define MOnCalcNext( _index ) ( _index-- ) #define MOnCalcBack( _index, _offset ) ( _index + _offset ) #define MOnCalcCheck( _index ) ( _index >= 0 ) #define MOnCalcValid( _index ) ( _index < rates_total ) #define MOnCalcStart \ ( rates_total - ( MOnCalcPrevTest ? 1 : prev_calculated ) ) #else // for MQL5 (as non-series) #define MOnCalcNext( _index ) ( _index++ ) #define MOnCalcBack( _index, _offset ) ( _index - _offset ) #define MOnCalcCheck( _index ) ( _index < rates_total ) #define MOnCalcValid( _index ) ( _index >= 0 ) #define MOnCalcStart \ ( MOnCalcPrevTest ? 0 : prev_calculated - 1 ) #endif // Define applied price macro (MQL4 only) #ifdef __MQL4__ #define MSetAppliedPrice( _type, _where, _index ) { switch( _type ) { \ case PRICE_WEIGHTED: _where = ( high[ _index ] + low[ _index ] + close[ _index ] \ + close[ _index ] ) * 0.25; break; \ case PRICE_TYPICAL: _where = ( high[ _index ] + low[ _index ] + close[ _index ] ) / 3.0; break; \ case PRICE_MEDIAN: _where = ( high[ _index ] + low[ _index ] ) * 0.5; break; \ case PRICE_HIGH: _where = high[ _index ]; break; \ case PRICE_LOW: _where = low[ _index ]; break; \ case PRICE_OPEN: _where = open[ _index ]; break; \ case PRICE_CLOSE: \ default: _where = close[ _index ]; }; } #endif // Define macro for calculating and assigning exponential moving average #define MCalcEma( _var, _value, _weight ) \ _var = _var##Prev + ( ( _value ) - _var##Prev ) * ( _weight ) // Define macro for invalid parameter values #define MCheckParameter( _condition, _text ) if( _condition ) \ { Print( "Error: Invalid ", _text ); return INIT_PARAMETERS_INCORRECT; } //--- Global variable declarations // Indicator buffers double g_adbPriceAverage[], // Buffer for average of price g_adbDeviationAverage[], // Buffer for average of absolute deviation g_adbCCI[]; // Buffer for commodity channel index // Miscellaneous global variables double g_dbEmaWeight; // Weight to be used for exponential moving averages //--- Event handling functions // Initialisation event handler int OnInit(void) { // Validate input parameters MCheckParameter( i_dbAveragingPeriod < 1.0, "averaging period" ); MCheckParameter( i_dbAdjustmentConstant < DBL_EPSILON, "adjustment constant" ); // Calculate EMA alpha weight for Wilder's moving average, also known as smoothed moving average (SMMA) g_dbEmaWeight = 2.0 / ( i_dbAveragingPeriod + 1.0 ); // Set number of significant digits (precision) IndicatorSetInteger( INDICATOR_DIGITS, _Digits ); // Set buffers int iBuffer = 0; #ifdef __MQL4__ IndicatorBuffers( MBuffers + 1 ); // Set total number of buffers (MQL4 Only) #endif SetIndexBuffer( iBuffer++, g_adbCCI, INDICATOR_DATA ); // Commodity channel index SetIndexBuffer( iBuffer++, g_adbPriceAverage, INDICATOR_CALCULATIONS ); // Price average SetIndexBuffer( iBuffer++, g_adbDeviationAverage, INDICATOR_CALCULATIONS ); // Absolute deviation average // Set indicator name and plot label #define MNameLabel( _prefix ) \ StringFormat( _prefix " ( %.1f, %.3f )", i_dbAveragingPeriod, i_dbAdjustmentConstant ) string sName = MNameLabel( MName ), sLabel = MNameLabel( indicator_label1 ); IndicatorSetString( INDICATOR_SHORTNAME, sName ); #ifdef __MQL4__ SetIndexLabel( 0, sLabel ); #else PlotIndexSetString( 0, PLOT_LABEL, sLabel ); #endif return INIT_SUCCEEDED; // Successful initialisation of indicator }; // Calculation event handler #ifdef __MQL4__ int OnCalculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[] ) { #else int OnCalculate( const int rates_total, const int prev_calculated, const int begin, const double& price[] ) { #endif // Main loop: calculate values and apply data to buffers for( int iCur = MOnCalcStart, iPrev = MOnCalcBack( iCur, 1 ); !IsStopped() && MOnCalcCheck( iCur ); MOnCalcNext( iCur ), MOnCalcNext( iPrev ) ) { // Get (or calculate) current applied price #ifdef __MQL4__ double dbPriceCur; MSetAppliedPrice( i_ePriceApplied, dbPriceCur, iCur ); #else double dbPriceCur = price[ iCur ]; #endif // Calculate the remaining values double dbPriceAverage, dbDeviationAverage, dbCCI; if( MOnCalcValid( iPrev ) ) { // Calculate price average double dbPriceAveragePrev = g_adbPriceAverage[ iPrev ]; MCalcEma( dbPriceAverage, dbPriceCur, g_dbEmaWeight ); // Calculate deviation and its absolute average double dbDeviation = dbPriceCur - dbPriceAverage, dbDeviationAveragePrev = g_adbDeviationAverage[ iPrev ]; MCalcEma( dbDeviationAverage, fabs( dbDeviation ), g_dbEmaWeight ); // Calculate commodity channel index dbCCI = dbDeviationAverage > DBL_EPSILON ? dbDeviation / ( dbDeviationAverage * i_dbAdjustmentConstant ) : ( dbDeviation > 0.0 ? DBL_MAX : -DBL_MAX ); } else { dbPriceAverage = dbPriceCur; dbDeviationAverage = dbCCI = 0.0; }; // Set buffer values g_adbCCI[ iCur ] = dbCCI; g_adbPriceAverage[ iCur ] = dbPriceAverage; g_adbDeviationAverage[ iCur ] = dbDeviationAverage; }; return rates_total; // Return value for prev_calculated of next call }; //---------------------------------------------------------------------------------------------------------------------