#property strict #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_type3 DRAW_LINE #property indicator_width1 1 #property indicator_width2 1 #property indicator_width3 1 #property indicator_color1 clrBlue #property indicator_color2 clrGreen #property indicator_color3 clrRed #property indicator_style1 STYLE_SOLID #property indicator_style2 STYLE_SOLID #property indicator_style3 STYLE_SOLID double avgLine[]; double upperTrendline[]; double LowerTrendline[]; double upperBand[2147483]; // keep track of upperband values double lowerBand[2147483]; // keep track of lowerband values bool trend = true; // Trend direction status bool trends[2147483]; // keep track of the trend status double trend_line[2147483]; // keep track of trendline values // Custom functions // Calculates the median of an array //@param array [in] (array[]) that will be used for the calculation //@returns (double) double ArrayMedian(const double& array[]) { double median; double copy[]; int lenght = ArraySize(array); ArrayResize(copy, lenght); ArrayCopy(copy, array, 0, 0, WHOLE_ARRAY); ArraySort(copy, WHOLE_ARRAY, 0, MODE_DESCEND); if (lenght % 2 == 0) // it's even { median = (copy[lenght / 2] + copy[(lenght / 2) - 1]) / 2.0; } else // it's odd { median = copy[lenght / 2]; } return (median); } // Calculates the average of an array //@param array [in] (array[]) that will be used for the calculation //@returns (double) double ArrayAvg(const double& array[]) { double sum = 0; double avg; for (int i = 0; i < ArraySize(array); i++) { sum += array[i]; } avg = sum / ArraySize(array); return (avg); } //Calculates the mode of an array //@param array [in] (array[]) that will be used for the calculation //@returns (double) double ArrayMode(const double& array[]) { double maxValue = -1.0; int maxCount = 0; for (int i = 0; i < ArraySize(array); i++) { double currentValue = array[i]; int currentCount = 1; for (int j = i + 1; j < ArraySize(array); j++) { if (array[j] == currentValue) { currentCount++; } } if (currentCount > maxCount) { maxCount = currentCount; maxValue = currentValue; } else if (currentCount == maxCount) { if (currentValue < maxValue) { maxValue = currentValue; } } } return (maxValue); } // Inputs input string separator = "========CALCULATION INPUTS========="; input int len = 20; // Lenght enum Type { type1 = 1, // AVG type2 = 2, // MEADIAN type3 = 3, // MODE }; input Type mode = type1; // Type input double distance = 1; // Distance input bool show_retest = false; // Retest Signals input string separator2 = "========STRONG SIGNAL SHAPES INPUTS========="; input ENUM_OBJECT objSell = OBJ_ARROW_DOWN; // sell signal shape (strong) input ENUM_OBJECT objBuy = OBJ_ARROW_UP; // buy signal shape (strong) input int sellSigSize = 3; // sell signal size (strong) input int buySigSize = 3; // buy signal size (strong) input color sellSigClr = clrWhite; // sell signal color (strong) input color buySigClr = clrWhite; // buy signal color (strong) input string separator3 = "========COMMON SIGNAL SHAPES INPUTS========="; input ENUM_OBJECT objSellcmn = OBJ_ARROW_DOWN; // sell signal shape input ENUM_OBJECT objBuycmn = OBJ_ARROW_UP; // buy signal shape input int sellSigSizecmn = 2; // sell signal size input int buySigSizecmn = 2; // buy signal size input color sellSigClrcmn = clrLightSalmon; // sell signal color input color buySigClrcmn = clrLightSeaGreen; // buy signal color input string separator4 = "========RETEST SIGNAL SHAPES INPUTS========="; input ENUM_OBJECT objSellrt = OBJ_ARROW_DOWN; // sell signal shape (retest) input ENUM_OBJECT objBuyrt = OBJ_ARROW_UP; // buy signal shape (retest) input int sellSigSizert = 1; // sell signal size (retest) input int buySigSizert = 1; // buy signal size (retest) input color sellSigClrrt = clrYellow; // sell signal color (retest) input color buySigClrrt = clrYellow; // buy signal color (retest) // Functions // Calculates a Gaussian filter for smoothing the data (uses close value as source) //@param length (int) Length of the filter //@param sigma (float) Standard deviation for the Gaussian function //@param shift (int) the index of the candle //@returns (float) Smoothed value for the current bar double Gaussian_filter(double lenght, double sigma, int shift) { double weights[100]; // Create an array to store weights for Gaussian filter double total = 0; // Sum of all weights, used for normalization double pi = M_PI; // Define Pi constant // Calculate weights for Gaussian filter for (int i = 0; i < lenght; i++) { double weight = MathExp(-0.5 * MathPow((i - lenght / 2) / sigma, 2.0)) / MathSqrt(sigma * 2.0 * pi); weights[i] = weight; total += weight; } // Normalize weights for (int i = 0; i < lenght; i++) { weights[i] = weights[i] / total; } // Apply Gaussian filter to the source series double sum = 0; for (int i = 0; i < lenght; i++) { sum = sum + Close[shift] * weights[i]; shift++; } return (sum); } // Multi-trend calculation using Gaussian filter //@param values [out] (array[][]) //@param period [in] (int) Lookback period for trend calculation //@param shift [in] (int) the index of the candle //@returns Void void Multi_trend(double& values[], double period, int shift, int arrShift) { int shifti = shift; double g_value[]; // Array to store Gaussian filtered values // Calculate the average true range (ATR) volatility double range[100]; for (int i = 0; i < 100; i++) { range[i] = High[shifti] - Low[shifti]; shifti++; } double volatility = ArrayAvg(range); double lower_band = 0; // Lower band for trend analysis double upper_band = 0; // Upper band for trend analysis double trend_line = 0; // Trend line value // Apply Gaussian filter with a step adjustment to calculate multiple trend lines for (int i = 0; i <= 20; i++) { double gaussian_filter = Gaussian_filter(period + i, 10, shift); ArrayResize(g_value, ArraySize(g_value) + 1); g_value[i] = gaussian_filter; } double coeff = 0.05; double score = 0.0; // Calculate score based on trend analysis for (int i = 0; i < ArraySize(g_value); i++) { double g_f = g_value[i]; if (g_f > g_value[0]) { score += coeff; } } // Determine value based on user-selected mode (AVG, MEDIAN, MODE) double value; switch (mode) { case 1: // AVG value = ArrayAvg(g_value); break; case 2: // Median value = ArrayMedian(g_value); break; case 3: // Mode value = ArrayMode(g_value); break; } lower_band = value - volatility * distance; // Calculate lower band based on value and volatility upper_band = value + volatility * distance; // Calculate upper band based on value and volatility lowerBand[arrShift] = lower_band; // save the band values upperBand[arrShift] = upper_band; // Check crossover and crossunder of price with bands to determine trend if (Close[shift + 1] < upperBand[arrShift - 1] && Close[shift] > upperBand[arrShift]) { trend = true; } else if (Close[shift + 1] > lowerBand[arrShift - 1] && Close[shift] < lowerBand[arrShift]) { trend = false; } // Set trend line based on trend direction if (trend) { trend_line = lower_band; } else if (!trend) { trend_line = upper_band; } values[0] = score; values[1] = value; values[2] = trend_line; } int OnInit() { //populate the arrays ArrayFill(lowerBand, 0, ArraySize(lowerBand), 0); ArrayFill(upperBand, 0, ArraySize(upperBand), 0); ArrayFill(trends, 0, ArraySize(trends), 0); ArrayFill(trend_line, 0, ArraySize(trend_line), 0); //Set the buffers IndicatorBuffers(3); SetIndexBuffer(0, avgLine); SetIndexBuffer(1, upperTrendline); SetIndexBuffer(2, LowerTrendline); int height = ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0); int width = ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0); //Create labels to show the strength ObjectCreate("StrengthUp", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "StrengthUp", OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSetInteger(0, "StrengthUp", OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(0, "StrengthUp", OBJPROP_FONTSIZE, 20); ObjectSetInteger(0, "StrengthUp", OBJPROP_XDISTANCE, width / 2); ObjectCreate("StrengthDown", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "StrengthDown", OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, "StrengthDown", OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(0, "StrengthDown", OBJPROP_FONTSIZE, 20); ObjectSetInteger(0, "StrengthDown", OBJPROP_XDISTANCE, width / 2); return (INIT_SUCCEEDED); } void OnDeinit(const int reason) { ObjectsDeleteAll(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[]) { ArraySetAsSeries(avgLine, false); ArraySetAsSeries(upperTrendline, false); ArraySetAsSeries(LowerTrendline, false); ArraySetAsSeries(time, false); ArraySetAsSeries(close, false); ArraySetAsSeries(high, false); ArraySetAsSeries(open, false); ArraySetAsSeries(low, false); int limit = prev_calculated; if (prev_calculated == 0) { limit = 101; } for (int i = limit; i < rates_total-1; i++) { int shift = iBarShift(_Symbol, PERIOD_CURRENT, time[i]); double values[4]; Multi_trend(values, len, shift, i); double avg = values[1]; double score = values[0]; trend_line[i] = values[2]; avgLine[i] = avg; if (trend) { upperTrendline[i] = EMPTY_VALUE; LowerTrendline[i] = trend_line[i]; } else if (!trend) { LowerTrendline[i] = EMPTY_VALUE; upperTrendline[i] = trend_line[i]; } trends[i] = trend; if (trends[i - 1] != trends[i]) // Check change in trend { string obName = time[i]; if (trends[i]) { // Buy signal if (score > 0.5) { // Strong ObjectCreate(obName, objBuy, 0, time[i], low[i]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger(0, obName, OBJPROP_WIDTH, buySigSize); ObjectSetInteger(0, obName, OBJPROP_COLOR, buySigClr); } else if (score < 0.5) { // Common ObjectCreate(obName, objBuycmn, 0, time[i], low[i]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger(0, obName, OBJPROP_WIDTH, buySigSizecmn); ObjectSetInteger(0, obName, OBJPROP_COLOR, buySigClrcmn); } } else if (!trends[i]) { // Sell signal if (score < 0.5) { // Strong ObjectCreate(obName, objSell, 0, time[i], high[i]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_BOTTOM); ObjectSetInteger(0, obName, OBJPROP_WIDTH, sellSigSize); ObjectSetInteger(0, obName, OBJPROP_COLOR, sellSigClr); } else if (score > 0.5) { // Common ObjectCreate(obName, objSellcmn, 0, time[i], high[i]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_BOTTOM); ObjectSetInteger(0, obName, OBJPROP_WIDTH, sellSigSizecmn); ObjectSetInteger(0, obName, OBJPROP_COLOR, sellSigClrcmn); } } } if (show_retest) { if (high[i - 1] > avgLine[i - 1] && high[i] < avgLine[i] && !trend) { // Sell retest string obName = time[i] + "retest"; ObjectCreate(obName, objSellrt, 0, time[i - 1], high[i - 1]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_BOTTOM); ObjectSetInteger(0, obName, OBJPROP_WIDTH, sellSigSizert); ObjectSetInteger(0, obName, OBJPROP_COLOR, sellSigClrrt); } if (close[i - 1] < avgLine[i - 1] && close[i] > avgLine[i] && trend) { // Buy retest string obName = time[i] + "retest"; ObjectCreate(obName, objBuyrt, 0, time[i], low[i]); ObjectSetInteger(0, obName, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger(0, obName, OBJPROP_WIDTH, buySigSizert); ObjectSetInteger(0, obName, OBJPROP_COLOR, buySigClrrt); } } // Display strength double score_up = (score - 1) * -1; double score_dn = 1 - score_up; if (score_up < 0) { score_up = 0; } ObjectSetString(0, "StrengthUp", OBJPROP_TEXT, "UP Strength " + score_up * 100 + "%"); ObjectSetString(0, "StrengthDown", OBJPROP_TEXT, "DOWN strength " + score_dn * 100 + "%"); } return (rates_total-1); }