TCJ Trend Arrow (Non-Repaint) MetaTrader 5 Indicator

The TCJ Trend Arrow (Non-Repaint) indicator is designed to visually highlight potential market trends by plotting arrows directly on the price chart. This indicator utilizes specific conditions to determine whether a trend is likely to move up or down and marks those moments with distinct arrows. While it does not repaint, ensuring that once an arrow is placed, it stays in its position, the tool is intended to help traders visualize trends based on predefined criteria without altering past signals.

#property indicator_chart_window
// Number of indicator buffers
#property indicator_buffers 2
// Two graphical plots used
#property indicator_plots   2

// +----------------------------------------------+
// |  Bullish Indicator Drawing Parameters        |
// +----------------------------------------------+
// Draw the indicator as an arrow
#property indicator_type1 DRAW_ARROW
// Indicator color is set to blue
#property indicator_color1 clrBlue
// Solid line style
#property indicator_style1 STYLE_SOLID
// Line thickness is set to 2
#property indicator_width1 2
// Label for the signal line
#property indicator_label1 "Buy trend_arrows signal"

// +----------------------------------------------+
// |  Bearish Indicator Drawing Parameters        |
// +----------------------------------------------+
// Draw the indicator as an arrow
#property indicator_type2 DRAW_ARROW
// Indicator color is set to red
#property indicator_color2 clrRed
// Solid line style
#property indicator_style2 STYLE_SOLID
// Line thickness is set to 2
#property indicator_width2 2
// Label for the signal line
#property indicator_label2 "Sell trend_arrows signal"

// +----------------------------------------------+
// |  Constant Definitions                        |
// +----------------------------------------------+
#define RESET  0 // Constant to return a command to the terminal for indicator recalculation

// +----------------------------------------------+
// |  Indicator Input Parameters                  |
// +----------------------------------------------+
input uint iPeriod = 15;          // Indicator period
input uint iFullPeriods = 1;
input int Shift = 0;              // Horizontal shift of the indicator in bars

// +----------------------------------------------+
// |  Dynamic Arrays Declaration                  |
// |  These will be used as indicator buffers     |
// +----------------------------------------------+
double SignUp[], SignDown[];
int arr[];
bool boolp1;
int ATR_Handle, min_rates_total;

// +------------------------------------------------------------------+
// | Custom Indicator Initialization Function                         |
// +------------------------------------------------------------------+
int OnInit()
{
    // Initialize variables for data counting
    min_rates_total = int(iPeriod + iFullPeriods);
    int ATR_Period = 15;
    min_rates_total = MathMax(min_rates_total, ATR_Period);

    // Get the handle of the ATR indicator
    ATR_Handle = iATR(NULL, 0, ATR_Period);
    if (ATR_Handle == INVALID_HANDLE)
    {
        Print("Failed to get the handle of the ATR indicator");
        return(INIT_FAILED);
    }

    // Allocate memory for arrays
    ArrayResize(arr, min_rates_total);

    // Initialize a variable for the short name of the indicator
    string shortname;
    StringConcatenate(shortname, "trend_arrows(", string(iPeriod), ", ", string(iFullPeriods), ", ", string(Shift), ")");
    // Create a name for display in a separate window and tooltip
    IndicatorSetString(INDICATOR_SHORTNAME, shortname);
    // Set the precision of indicator values
    IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

    // Convert dynamic array to indicator buffer
    SetIndexBuffer(0, SignUp, INDICATOR_DATA);
    // Shift the indicator 1 horizontally by Shift
    PlotIndexSetInteger(0, PLOT_SHIFT, Shift);
    // Shift the starting point of indicator 1 drawing
    PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 3 * min_rates_total);
    // Index elements in buffers as time series
    ArraySetAsSeries(SignUp, true);
    // Set values for the indicator that will not be visible on the chart
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
    // Set the symbol for the indicator
    PlotIndexSetInteger(0, PLOT_ARROW, 233);

    // Convert dynamic array to indicator buffer
    SetIndexBuffer(1, SignDown, INDICATOR_DATA);
    // Shift the indicator 2 horizontally by Shift
    PlotIndexSetInteger(1, PLOT_SHIFT, Shift);
    // Shift the starting point of indicator 2 drawing
    PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 3 * min_rates_total);
    // Index elements in buffers as time series
    ArraySetAsSeries(SignDown, true);
    // Set values for the indicator that will not be visible on the chart
    PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
    // Set the symbol for the indicator
    PlotIndexSetInteger(1, PLOT_ARROW, 234);

    return(INIT_SUCCEEDED);
}

// +------------------------------------------------------------------+
// | Custom Indicator Iteration Function                              |
// +------------------------------------------------------------------+
int OnCalculate(const int rates_total,    // Number of bars in the history on the current tick
                const int prev_calculated,// Number of bars in the history on the previous tick
                const datetime &time[],
                const double &open[],
                const double &high[],     // Price array of high values for indicator calculation
                const double &low[],      // Price array of low values for indicator calculation
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    // Check if the number of bars is sufficient for calculation
    if (BarsCalculated(ATR_Handle) < rates_total || rates_total < min_rates_total) return(RESET);

    int limit, bar, to_copy;
    double ATR[], TrendUp, TrendDown;
    static double TrendUp_prev, TrendDown_prev;

    // Index elements in arrays as time series
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(ATR, true);

    // Calculate the starting number for the loop recalculating bars
    if (prev_calculated > rates_total || prev_calculated <= 0) // Check if this is the first calculation start
    {
        limit = rates_total - min_rates_total - 1;               // Starting number for calculating all bars

        int tms1 = rates_total - 2 - min_rates_total;
        while (!isDelimeter(Period(), time, tms1)) tms1--;
        boolp1 = tms1;
        tms1--;
        for (int rrr = 0; rrr < int(iPeriod); rrr++)
        {
            while (!isDelimeter(Period(), time, tms1)) tms1--;
            tms1--;
        }
        tms1++;
        limit = tms1;
    }
    else
    {
        limit = rates_total - prev_calculated;                 // Starting number for calculating new bars
    }
    to_copy = limit + 1;

    // Copy newly appeared data into the ATR[] array
    if (CopyBuffer(ATR_Handle, 0, 0, to_copy, ATR) <= 0) return(RESET);

    // Main loop for indicator calculation
    for (bar = limit; bar >= 0 && !IsStopped(); bar--)
    {
        TrendUp = 0.0;
        TrendDown = 0.0;
        SignUp[bar] = 0.0;
        SignDown[bar] = 0.0;
        double HH = AverageHigh(high, time, bar);
        double LL = AverageLow(low, time, bar);

        if (close[bar] > HH) TrendUp = LL;
        else
        {
            if (close[bar] < LL) TrendDown = HH;
            else
            {
                if (TrendDown_prev) TrendDown = HH;
                if (TrendUp_prev) TrendUp = LL;
            }
        }

        if (!TrendUp_prev && TrendUp) SignUp[bar] = low[bar] - ATR[bar] * 3 / 8;
        if (!TrendDown_prev && TrendDown) SignDown[bar] = high[bar] + ATR[bar] * 3 / 8;

        if (bar)
        {
            TrendUp_prev = TrendUp;
            TrendDown_prev = TrendDown;
        }
    }

    return(rates_total);
}

// +------------------------------------------------------------------+
// | AverageHigh Function                                             |
// +------------------------------------------------------------------+
double AverageHigh(const double &High[], const datetime &Time[], int index)
{
    double hhv;
    double ret = 0.0;
    int nbars = index;
    int max = int(iPeriod + iFullPeriods);
    boolp1 = false;
    for (int iii = 0; iii < max; iii++)
    {
        while (!isDelimeter(Period(), Time, nbars)) nbars++;
        if (!boolp1) boolp1 = nbars;
        arr[iii] = nbars;
        nbars++;
    }

    for (int count = int(iPeriod - 1); count > 0; count--)
    {
        hhv = High[ArrayMaximum(High, arr[count - 1] + 1, arr[count] - arr[count - 1])];
        ret += hhv;
    }
    if (iFullPeriods == 1)
    {
        hhv = High[ArrayMaximum(High, arr[iPeriod - 1] + 1, arr[iPeriod] - arr[iPeriod - 1])];
        ret += hhv;
        ret /= NormalizeDouble(iPeriod, 0);
    }
    else
    {
        ret /= NormalizeDouble(iPeriod - 1, 0);
    }
    return(ret);
}

// +------------------------------------------------------------------+
// | AverageLow Function                                              |
// +------------------------------------------------------------------+
double AverageLow(const double &Low[], const datetime &Time[], int index)
{
    double llv;
    double ret = 0.0;
    int nbars = index;
    int max = int(iPeriod + iFullPeriods);
    for (int iii = 0; iii < max; iii++)
    {
        while (!isDelimeter(Period(), Time, nbars)) nbars++;
        arr[iii] = nbars;
        nbars++;
    }

    for (int count = int(iPeriod - 1); count > 0; count--)
    {
        llv = Low[ArrayMinimum(Low, arr[count - 1] + 1, arr[count] - arr[count - 1])];
        ret += llv;
    }

    if (iFullPeriods == 1)
    {
        llv = Low[ArrayMinimum(Low, arr[iPeriod - 1] + 1, arr[iPeriod] - arr[iPeriod - 1])];
        ret += llv;
        ret /= NormalizeDouble(iPeriod, 0);
    }
    else
    {
        ret /= NormalizeDouble(iPeriod - 1, 0);
    }
    return(ret);
}

// +------------------------------------------------------------------+
// | isDelimeter Function                                             |
// +------------------------------------------------------------------+
bool isDelimeter(int TF, const datetime &Time[], int bar)
{
    int t1, t2;
    switch (TF)
    {
        case 1:
            t1 = TimeMinute(Time[bar]);
            t2 = TimeMinute(Time[bar + 1]);
            if (t1 != t2) return(true);
            break;

        case 5:
        case 15:
        case 30:
            t1 = TimeHour(Time[bar]) * 60 + TimeMinute(Time[bar]);
            t2 = TimeHour(Time[bar + 1]) * 60 + TimeMinute(Time[bar + 1]);
            if (t1 / TF != t2 / TF) return(true);
            break;

        case 60:
            t1 = TimeHour(Time[bar]);
            t2 = TimeHour(Time[bar + 1]);
            if (t1 != t2) return(true);
            break;

        case 240:
            t1 = TimeHour(Time[bar]);
            t2 = TimeHour(Time[bar + 1]);
            if (t1 / 4 != t2 / 4) return(true);
            break;

        case 1440:
            t1 = TimeDay(Time[bar]);
            t2 = TimeDay(Time[bar + 1]);
            if (t1 != t2) return(true);
            break;

        case 10080:
            t1 = TimeDayOfYear(Time[bar]);
            t2 = TimeDayOfYear(Time[bar + 1]);
            if (t1 / 7 != t2 / 7) return(true);
            break;

        case 43200:
            t1 = TimeMonth(Time[bar]);
            t2 = TimeMonth(Time[bar + 1]);
            if (t1 != t2) return(true);
            break;
    }
    return(false);
}

What is the Trend Arrows Indicator?

The Trend Arrows Indicator is a custom-built tool that signals potential buy and sell points on a trading chart. It achieves this by analyzing past price data and plotting arrows on the chart to represent these signals. The indicator works by comparing current price movements against calculated thresholds to determine when to signal a potential trend change.

See also  Zippor AMA v1.0 (Non-Lag) MetaTrader 5 Indicator

How Does It Work?

Indicator Initialization

When the indicator is first initialized, it sets up the environment in which it will operate. This involves several steps, such as allocating memory for data storage, setting up how the indicator will appear on the chart, and defining the conditions under which it will operate.

Setting Up Buffers and Arrays

The indicator relies on buffers to store data for calculations and graphical plotting. Two primary buffers are used here: one for buy signals and another for sell signals. These buffers store the calculated values that will later be displayed on the chart as arrows.

Calculation Logic

The core functionality of the Trend Arrows Indicator revolves around its ability to identify market trends. This is done by comparing the current price data with the average high and average low over a specified period. Here’s a breakdown of how this works:

Average High and Average Low

  • Average High: The indicator calculates the average of the highest prices within a certain period. This value helps to identify when the price is reaching levels that might indicate an uptrend.
  • Average Low: Similarly, the indicator calculates the average of the lowest prices. This value is crucial for identifying potential downtrends.

Signal Generation

Once the average high and low values are calculated, the indicator checks the current price against these values. If the current price exceeds the average high, it may signal a buy trend by plotting an arrow on the chart. Conversely, if the price drops below the average low, a sell trend might be indicated.

See also  Hybrid Vision FX Indicator (Non-Repaint) MetaTrader 5 Indicator

Delimiters and Timeframes

The indicator also includes logic to determine whether a new time period has started. This is important because it ensures that the indicator doesn’t mistakenly calculate signals based on incomplete data. By checking if a delimiter (a boundary between two time periods) has been crossed, the indicator accurately places its signals.

Indicator Limitations

While the Trend Arrows Indicator can be a useful tool, it has some inherent limitations. Understanding these can help traders use the indicator more effectively.

Repainting

One of the most common issues with some indicators is repainting, where past signals change after new data is received. Fortunately, this indicator does not repaint, meaning once a signal is placed, it stays fixed on the chart. This provides a level of reliability in analyzing past performance.

Lagging

This indicator does exhibit lag. Lag occurs when there is a delay in the indicator’s response to market movements. This is because the indicator waits for confirmation before placing signals, which can sometimes mean that the signals appear slightly after the ideal entry or exit point.

Recalculation

Another factor to consider is that the indicator may recalculate under certain conditions. Recalculation can result in signals changing slightly after they have been plotted. While this doesn’t happen often, it’s important to be aware that some recalculations may occur based on new data or adjustments in the chart.

Conclusion

The Trend Arrows Indicator is a straightforward tool that helps visualize potential market trends by plotting arrows on a chart. It leverages historical price data to identify points where a trend might be forming or reversing. While it has some limitations, such as lag and occasional recalculations, it does not repaint, making it a consistent option for those who prefer fixed signals on their charts.

See also  Bizzwill Adx Cross Arrow (Non-Repaint) MetaTrader 5 Indicator

Understanding how this indicator works, including its strengths and weaknesses, can help traders use it more effectively. Remember, the effectiveness of any indicator depends on how well it fits into your overall trading strategy.

Leave a Comment