The Doom TMA Band + Arrow (Non-Repaint) is a custom indicator designed to plot Triangular Moving Averages (TMA) on a chart along with upper and lower bands based on these averages. It includes visual markers in the form of arrows that appear when specific conditions involving price movements relative to these bands are met. The indicator is structured to be non-repainting, meaning once an arrow or line is drawn, it remains fixed, reflecting the historical data without recalculating based on new information.
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_color1 White
#property indicator_color2 OrangeRed
#property indicator_color3 OrangeRed
#property indicator_color4 White
#property indicator_color5 White
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_width2 6
#property indicator_width3 6
#property indicator_width4 8
#property indicator_width5 8
//+------------------------------------------------------------------+
//| Input Parameters |
//+------------------------------------------------------------------+
extern string TimeFrame = "current time frame"; // Time frame setting
extern int HalfLength = 55; // Half length of TMA calculation
extern int Price = PRICE_WEIGHTED; // Price type to be used in calculations
extern double BandsDeviations = 2.5; // Standard deviation multiplier for bands
extern bool Interpolate = true; // Interpolation switch
extern bool alertsOn = false; // Alerts on/off switch
extern bool alertsOnCurrent = false; // Alerts on the current bar switch
extern bool alertsOnHighLow = false; // Alerts on high/low switch
extern bool alertsMessage = false; // Message alert switch
extern bool alertsSound = false; // Sound alert switch
extern bool alertsEmail = false; // Email alert switch
//+------------------------------------------------------------------+
//| Indicator Buffers |
//+------------------------------------------------------------------+
double tmBuffer[]; // TMA Buffer
double upBuffer[]; // Upper Band Buffer
double dnBuffer[]; // Lower Band Buffer
double wuBuffer[]; // Weighted Upper Band Buffer
double wdBuffer[]; // Weighted Lower Band Buffer
double upArrow[]; // Up Arrow Buffer
double dnArrow[]; // Down Arrow Buffer
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
string IndicatorFileName;
bool calculatingTma = false;
bool returningBars = false;
int timeFrame;
//+------------------------------------------------------------------+
//| Initialize Indicator |
//+------------------------------------------------------------------+
int init() {
// Convert time frame string to integer format
timeFrame = stringToTimeFrame(TimeFrame);
HalfLength = MathMax(HalfLength, 1); // Ensure HalfLength is at least 1
// Set up indicator buffers and drawing styles
IndicatorBuffers(7);
SetIndexBuffer(0, tmBuffer); SetIndexDrawBegin(0, HalfLength);
SetIndexBuffer(1, upBuffer); SetIndexDrawBegin(1, HalfLength);
SetIndexBuffer(2, dnBuffer); SetIndexDrawBegin(2, HalfLength);
SetIndexBuffer(3, dnArrow); SetIndexStyle(3, DRAW_ARROW); SetIndexArrow(3, 82);
SetIndexBuffer(4, upArrow); SetIndexStyle(4, DRAW_ARROW); SetIndexArrow(4, 82);
SetIndexBuffer(5, wuBuffer);
SetIndexBuffer(6, wdBuffer);
// Check for special modes
if (TimeFrame == "calculateTma") {
calculatingTma = true;
return(0);
}
if (TimeFrame == "returnBars") {
returningBars = true;
return(0);
}
// Get the name of the indicator file
IndicatorFileName = WindowExpertName();
return(0);
}
//+------------------------------------------------------------------+
//| De-initialize Indicator |
//+------------------------------------------------------------------+
int deinit() {
return(0);
}
//+------------------------------------------------------------------+
//| Start Indicator Calculation |
//+------------------------------------------------------------------+
int start() {
int counted_bars = IndicatorCounted(); // Number of bars that have been counted
int limit;
// Exit if an error occurred
if (counted_bars < 0) return(-1);
if (counted_bars > 0) counted_bars--;
// Set the limit for bars to calculate
limit = MathMin(Bars-1, Bars - counted_bars + HalfLength);
// Return the number of bars if in returningBars mode
if (returningBars) {
tmBuffer[0] = limit;
return(0);
}
// Calculate TMA if in calculatingTma mode
if (calculatingTma) {
calculateTma(limit);
return(0);
}
// Handle multi-timeframe calculations
if (timeFrame > Period()) {
limit = MathMax(limit, MathMin(Bars-1, iCustom(NULL, timeFrame, IndicatorFileName, "returnBars", 0, 0) * timeFrame / Period()));
}
// Calculate indicator values for each bar
for (int i = limit; i >= 0; i--) {
int shift1 = iBarShift(NULL, timeFrame, Time[i]);
datetime time1 = iTime(NULL, timeFrame, shift1);
// Get TMA and bands values from custom indicator
tmBuffer[i] = iCustom(NULL, timeFrame, IndicatorFileName, "calculateTma", HalfLength, Price, BandsDeviations, 0, shift1);
upBuffer[i] = iCustom(NULL, timeFrame, IndicatorFileName, "calculateTma", HalfLength, Price, BandsDeviations, 1, shift1);
dnBuffer[i] = iCustom(NULL, timeFrame, IndicatorFileName, "calculateTma", HalfLength, Price, BandsDeviations, 2, shift1);
// Initialize arrow buffers
upArrow[i] = EMPTY_VALUE;
dnArrow[i] = EMPTY_VALUE;
// Determine arrow conditions
if (High[i+1] > upBuffer[i+1] && Close[i+1] > Open[i+1] && Close[i] < Open[i]) upArrow[i] = High[i];
if (Low[i+1] < dnBuffer[i+1] && Close[i+1] < Open[i+1] && Close[i] > Open[i]) dnArrow[i] = Low[i];
// Handle interpolation between bars
if (timeFrame <= Period() || shift1 == iBarShift(NULL, timeFrame, Time[i-1])) continue;
if (!Interpolate) continue;
int n = 1;
while (i+n < Bars && Time[i+n] >= time1) n++;
double factor = 1.0 / n;
for (int k = 1; k < n; k++) {
tmBuffer[i+k] = k * factor * tmBuffer[i+n] + (1.0 - k * factor) * tmBuffer[i];
upBuffer[i+k] = k * factor * upBuffer[i+n] + (1.0 - k * factor) * upBuffer[i];
dnBuffer[i+k] = k * factor * dnBuffer[i+n] + (1.0 - k * factor) * dnBuffer[i];
}
}
// Handle alerts
if (alertsOn) {
int forBar = alertsOnCurrent ? 0 : 1;
// Check for alert conditions
if (alertsOnHighLow) {
if (High[forBar] > upBuffer[forBar] && High[forBar+1] < upBuffer[forBar+1]) doAlert("high penetrated upper bar");
if (Low[forBar] < dnBuffer[forBar] && Low[forBar+1] > dnBuffer[forBar+1]) doAlert("low penetrated lower bar");
} else {
if (Close[forBar] > upBuffer[forBar] && Close[forBar+1] < upBuffer[forBar+1]) doAlert("close penetrated upper bar");
if (Close[forBar] < dnBuffer[forBar] && Close[forBar+1] > dnBuffer[forBar+1]) doAlert("close penetrated lower bar");
}
}
return(0);
}
//+------------------------------------------------------------------+
//| TMA Calculation Function |
//+------------------------------------------------------------------+
void calculateTma(int limit) {
int i, j, k;
double FullLength = 2.0 * HalfLength + 1.0;
// Loop through bars to calculate TMA
for (i = limit; i >= 0; i--) {
double sum = (HalfLength + 1) * iMA(NULL, 0, 1, 0, MODE_SMA, Price, i);
double sumw = (HalfLength + 1);
// Calculate weighted moving average
for (j = 1, k = HalfLength; j <= HalfLength; j++, k--) {
sum += k * iMA(NULL, 0, 1, 0, MODE_SMA, Price, i+j);
sumw += k;
if (j <= i) {
sum += k * iMA(NULL, 0, 1, 0, MODE_SMA, Price, i-j);
sumw += k;
}
}
tmBuffer[i] = sum / sumw;
// Calculate upper and lower bands
double diff = iMA(NULL, 0, 1, 0, MODE_SMA, Price, i) - tmBuffer[i];
wuBuffer[i] = diff;
wdBuffer[i] = diff;
upBuffer[i] = tmBuffer[i] + BandsDeviations * StdDev(wuBuffer, i, FullLength);
dnBuffer[i] = tmBuffer[i] - BandsDeviations * StdDev(wdBuffer, i, FullLength);
}
}
//+------------------------------------------------------------------+
//| Alert Function |
//+------------------------------------------------------------------+
void doAlert(string msg) {
if (alertsMessage) Alert(Symbol(), " ", Period(), ": ", msg);
if (alertsSound) PlaySound("alert2.wav");
if (alertsEmail) SendMail(Symbol() + " " + Period(), msg);
}
//+------------------------------------------------------------------+
//| Time Frame Conversion Function |
//+------------------------------------------------------------------+
int stringToTimeFrame(string tf) {
if (tf == "current time frame") return Period();
if (tf == "M1") return PERIOD_M1;
if (tf == "M5") return PERIOD_M5;
if (tf == "M15") return PERIOD_M15;
if (tf == "M30") return PERIOD_M30;
if (tf == "H1") return PERIOD_H1;
if (tf == "H4") return PERIOD_H4;
if (tf == "D1") return PERIOD_D1;
if (tf == "W1") return PERIOD_W1;
if (tf == "MN") return PERIOD_MN1;
return Period(); // Default to current time frame if unrecognized
}
//+------------------------------------------------------------------+
What is the Indicator Designed to Do?
This custom indicator is designed to calculate and display Triangular Moving Averages and their associated upper and lower bands. In addition to this, it plots arrows on the chart whenever specific conditions are met. These conditions relate to price movements concerning the upper and lower bands. The goal of these elements is to provide traders with visual cues about potential price movements, which could be of interest depending on their trading strategy.
The Core Components of the Indicator
Triangular Moving Average (TMA):
The TMA is a type of moving average that applies a weighted calculation to smooth out the price data. Unlike simple moving averages, where all prices are weighted equally, TMA gives more weight to prices in the middle of the data set. This results in a smoother line, which can help identify trends without the noise of short-term fluctuations.
Upper and Lower Bands:
The upper and lower bands are calculated using the TMA as a baseline. These bands are offset from the TMA by a certain number of standard deviations (as specified by the BandsDeviations parameter). These bands can help identify overbought or oversold conditions depending on where the price is relative to them.
Key Parameters Explained
TimeFrame
This parameter allows you to set the time frame for which the indicator is calculated. You can apply it to the current time frame of the chart or choose a different one. This flexibility is useful when analyzing trends over multiple time periods.
HalfLength
The HalfLength parameter is central to the TMA calculation. It defines how many bars (or periods) are considered when calculating the average. The name “HalfLength” comes from the way TMA is calculated, where the length of the moving average is effectively doubled, making this value half of the total length.
Price
The Price parameter determines which price data is used for the calculations. This could be the closing price, opening price, or other price types like the weighted price. The flexibility to choose different price data allows for tailored analysis based on your trading strategy.
BandsDeviations
This parameter is key in determining how far the upper and lower bands are from the TMA. By adjusting the BandsDeviations, you can control the sensitivity of the bands—smaller deviations mean tighter bands, while larger deviations mean wider bands.
Visual Elements: Arrows on the Chart
Up and Down Arrows
One of the key visual features of this indicator is the plotting of arrows on the chart. These arrows are not random; they appear based on specific conditions involving price and the bands:
- Up Arrow: This appears when the price has moved above the upper band and then reverses direction, closing below the open price on the next bar. It’s a signal that the price might be pulling back after hitting a potential high.
- Down Arrow: This appears when the price has moved below the lower band and then reverses direction, closing above the open price on the next bar. It’s a signal that the price might be bouncing back after hitting a potential low.
Handling Different Time Frames
The indicator can adapt to different time frames, which is crucial for traders who want to see how the TMA and bands behave over varying periods. The code is designed to handle these changes efficiently, ensuring that the visual elements and calculations remain accurate regardless of the time frame selected.
Alerts: Notifications Based on Specific Conditions
Although alerts are a more minor feature in this context, they still play a role. The indicator can generate alerts when certain conditions are met, such as the price crossing above or below the bands. These alerts can be sent as messages, sounds, or even emails, depending on the user’s preference. This feature is particularly useful for those who don’t want to constantly monitor their charts but still want to be informed of key movements.
Conclusion
This custom indicator is all about visualizing price movements relative to the Triangular Moving Averages and their associated bands. The arrows provide quick visual cues about potential market reversals, which can be useful for decision-making. However, it’s important to remember that indicators are tools and should be used in conjunction with other analysis methods.