#property version "1.00" #property indicator_chart_window #property indicator_buffers 9 #property indicator_plots 6 //--- plot properties #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_type3 DRAW_LINE #property indicator_type4 DRAW_NONE #property indicator_type5 DRAW_ARROW #property indicator_type6 DRAW_ARROW #property indicator_color1 clrBlue // Basis #property indicator_color2 clrLime // Bull Level #property indicator_color3 clrRed // Bear Level #property indicator_color4 clrWhite // Trend #property indicator_color5 clrLime // Buy Signal #property indicator_color6 clrRed // Sell Signal #property indicator_width1 2 #property indicator_width2 2 #property indicator_width3 2 #property indicator_width4 2 //--- indicator buffers double BasisBuffer[]; double BullLevelBuffer[]; double BearLevelBuffer[]; double TrendBuffer[]; double BuySignalBuffer[]; double SellSignalBuffer[]; double UpperBuffer[]; double LowerBuffer[]; double StdDevBuffer[]; //--- input parameters input int InpLength = 10; // Main Length input int InpSmoothLen = 14; // Smoothing Length input double InpSensitivity = 2.0; // Sensitivity input bool InpShowSignals = true; // Show Signals input int InpMaxBars = 2000; // Max Bars (0 = all) input bool UseRetest= true; //--- handles int fastEmaHandle; int slowEmaHandle; int stdDevHandle; int lastsignal = -1; double iMAOnArray(const double &array[], int total, int period, int ma_shift, int shift) { if(total == 0) total = ArraySize(array); if(total <= 0) return 0.0; if(shift < 0 || period <= 0) return 0.0; if(shift > total - 1) return 0.0; // Account for ma_shift in the calculations shift += ma_shift; if(shift > total - 1) return 0.0; if(total - shift < period) return 0.0; double buf[]; ArrayResize(buf, total); ArrayInitialize(buf, 0.0); // Calculate the initial SMA for more accuracy double sum = 0; for(int i = 0; i < period && shift + i < total; i++) { sum += array[total - 1 - i]; } buf[total - 1] = sum / period; // Use the correct smoothing factor double alpha = 2.0 / (period + 1.0); // Calculate EMA from newest to oldest for(int pos = total - 2; pos >= 0; pos--) { buf[pos] = array[pos] * alpha + buf[pos + 1] * (1.0 - alpha); } return buf[shift]; } //-----------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize indicator buffers SetIndexBuffer(0, BasisBuffer, INDICATOR_DATA); SetIndexBuffer(1, BullLevelBuffer, INDICATOR_DATA); SetIndexBuffer(2, BearLevelBuffer, INDICATOR_DATA); SetIndexBuffer(3, TrendBuffer, INDICATOR_DATA); SetIndexBuffer(4, BuySignalBuffer, INDICATOR_DATA); SetIndexBuffer(5, SellSignalBuffer, INDICATOR_DATA); SetIndexBuffer(6, UpperBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(7, LowerBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(8, StdDevBuffer, INDICATOR_CALCULATIONS); // Set up arrow codes for signals PlotIndexSetInteger(4, PLOT_ARROW, 233); PlotIndexSetInteger(5, PLOT_ARROW, 234); // Set empty values PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE); ArraySetAsSeries(BasisBuffer, true); ArraySetAsSeries(BullLevelBuffer, true); ArraySetAsSeries(BearLevelBuffer, true); ArraySetAsSeries(TrendBuffer, true); ArraySetAsSeries(BuySignalBuffer, true); ArraySetAsSeries(SellSignalBuffer, true); ArraySetAsSeries(UpperBuffer, true); ArraySetAsSeries(LowerBuffer, true); // Initialize handles fastEmaHandle = iMA(Symbol(), Period(), InpLength, 0, MODE_EMA, PRICE_TYPICAL); slowEmaHandle = iMA(Symbol(), Period(), InpLength * 2, 0, MODE_EMA, PRICE_TYPICAL); stdDevHandle = iStdDev(Symbol(), Period(), InpLength, 0, MODE_SMA, PRICE_TYPICAL); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(fastEmaHandle); IndicatorRelease(slowEmaHandle); IndicatorRelease(stdDevHandle); } //+------------------------------------------------------------------+ 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(rates_total < InpLength * 2) return(0); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); int limit; int startBar = 0; // Handle initial calculation if(prev_calculated == 0) { // For initial calculation, respect the max bars setting startBar = (InpMaxBars > 0) ? MathMax(rates_total - InpMaxBars, 0) : 0; limit = rates_total - startBar - InpLength * 2; // Initialize arrays from the start bar for(int i = rates_total - 1; i >= startBar; i--) { BasisBuffer[i] = EMPTY_VALUE; BullLevelBuffer[i] = EMPTY_VALUE; BearLevelBuffer[i] = EMPTY_VALUE; TrendBuffer[i] = 0; BuySignalBuffer[i] = EMPTY_VALUE; SellSignalBuffer[i] = EMPTY_VALUE; } } else { // For real-time updates, always process new bars limit = rates_total - prev_calculated; if(limit > 1) limit++; } // Copy indicator data - always copy enough bars for calculation double fastMA[], slowMA[], stdDev[]; ArraySetAsSeries(fastMA, true); ArraySetAsSeries(slowMA, true); ArraySetAsSeries(stdDev, true); int copyBars = rates_total - startBar; if(CopyBuffer(fastEmaHandle, 0, 0, copyBars, fastMA) <= 0) return(0); if(CopyBuffer(slowEmaHandle, 0, 0, copyBars, slowMA) <= 0) return(0); if(CopyBuffer(stdDevHandle, 0, 0, copyBars, stdDev) <= 0) return(0); // Calculate initial buffers for(int i = limit; i >= 0 && !IsStopped(); i--) { if(i + startBar >= rates_total) continue; BasisBuffer[i] = (fastMA[i] + slowMA[i]) / 2.0; StdDevBuffer[i] = stdDev[i]; double volatility = iMAOnArray(StdDevBuffer, 0, InpSmoothLen, 0, i) * InpSensitivity; UpperBuffer[i] = BasisBuffer[i] + volatility; LowerBuffer[i] = BasisBuffer[i] - volatility; } // Initialize first bar's trend if(prev_calculated == 0) { int firstBar = rates_total - startBar - InpLength * 2; TrendBuffer[firstBar] = close[firstBar] > BasisBuffer[firstBar] ? 1 : -1; if(TrendBuffer[firstBar] > 0) { BullLevelBuffer[firstBar] = LowerBuffer[firstBar]; BearLevelBuffer[firstBar] = EMPTY_VALUE; } else { BearLevelBuffer[firstBar] = UpperBuffer[firstBar]; BullLevelBuffer[firstBar] = EMPTY_VALUE; } } // Calculate remaining bars for(int i = MathMin(limit, rates_total - 2); i >= 0 && !IsStopped(); i--) { // Initialize signal buffers BuySignalBuffer[i] = EMPTY_VALUE; SellSignalBuffer[i] = EMPTY_VALUE; // Get previous values double prevTrend = TrendBuffer[i + 1]; double prevBullLevel = BullLevelBuffer[i + 1]; double prevBearLevel = BearLevelBuffer[i + 1]; // Update trend and levels if(prevTrend > 0) { if(close[i + 1] < prevBullLevel && prevBullLevel != EMPTY_VALUE) { TrendBuffer[i] = -1; BearLevelBuffer[i] = UpperBuffer[i]; BullLevelBuffer[i] = EMPTY_VALUE; if(InpShowSignals) SellSignalBuffer[i + 1] = UpperBuffer[i + 1]; lastsignal = POSITION_TYPE_SELL; } else { TrendBuffer[i] = 1; BullLevelBuffer[i] = LowerBuffer[i]; BearLevelBuffer[i] = EMPTY_VALUE; } } else { if(close[i + 1] > prevBearLevel && prevBearLevel != EMPTY_VALUE) { TrendBuffer[i] = 1; BullLevelBuffer[i] = LowerBuffer[i]; BearLevelBuffer[i] = EMPTY_VALUE; if(InpShowSignals) BuySignalBuffer[i + 1] = LowerBuffer[i + 1]; lastsignal = POSITION_TYPE_BUY; } else { TrendBuffer[i] = -1; BearLevelBuffer[i] = UpperBuffer[i]; BullLevelBuffer[i] = EMPTY_VALUE; } } if(UseRetest) { if(lastsignal==POSITION_TYPE_BUY) { if(close[i+1]<=BasisBuffer[i+1] && close[i]>=BasisBuffer[i]) BuySignalBuffer[i ] = LowerBuffer[i ]; } else if(lastsignal==POSITION_TYPE_SELL) { if(close[i+1]>=BasisBuffer[i+1] && close[i]<=BasisBuffer[i]) SellSignalBuffer[i ] = UpperBuffer[i ]; } } } return(rates_total); }