// More information about this indicator can be found at: //http://fxcodebase.com/code/viewtopic.php?f=38&t=70474 //+------------------------------------------------------------------+ //| Copyright © 2020, Gehtsoft USA LLC | //| http://fxcodebase.com | //+------------------------------------------------------------------+ //| Developed by : Mario Jemic | //| mario.jemic@gmail.com | //| https://AppliedMachineLearning.systems | //+------------------------------------------------------------------+ //| Support our efforts by donating | //| Paypal : https://goo.gl/9Rj74e | //| Patreon : https://goo.gl/GdXWeN | //+------------------------------------------------------------------+ #property copyright "Copyright © 2020, Gehtsoft USA LLC" #property link "http://fxcodebase.com" #property version "1.0" #property strict #property indicator_separate_window #property indicator_buffers 4 input int Period = 50; // CCI Period input double Smoothing = 5; // Smoothing input double Fast = 2.618; // Fast Smoothing input double Slow = 4.236; // Slow Smoothing input color Up = Green; // Up Color input color Down = Red; // Down Color input color color1 = Gray; // Cental Line Color input color color2 = Gray; // Fast Line Color input color color3 = Gray; // Slow Line Color input int bars_limit = 1000; // Bars limit // Stream v.3.0 // More templates and snippets on https://github.com/sibvic/mq4-templates interface IStream { public: virtual void AddRef() = 0; virtual void Release() = 0; virtual int Size() = 0; virtual bool GetValue(const int period, double &val) = 0; }; // Instrument info v.1.7 // More templates and snippets on https://github.com/sibvic/mq4-templates #ifndef InstrumentInfo_IMP #define InstrumentInfo_IMP class InstrumentInfo { string _symbol; double _mult; double _point; double _pipSize; int _digits; double _tickSize; public: InstrumentInfo(const string symbol) { _symbol = symbol; _point = MarketInfo(symbol, MODE_POINT); _digits = (int)MarketInfo(symbol, MODE_DIGITS); _mult = _digits == 3 || _digits == 5 ? 10 : 1; _pipSize = _point * _mult; _tickSize = MarketInfo(_symbol, MODE_TICKSIZE); } // Return < 0 when lot1 < lot2, > 0 when lot1 > lot2 and 0 owtherwise int CompareLots(double lot1, double lot2) { double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP); if (lotStep == 0) { return lot1 < lot2 ? -1 : (lot1 > lot2 ? 1 : 0); } int lotSteps1 = (int)floor(lot1 / lotStep + 0.5); int lotSteps2 = (int)floor(lot2 / lotStep + 0.5); int res = lotSteps1 - lotSteps2; return res; } static double GetBid(const string symbol) { return MarketInfo(symbol, MODE_BID); } double GetBid() { return GetBid(_symbol); } static double GetAsk(const string symbol) { return MarketInfo(symbol, MODE_ASK); } double GetAsk() { return GetAsk(_symbol); } static double GetPipSize(const string symbol) { double point = MarketInfo(symbol, MODE_POINT); double digits = (int)MarketInfo(symbol, MODE_DIGITS); double mult = digits == 3 || digits == 5 ? 10 : 1; return point * mult; } double GetPipSize() { return _pipSize; } double GetPointSize() { return _point; } string GetSymbol() { return _symbol; } double GetSpread() { return (GetAsk() - GetBid()) / GetPipSize(); } int GetDigits() { return _digits; } double GetTickSize() { return _tickSize; } double GetMinLots() { return SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MIN); }; double AddPips(const double rate, const double pips) { return RoundRate(rate + pips * _pipSize); } double RoundRate(const double rate) { return NormalizeDouble(MathFloor(rate / _tickSize + 0.5) * _tickSize, _digits); } double RoundLots(const double lots) { double lotStep = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_STEP); if (lotStep == 0) { return 0.0; } return floor(lots / lotStep) * lotStep; } double LimitLots(const double lots) { double minVolume = GetMinLots(); if (minVolume > lots) { return 0.0; } double maxVolume = SymbolInfoDouble(_symbol, SYMBOL_VOLUME_MAX); if (maxVolume < lots) { return maxVolume; } return lots; } double NormalizeLots(const double lots) { return LimitLots(RoundLots(lots)); } }; #endif // Abstract stream v1.1 // More templates and snippets on https://github.com/sibvic/mq4-templates #ifndef AStream_IMP class AStream : public IStream { protected: string _symbol; ENUM_TIMEFRAMES _timeframe; double _shift; InstrumentInfo *_instrument; int _references; AStream(const string symbol, const ENUM_TIMEFRAMES timeframe) { _references = 1; _shift = 0.0; _symbol = symbol; _timeframe = timeframe; _instrument = new InstrumentInfo(_symbol); } ~AStream() { delete _instrument; } public: void SetShift(const double shift) { _shift = shift; } void AddRef() { ++_references; } void Release() { --_references; if (_references == 0) delete &this; } int Size() { return iBars(_symbol, _timeframe); } }; #define AStream_IMP #endif // Colored stream v3.2 #ifndef ColoredStream_IMP #define ColoredStream_IMP class ColoredStreamData { public: double Stream[]; }; class ColoredStream : public AStream { public: ColoredStreamData _streams[]; double _data[]; ColoredStream(const string symbol, const ENUM_TIMEFRAMES timeframe) :AStream(symbol, timeframe) { } void Init(double defaultValue) { for (int i = 0; i < ArraySize(_streams); ++i) { ArrayInitialize(_streams[i].Stream, defaultValue); } ArrayInitialize(_data, defaultValue); } int RegisterInternalStream(int id) { SetIndexBuffer(id + 0, _data); SetIndexStyle(id + 0, DRAW_NONE); return id + 1; } int RegisterStream(int id, color clr, string label = "", int lineType = DRAW_LINE, ENUM_LINE_STYLE lineStyle = STYLE_SOLID, int width = 1) { int size = ArraySize(_streams); ArrayResize(_streams, size + 1); SetIndexStyle(id, lineType, lineStyle, width, clr); SetIndexBuffer(id, _streams[size].Stream); SetIndexEmptyValue(id, EMPTY_VALUE); if (label != "") SetIndexLabel(id, label); return id + 1; } int GetColorIndex(int period) { for (int i = 0; i < ArraySize(_streams); ++i) { if (_streams[i].Stream[period] != EMPTY_VALUE) return i; } return -1; } void Set(double value, int period, int colorIndex) { _data[period] = value; for (int i = 0; i < ArraySize(_streams); ++i) { if (colorIndex == i) { _streams[i].Stream[period] = value; if (period + 1 < iBars(_symbol, _timeframe) && _streams[i].Stream[period + 1] == EMPTY_VALUE) _streams[i].Stream[period + 1] = _data[period + 1]; } else _streams[i].Stream[period] = EMPTY_VALUE; } } bool GetValue(const int period, double &val) { if (period >= iBars(_symbol, _timeframe)) { return false; } val = _data[period]; return _data[period] != EMPTY_VALUE; } }; #endif string IndicatorObjPrefix; bool NamesCollision(const string name) { for (int k = ObjectsTotal(); k >= 0; k--) { if (StringFind(ObjectName(0, k), name) == 0) { return true; } } return false; } string GenerateIndicatorPrefix(const string target) { for (int i = 0; i < 1000; ++i) { string prefix = target + "_" + IntegerToString(i); if (!NamesCollision(prefix)) { return prefix; } } return target; } ColoredStream* Central; double Average[], Data1[], Data2[], ema2[], fast_line[], slow_line[], fast[], slow[]; int init() { IndicatorObjPrefix = GenerateIndicatorPrefix("qqecci"); IndicatorShortName("QQE of CCI"); IndicatorBuffers(12); int id = 0; Central = new ColoredStream(_Symbol, (ENUM_TIMEFRAMES)_Period); id = Central.RegisterStream(id, Up, "Central"); id = Central.RegisterStream(id, Down, "Central"); SetIndexStyle(id, DRAW_LINE, STYLE_SOLID, 1, color2); SetIndexBuffer(id, fast); SetIndexLabel(id, "Fast"); ++id; SetIndexStyle(id, DRAW_LINE, STYLE_SOLID, 1, color3); SetIndexBuffer(id, slow); SetIndexLabel(id, "Slow"); ++id; id = Central.RegisterInternalStream(id); SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, Average); ++id; SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, Data1); ++id; SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, Data2); ++id; SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, ema2); ++id; SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, fast_line); ++id; SetIndexStyle(id, DRAW_NONE); SetIndexBuffer(id, slow_line); ++id; return INIT_SUCCEEDED; } int deinit() { delete Central; ObjectsDeleteAll(ChartID(), IndicatorObjPrefix); return 0; } 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 (prev_calculated <= 0 || prev_calculated > rates_total) { Central.Init(0); ArrayInitialize(Average, 0); ArrayInitialize(Data1, 0); ArrayInitialize(Data2, 0); ArrayInitialize(ema2, 0); ArrayInitialize(fast, 0); ArrayInitialize(slow, 0); ArrayInitialize(fast_line, 0); ArrayInitialize(slow_line, 0); ArrayInitialize(fast, 0); ArrayInitialize(slow, 0); } bool timeSeries = ArrayGetAsSeries(time); bool openSeries = ArrayGetAsSeries(open); bool highSeries = ArrayGetAsSeries(high); bool lowSeries = ArrayGetAsSeries(low); bool closeSeries = ArrayGetAsSeries(close); bool tickVolumeSeries = ArrayGetAsSeries(tick_volume); ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(tick_volume, true); int toSkip = Period; int veryFirst = MathMin(bars_limit, rates_total - 1 - toSkip); int CentralFirst = veryFirst - Smoothing; int ema2First = CentralFirst - Period; int ema3First = ema2First - Period; for (int pos = MathMin(bars_limit, rates_total - 1 - MathMax(prev_calculated, toSkip)); pos >= 0 && !IsStopped(); --pos) { Average[pos] = iMA(_Symbol, _Period, Period, 9, MODE_SMA, PRICE_CLOSE, pos); double Sum = 0; for (int i = 0; i < Period; ++i) { Sum += MathAbs(close[pos + i] - Average[pos]); } Data1[pos] = (close[pos] - Average[pos]) / (0.015 * Sum / Period); if (pos > CentralFirst) { continue; } double central = iMAOnArray(Data1, 0, Smoothing, 0, MODE_EMA, pos); Data2[pos] = Central._data[pos + 1] == EMPTY_VALUE ? 0 : MathAbs(Central._data[pos + 1] - central); if (pos > ema2First) { continue; } ema2[pos] = iMAOnArray(Data2, 0, Period, 0, MODE_EMA, pos); if (pos > ema3First) { continue; } double ema3 = iMAOnArray(ema2, 0, Period, 0, MODE_EMA, pos); fast_line[pos] = ema3 * Fast; slow_line[pos] = ema3 * Slow; double Value = slow[pos + 1]; if (central < slow[pos]) { Value = central + slow_line[pos]; if (Central._data[pos + 1] < slow[pos + 1] && Central._data[pos + 1] > slow[pos + 1]) { Value = slow[pos + 1]; } } if (central > slow[pos]) { Value = central - slow_line[pos]; if (Central._data[pos + 1] >slow[pos + 1] && Central._data[pos + 1] < slow[pos + 1]) { Value = slow[pos + 1]; } } slow[pos] = Value; Value = fast[pos + 1]; if (central < fast[pos]) { Value = central + fast_line[pos]; if (Central._data[pos + 1] < fast[pos + 1] && Central._data[pos + 1] > fast[pos + 1]) { Value = fast[pos + 1]; } } if (central > fast[pos]) { Value = central - fast_line[pos]; if (Central._data[pos + 1] >fast[pos + 1] && Central._data[pos + 1] < fast[pos + 1]) { Value = fast[pos + 1]; } } fast[pos] = Value; int Color = Central.GetColorIndex(pos + 1); if (central > fast[pos] && central > slow[pos]) { Color = 1; } else if (central < fast[pos] && central < slow[pos]) { Color = 0; } Central.Set(central, pos, Color); } ArraySetAsSeries(time, timeSeries); ArraySetAsSeries(open, openSeries); ArraySetAsSeries(high, highSeries); ArraySetAsSeries(low, lowSeries); ArraySetAsSeries(close, closeSeries); ArraySetAsSeries(tick_volume, tickVolumeSeries); return rates_total; }