//+------------------------------------------------------------------+ //| Keltner Channel.mq5 | //| Copyright 2009-2025, MetaQuotes Ltd | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009-2025, MetaQuotes Ltd" #property link "http://www.mql5.com" #property description "Keltner Channel" #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed //--- labels #property indicator_label1 "Upper Keltner" #property indicator_label2 "Middle Keltner" #property indicator_label3 "Lower Keltner" //--- input parameters input int InpEMAPeriod=20; // Period of EMA input int InpATRPeriod=10; // Period of ATR input double InpATRFactor=2.0; // ATR multiplier input bool InpShowLabel=true; // Show price of level //--- global variables for parameters int ExtEMAPeriod; int ExtATRPeriod; double ExtATRFactor; //--- indicator buffers double ExtUppBuffer[]; double ExtEMABuffer[]; double ExtDwnBuffer[]; //--- indicator handles int ExtEMAHandle; int ExtATRHandle; //--- unique prefix to identify indicator objects string ExtPrefixUniq; int ExtPeriod; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- check for input values if(InpEMAPeriod<10) { ExtEMAPeriod=20; PrintFormat("Incorrect value for input variable InpEMAPeriod=%d. Indicator will use value=%d for calculations.", InpEMAPeriod, ExtEMAPeriod); } else ExtEMAPeriod=InpEMAPeriod; if(InpATRPeriod<3) { ExtATRPeriod=10; PrintFormat("Incorrect value for input variable InpATRPeriod=%d. Indicator will use value=%d for calculations.", InpATRPeriod, ExtATRPeriod); } else ExtATRPeriod=InpATRPeriod; if(InpATRFactor<1.0) { ExtATRFactor=2.0; PrintFormat("Incorrect value for input variable InpBandsDeviations=%f. Indicator will use value=%f for calculations.", InpATRFactor, ExtATRFactor); } else ExtATRFactor=InpATRFactor; //--- define buffers SetIndexBuffer(0, ExtUppBuffer); SetIndexBuffer(1, ExtEMABuffer); SetIndexBuffer(2, ExtDwnBuffer); //--- indexes draw begin settings PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpEMAPeriod+1); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpEMAPeriod+1); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, InpEMAPeriod+1); //--- set a 1-bar offset for each line PlotIndexSetInteger(0, PLOT_SHIFT, 1); PlotIndexSetInteger(1, PLOT_SHIFT, 1); PlotIndexSetInteger(2, PLOT_SHIFT, 1); //--- set drawing line empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0); //--- indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Keltner Channel"); //--- number of digits of indicator value IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- create indicators ExtEMAHandle=iMA(NULL, 0, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); ExtATRHandle=iATR(NULL, 0, InpATRPeriod); ExtPeriod=PeriodSeconds(_Period); //--- prepare prefix for objects string number=StringFormat("%I64d", GetTickCount64()); ExtPrefixUniq=StringSubstr(number, StringLen(number)-4); ExtPrefixUniq=ExtPrefixUniq+"_KLT"; Print("Indicator \"Keltner Channels\" started, prefix=", ExtPrefixUniq); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if this is the first calculation of the indicator if(prev_calculated==0) { //--- populate the beginning values, for which the indicator cannot be calculated, with empty values ArrayFill(ExtUppBuffer, 0, rates_total, 0); ArrayFill(ExtEMABuffer, 0, rates_total, 0); ArrayFill(ExtDwnBuffer, 0, rates_total, 0); //--- get EMA values into the indicator buffer if(CopyBuffer(ExtEMAHandle, 0, 0, rates_total, ExtEMABuffer)<0) return(0); //--- get ATR indicator values into a dynamic array double atr[]; if(CopyBuffer(ExtATRHandle, 0, 0, rates_total, atr)<0) return(0); //--- shift from the beginning by the required number of bars int start=MathMax(InpEMAPeriod, InpATRPeriod)+1; //--- fill in the values of the upper and lower channel borders for(int i=start; i