261 lines
9.4 KiB
MQL5
261 lines
9.4 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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;
| |||
int ExtPeriod;
| |||
| |||
//--- indicator buffers
| |||
double ExtUppBuffer[];
| |||
double ExtEMABuffer[];
| |||
double ExtDwnBuffer[];
| |||
| |||
//--- indicator handles
| |||
int ExtEMAHandle;
| |||
int ExtATRHandle;
| |||
| |||
//--- unique prefix for indicator objects
| |||
string ExtPrefixUniq;
| |||
//--- current chart scale
| |||
long ExtChartScale=2;
| |||
//--- price labels should not be shown on the built-in VPS
| |||
bool ExtShowLabel=false;
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator initialization function |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
//--- if the indicator is running on the built-in VPS, price labels should not be shown
| |||
if(!TerminalInfoInteger(TERMINAL_VPS))
| |||
ExtShowLabel=InpShowLabel;
| |||
| |||
//--- 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<rates_total; i++)
| |||
{
| |||
ExtUppBuffer[i]=ExtEMABuffer[i]+InpATRFactor*atr[i];
| |||
ExtDwnBuffer[i]=ExtEMABuffer[i]-InpATRFactor*atr[i];
| |||
}
| |||
| |||
//--- succesfully calculated
| |||
return(rates_total);
| |||
}
| |||
| |||
//--- if the indicator has previously been calculated, calculate values for the last 2 bars
| |||
int start=prev_calculated-2;
| |||
for(int i=start; i<rates_total; i++)
| |||
{
| |||
//--- for element-by-element copying from the indicator, use the reverse index
| |||
int reverse_index=rates_total-i;
| |||
| |||
//--- get indicator values
| |||
double ema[];
| |||
if(CopyBuffer(ExtEMAHandle, 0, reverse_index, 1, ema)<0)
| |||
return(prev_calculated);
| |||
double atr[];
| |||
if(CopyBuffer(ExtATRHandle, 0, reverse_index, 1, atr)<0)
| |||
return(prev_calculated);
| |||
| |||
//--- write values into buffers
| |||
ExtEMABuffer[i]=ema[0];
| |||
ExtUppBuffer[i]=ema[0]+InpATRFactor*atr[0];
| |||
ExtDwnBuffer[i]=ema[0]-InpATRFactor*atr[0];
| |||
}
| |||
| |||
//--- draw labels on levels
| |||
if(ExtShowLabel)
| |||
{
| |||
ShowPriceLevels(time[rates_total-1]+ExtPeriod, rates_total-1);
| |||
ChartRedraw();
| |||
}
| |||
| |||
//--- succesfully calculated
| |||
return(rates_total);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Custom indicator deinitialization function |
| |||
//+------------------------------------------------------------------+
| |||
void OnDeinit(const int reason)
| |||
{
| |||
//--- delete all our graphical objects after use
| |||
Print("Indicator \"Keltner Channels\" stopped, delete all objects with prefix=", ExtPrefixUniq);
| |||
ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_ARROW_RIGHT_PRICE);
| |||
ChartRedraw(0);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Handles CHARTEVENT_CHART_CHANGE to track chart window updates |
| |||
//+------------------------------------------------------------------+
| |||
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
| |||
{
| |||
if(id==CHARTEVENT_CHART_CHANGE)
| |||
if(!ChartGetInteger(0, CHART_SCALE, 0, ExtChartScale))
| |||
{
| |||
//--- output an error message to the Experts journal
| |||
Print(__FUNCTION__+", ChartGetInteger(CHART_SCALE) failed, error = ", GetLastError());
| |||
}
| |||
//---
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Show prices' levels |
| |||
//+------------------------------------------------------------------+
| |||
void ShowPriceLevels(datetime time, int last_index)
| |||
{
| |||
ShowRightPrice(ExtPrefixUniq+"_Upp", time, ExtUppBuffer[last_index], clrBlue);
| |||
ShowRightPrice(ExtPrefixUniq+"_EMA", time, ExtEMABuffer[last_index], clrGray);
| |||
ShowRightPrice(ExtPrefixUniq+"_DWN", time, ExtDwnBuffer[last_index], clrRed);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Create or Update "Right Price Label" object |
| |||
//+------------------------------------------------------------------+
| |||
bool ShowRightPrice(const string name, datetime time, double price, color clr)
| |||
{
| |||
if(!ObjectCreate(0, name, OBJ_ARROW_RIGHT_PRICE, 0, time, price))
| |||
{
| |||
ObjectMove(0, name, 0, time, price);
| |||
return(false);
| |||
}
| |||
| |||
//--- adjust label size based on chart scale
| |||
int width=ExtChartScale>1 ? 2:1; // if chart scale > 1, then label size = 2
| |||
| |||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
| |||
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
| |||
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
| |||
ObjectSetInteger(0, name, OBJPROP_BACK, false);
| |||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
| |||
ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
| |||
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
| |||
ObjectSetInteger(0, name, OBJPROP_ZORDER, 0);
| |||
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
|