264 lines
12 KiB
MQL5
264 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| CustomRectangles.mq5 |
|
|
//| Copyright 2025, Rahul Dhangar |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025"
|
|
#property link "https://www.rahuldhangar.com"
|
|
#property version "1.00"
|
|
#property indicator_chart_window
|
|
#property indicator_plots 0
|
|
|
|
// Input parameters
|
|
input int DaysToShow = 20; // Number of Periods to Show
|
|
input ENUM_TIMEFRAMES AggTFInput = PERIOD_D1; // Aggregation timeframe
|
|
input int ShiftBars = 0; // Shift (bars)
|
|
input int ATRLen = 14; // ATR Length
|
|
|
|
// Color inputs
|
|
input color ColText = clrBlack;
|
|
input color ColRed = clrRed;
|
|
input color ColGreen = clrLime;
|
|
input color ColBodyBull = clrWhite;
|
|
input color ColBodyBear = clrBlack;
|
|
|
|
// Structure to store rectangle data
|
|
struct DayRectangles {
|
|
string redRect;
|
|
string greenRect;
|
|
string bodyRect;
|
|
string infoLabel; // text above red rectangle
|
|
string atrLabel; // text below green rectangle
|
|
};
|
|
|
|
// Array to store rectangle names
|
|
DayRectangles rectangles[];
|
|
int rectangleCount = 0;
|
|
|
|
// Compute ATR at a given index on series arrays (index 0 = most recent)
|
|
double CalcATRAt(const double &h[], const double &l[], const double &c[], int idx, int len)
|
|
{
|
|
int sz = ArraySize(h);
|
|
if(idx + len >= sz) return(0.0);
|
|
double sum = 0.0;
|
|
for(int j = idx; j < idx + len; j++)
|
|
{
|
|
double tr1 = h[j] - l[j];
|
|
double tr2 = MathAbs(h[j] - c[j+1]);
|
|
double tr3 = MathAbs(l[j] - c[j+1]);
|
|
double tr = MathMax(tr1, MathMax(tr2, tr3));
|
|
sum += tr;
|
|
}
|
|
return(sum / len);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
ArrayResize(rectangles, DaysToShow);
|
|
rectangleCount = 0;
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Delete all rectangles
|
|
for(int i = 0; i < rectangleCount; i++)
|
|
{
|
|
if(rectangles[i].redRect != "")
|
|
ObjectDelete(0, rectangles[i].redRect);
|
|
if(rectangles[i].greenRect != "")
|
|
ObjectDelete(0, rectangles[i].greenRect);
|
|
if(rectangles[i].bodyRect != "")
|
|
ObjectDelete(0, rectangles[i].bodyRect);
|
|
if(rectangles[i].infoLabel != "")
|
|
ObjectDelete(0, rectangles[i].infoLabel);
|
|
if(rectangles[i].atrLabel != "")
|
|
ObjectDelete(0, rectangles[i].atrLabel);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
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[])
|
|
{
|
|
// Remove previously drawn rectangles so we can re-draw the last N days
|
|
// Clear existing rectangles
|
|
for(int i = 0; i < rectangleCount; i++)
|
|
{
|
|
if(rectangles[i].redRect != "")
|
|
ObjectDelete(0, rectangles[i].redRect);
|
|
if(rectangles[i].greenRect != "")
|
|
ObjectDelete(0, rectangles[i].greenRect);
|
|
if(rectangles[i].bodyRect != "")
|
|
ObjectDelete(0, rectangles[i].bodyRect);
|
|
}
|
|
rectangleCount = 0;
|
|
|
|
// Ensure storage can hold current count
|
|
ArrayResize(rectangles, DaysToShow);
|
|
|
|
// Determine effective aggregation timeframe (use input only if higher than chart TF)
|
|
ENUM_TIMEFRAMES chartTF = (ENUM_TIMEFRAMES)Period();
|
|
ENUM_TIMEFRAMES aggTF = (PeriodSeconds(AggTFInput) > PeriodSeconds(chartTF)) ? AggTFInput : chartTF;
|
|
|
|
// Fetch aggregated timeframe data (extra bars for ATR calculation)
|
|
int barsToFetch = DaysToShow + ShiftBars + ATRLen + 2;
|
|
double o[], h[], l[], c[];
|
|
datetime t[];
|
|
|
|
if(CopyTime(_Symbol, aggTF, 0, barsToFetch, t) <= 0) return(0);
|
|
if(CopyOpen(_Symbol, aggTF, 0, barsToFetch, o) <= 0) return(0);
|
|
if(CopyHigh(_Symbol, aggTF, 0, barsToFetch, h) <= 0) return(0);
|
|
if(CopyLow(_Symbol, aggTF, 0, barsToFetch, l) <= 0) return(0);
|
|
if(CopyClose(_Symbol, aggTF, 0, barsToFetch, c) <= 0) return(0);
|
|
|
|
ArraySetAsSeries(t, true);
|
|
ArraySetAsSeries(o, true);
|
|
ArraySetAsSeries(h, true);
|
|
ArraySetAsSeries(l, true);
|
|
ArraySetAsSeries(c, true);
|
|
|
|
// Create rectangles for each aggregated bar
|
|
int built = 0;
|
|
for(int i = 1 + ShiftBars; built < DaysToShow && i < ArraySize(t); i++)
|
|
{
|
|
if(i - 1 >= ArraySize(t)) break;
|
|
datetime dayStart = t[i];
|
|
datetime dayEnd = t[i-1];
|
|
|
|
double dOpen = o[i];
|
|
double dHigh = h[i];
|
|
double dLow = l[i];
|
|
double dClose = c[i];
|
|
|
|
bool isBullish = (dClose > dOpen);
|
|
|
|
// Create unique names for rectangles
|
|
string idxStr = IntegerToString(i);
|
|
string prefix = "DayRect_" + idxStr + "_";
|
|
|
|
if(isBullish)
|
|
{
|
|
// Bullish day: Close > Open
|
|
|
|
// Red rectangle: Closing price to High
|
|
string redName = prefix + "Red";
|
|
ObjectCreate(0, redName, OBJ_RECTANGLE, 0, dayStart, dClose, dayEnd, dHigh);
|
|
ObjectSetInteger(0, redName, OBJPROP_COLOR, ColRed);
|
|
ObjectSetInteger(0, redName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, redName, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, redName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, redName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, redName, OBJPROP_BGCOLOR, ColorToARGB(ColRed, 25)); // ~90% transparent
|
|
rectangles[rectangleCount].redRect = redName;
|
|
|
|
// Green rectangle: Opening price to Low
|
|
string greenName = prefix + "Green";
|
|
ObjectCreate(0, greenName, OBJ_RECTANGLE, 0, dayStart, dLow, dayEnd, dOpen);
|
|
ObjectSetInteger(0, greenName, OBJPROP_COLOR, ColGreen);
|
|
ObjectSetInteger(0, greenName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, greenName, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, greenName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, greenName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, greenName, OBJPROP_BGCOLOR, ColorToARGB(ColGreen, 25));
|
|
rectangles[rectangleCount].greenRect = greenName;
|
|
|
|
// White rectangle: Opening to Closing price (body)
|
|
string bodyName = prefix + "Body";
|
|
ObjectCreate(0, bodyName, OBJ_RECTANGLE, 0, dayStart, dOpen, dayEnd, dClose);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_COLOR, ColBodyBull);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_WIDTH, 2);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_BGCOLOR, ColorToARGB(ColBodyBull, 25));
|
|
rectangles[rectangleCount].bodyRect = bodyName;
|
|
}
|
|
else
|
|
{
|
|
// Bearish day: Close <= Open
|
|
|
|
// Red rectangle: Opening price to High
|
|
string redName = prefix + "Red";
|
|
ObjectCreate(0, redName, OBJ_RECTANGLE, 0, dayStart, dOpen, dayEnd, dHigh);
|
|
ObjectSetInteger(0, redName, OBJPROP_COLOR, ColRed);
|
|
ObjectSetInteger(0, redName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, redName, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, redName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, redName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, redName, OBJPROP_BGCOLOR, ColorToARGB(ColRed, 25));
|
|
rectangles[rectangleCount].redRect = redName;
|
|
|
|
// Green rectangle: Closing price to Low
|
|
string greenName = prefix + "Green";
|
|
ObjectCreate(0, greenName, OBJ_RECTANGLE, 0, dayStart, dLow, dayEnd, dClose);
|
|
ObjectSetInteger(0, greenName, OBJPROP_COLOR, ColGreen);
|
|
ObjectSetInteger(0, greenName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, greenName, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, greenName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, greenName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, greenName, OBJPROP_BGCOLOR, ColorToARGB(ColGreen, 25));
|
|
rectangles[rectangleCount].greenRect = greenName;
|
|
|
|
// Black rectangle: Closing to Opening price (body)
|
|
string bodyName = prefix + "Body";
|
|
ObjectCreate(0, bodyName, OBJ_RECTANGLE, 0, dayStart, dClose, dayEnd, dOpen);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_COLOR, ColBodyBear);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_WIDTH, 2);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_FILL, true);
|
|
ObjectSetInteger(0, bodyName, OBJPROP_BGCOLOR, ColorToARGB(ColBodyBear, 25));
|
|
rectangles[rectangleCount].bodyRect = bodyName;
|
|
}
|
|
|
|
// Labels: sizes in pips (points) and ATR comparison
|
|
double pip = _Point;
|
|
int redPips = (int)MathRound(MathAbs(dHigh - (isBullish ? dClose : dOpen)) / pip);
|
|
int greenPips = (int)MathRound(MathAbs((isBullish ? dOpen : dClose) - dLow) / pip);
|
|
int bodyPips = (int)MathRound(MathAbs(dClose - dOpen) / pip);
|
|
int candlePips = (int)MathRound(MathAbs(dHigh - dLow) / pip);
|
|
double atrVal = CalcATRAt(h, l, c, i, ATRLen);
|
|
int atrPips = (int)MathRound(atrVal / pip);
|
|
|
|
datetime midTime = dayStart; // + (dayEnd - dayStart) / 2;
|
|
string infoName = "DayInfo_" + idxStr;
|
|
string infoText = "R:" + IntegerToString(redPips) + " G:" + IntegerToString(greenPips) + " B:" + IntegerToString(bodyPips);
|
|
ObjectCreate(0, infoName, OBJ_TEXT, 0, midTime, dHigh + 40 * pip);
|
|
ObjectSetString(0, infoName, OBJPROP_TEXT, infoText);
|
|
ObjectSetInteger(0, infoName, OBJPROP_COLOR, ColText);
|
|
rectangles[rectangleCount].infoLabel = infoName;
|
|
|
|
string atrName = "DayATR_" + idxStr;
|
|
string cmp = (candlePips > atrPips) ? ">" : (candlePips < atrPips) ? "<" : "=";
|
|
string atrText = "ATR" + IntegerToString(ATRLen) + ": " + IntegerToString(atrPips) + " | " + candlePips + " " + cmp + " ATR";
|
|
ObjectCreate(0, atrName, OBJ_TEXT, 0, midTime, dLow - 10 * pip);
|
|
ObjectSetString(0, atrName, OBJPROP_TEXT, atrText);
|
|
ObjectSetInteger(0, atrName, OBJPROP_COLOR, ColText);
|
|
rectangles[rectangleCount].atrLabel = atrName;
|
|
|
|
rectangleCount++;
|
|
if(rectangleCount >= DaysToShow) break;
|
|
built++;
|
|
}
|
|
|
|
ChartRedraw();
|
|
return(rates_total);
|
|
}
|
|
//+------------------------------------------------------------------+
|