454 lines
No EOL
14 KiB
MQL5
454 lines
No EOL
14 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| DigitalRogue |
|
|
//| Algorithmic Trading Systems for MetaTrader 5 |
|
|
//| |
|
|
//| Camarilla_Levels.mq5 |
|
|
//| Day-segmented Camarilla levels from previous daily OHLC |
|
|
//| Shows current day plus recent history and exposes current |
|
|
//| buffers |
|
|
//| |
|
|
//| Copyright 2026, Jim Gray |
|
|
//| All rights reserved. |
|
|
//| https://www.mql5.com/en/users/digitalrogue |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
#property indicator_chart_window
|
|
#property indicator_plots 10
|
|
#property indicator_buffers 10
|
|
|
|
#property indicator_type1 DRAW_NONE
|
|
#property indicator_type2 DRAW_NONE
|
|
#property indicator_type3 DRAW_NONE
|
|
#property indicator_type4 DRAW_NONE
|
|
#property indicator_type5 DRAW_NONE
|
|
#property indicator_type6 DRAW_NONE
|
|
#property indicator_type7 DRAW_NONE
|
|
#property indicator_type8 DRAW_NONE
|
|
#property indicator_type9 DRAW_NONE
|
|
#property indicator_type10 DRAW_NONE
|
|
|
|
#property indicator_label1 "L1"
|
|
#property indicator_label2 "L2"
|
|
#property indicator_label3 "L3"
|
|
#property indicator_label4 "L4"
|
|
#property indicator_label5 "L5"
|
|
#property indicator_label6 "H1"
|
|
#property indicator_label7 "H2"
|
|
#property indicator_label8 "H3"
|
|
#property indicator_label9 "H4"
|
|
#property indicator_label10 "H5"
|
|
|
|
double L1Buffer[];
|
|
double L2Buffer[];
|
|
double L3Buffer[];
|
|
double L4Buffer[];
|
|
double L5Buffer[];
|
|
double H1Buffer[];
|
|
double H2Buffer[];
|
|
double H3Buffer[];
|
|
double H4Buffer[];
|
|
double H5Buffer[];
|
|
|
|
input double CamarillaMultiplier = 1.1;
|
|
input int VisibleDaySets = 3;
|
|
|
|
input color H1_Color = clrSilver;
|
|
input color H2_Color = clrSilver;
|
|
input color H3_Color = clrOrange;
|
|
input color H4_Color = clrRed;
|
|
input color H5_Color = clrFireBrick;
|
|
input color L1_Color = clrSilver;
|
|
input color L2_Color = clrSilver;
|
|
input color L3_Color = clrDodgerBlue;
|
|
input color L4_Color = clrDeepSkyBlue;
|
|
input color L5_Color = clrRoyalBlue;
|
|
|
|
input int MinorWidth = 1;
|
|
input int MajorWidth = 2;
|
|
input int ExtremeWidth = 2;
|
|
input ENUM_LINE_STYLE MinorStyle = STYLE_DOT;
|
|
input ENUM_LINE_STYLE MajorStyle = STYLE_SOLID;
|
|
input ENUM_LINE_STYLE ExtremeStyle = STYLE_DASH;
|
|
|
|
input bool ShowLabels = true;
|
|
input color LabelColor = clrWhite;
|
|
input int LabelFontSize = 8;
|
|
|
|
input bool EnableAlerts = false;
|
|
input bool AlertOnTouch = false;
|
|
input bool AlertOnCross = false;
|
|
input int TouchTolerancePoints = 5;
|
|
|
|
datetime g_lastDay = 0;
|
|
datetime g_lastAlertBar = 0;
|
|
datetime g_lastChartBar = 0;
|
|
int g_lastPrevCalculated = 0;
|
|
|
|
double g_l1 = EMPTY_VALUE;
|
|
double g_l2 = EMPTY_VALUE;
|
|
double g_l3 = EMPTY_VALUE;
|
|
double g_l4 = EMPTY_VALUE;
|
|
double g_l5 = EMPTY_VALUE;
|
|
double g_h1 = EMPTY_VALUE;
|
|
double g_h2 = EMPTY_VALUE;
|
|
double g_h3 = EMPTY_VALUE;
|
|
double g_h4 = EMPTY_VALUE;
|
|
double g_h5 = EMPTY_VALUE;
|
|
|
|
int g_lastSideL3 = 0;
|
|
int g_lastSideL4 = 0;
|
|
int g_lastSideL5 = 0;
|
|
int g_lastSideH3 = 0;
|
|
int g_lastSideH4 = 0;
|
|
int g_lastSideH5 = 0;
|
|
|
|
string PREFIX = "DR_CAM_";
|
|
|
|
int OnInit()
|
|
{
|
|
IndicatorSetString(INDICATOR_SHORTNAME, "DigitalRogue Camarilla Levels");
|
|
|
|
SetIndexBuffer(0, L1Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(1, L2Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(2, L3Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(3, L4Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(4, L5Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(5, H1Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(6, H2Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(7, H3Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(8, H4Buffer, INDICATOR_DATA);
|
|
SetIndexBuffer(9, H5Buffer, INDICATOR_DATA);
|
|
|
|
ArraySetAsSeries(L1Buffer, true);
|
|
ArraySetAsSeries(L2Buffer, true);
|
|
ArraySetAsSeries(L3Buffer, true);
|
|
ArraySetAsSeries(L4Buffer, true);
|
|
ArraySetAsSeries(L5Buffer, true);
|
|
ArraySetAsSeries(H1Buffer, true);
|
|
ArraySetAsSeries(H2Buffer, true);
|
|
ArraySetAsSeries(H3Buffer, true);
|
|
ArraySetAsSeries(H4Buffer, true);
|
|
ArraySetAsSeries(H5Buffer, true);
|
|
|
|
for(int i = 0; i < 10; i++)
|
|
PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
|
|
|
RefreshLevels();
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
void OnDeinit(const int reason)
|
|
{
|
|
DeleteAllObjects();
|
|
}
|
|
|
|
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 <= 0)
|
|
return 0;
|
|
|
|
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
|
|
bool dayChanged = (currentDay != g_lastDay);
|
|
bool fullSync = (prev_calculated <= 0) || dayChanged;
|
|
|
|
if(dayChanged)
|
|
RefreshLevels();
|
|
else
|
|
SyncBuffers(rates_total, fullSync);
|
|
|
|
bool newBar = (g_lastChartBar != time[rates_total - 1]);
|
|
g_lastChartBar = time[rates_total - 1];
|
|
|
|
if(ShowLabels && (fullSync || newBar))
|
|
AlignCurrentLabels();
|
|
|
|
CheckAlerts(time);
|
|
g_lastPrevCalculated = rates_total;
|
|
return rates_total;
|
|
}
|
|
|
|
void RefreshLevels()
|
|
{
|
|
g_lastDay = iTime(_Symbol, PERIOD_D1, 0);
|
|
DeleteAllObjects();
|
|
|
|
int daySets = MathMax(1, VisibleDaySets);
|
|
for(int shift = daySets - 1; shift >= 0; shift--)
|
|
BuildDaySet(shift, shift == 0);
|
|
}
|
|
|
|
void SyncBuffers(const int rates_total, const bool fullSync)
|
|
{
|
|
if(rates_total <= 0)
|
|
return;
|
|
|
|
// On incremental updates, only fill the newest bar (index 0 with AsSeries)
|
|
int startIdx, endIdx;
|
|
if(fullSync)
|
|
{
|
|
startIdx = 0;
|
|
endIdx = rates_total - 1;
|
|
}
|
|
else
|
|
{
|
|
// Only update the last couple bars on tick updates
|
|
startIdx = 0;
|
|
endIdx = MathMin(2, rates_total - 1);
|
|
}
|
|
|
|
for(int i = startIdx; i <= endIdx; i++)
|
|
{
|
|
L1Buffer[i] = g_l1;
|
|
L2Buffer[i] = g_l2;
|
|
L3Buffer[i] = g_l3;
|
|
L4Buffer[i] = g_l4;
|
|
L5Buffer[i] = g_l5;
|
|
H1Buffer[i] = g_h1;
|
|
H2Buffer[i] = g_h2;
|
|
H3Buffer[i] = g_h3;
|
|
H4Buffer[i] = g_h4;
|
|
H5Buffer[i] = g_h5;
|
|
}
|
|
}
|
|
|
|
bool BuildDaySet(const int shift, const bool cacheCurrent)
|
|
{
|
|
datetime dayStart = iTime(_Symbol, PERIOD_D1, shift);
|
|
datetime nextDay = dayStart + 86400;
|
|
if(dayStart <= 0)
|
|
return false;
|
|
|
|
double prevHigh = iHigh(_Symbol, PERIOD_D1, shift + 1);
|
|
double prevLow = iLow(_Symbol, PERIOD_D1, shift + 1);
|
|
double prevClose = iClose(_Symbol, PERIOD_D1, shift + 1);
|
|
if(prevHigh <= 0.0 || prevLow <= 0.0 || prevClose <= 0.0 || prevHigh <= prevLow)
|
|
return false;
|
|
|
|
double range = prevHigh - prevLow;
|
|
double factor = range * CamarillaMultiplier;
|
|
|
|
double l1 = prevClose - factor / 12.0;
|
|
double l2 = prevClose - factor / 6.0;
|
|
double l3 = prevClose - factor / 4.0;
|
|
double l4 = prevClose - factor / 2.0;
|
|
double h1 = prevClose + factor / 12.0;
|
|
double h2 = prevClose + factor / 6.0;
|
|
double h3 = prevClose + factor / 4.0;
|
|
double h4 = prevClose + factor / 2.0;
|
|
|
|
double h5 = (prevLow > 0.0 ? (prevHigh / prevLow) * prevClose : EMPTY_VALUE);
|
|
double l5 = (h5 != EMPTY_VALUE ? (2.0 * prevClose) - h5 : EMPTY_VALUE);
|
|
|
|
string suffix = TimeToString(dayStart, TIME_DATE);
|
|
|
|
CreateSegment("L1", suffix, dayStart, nextDay, l1, L1_Color, MinorWidth, MinorStyle);
|
|
CreateSegment("L2", suffix, dayStart, nextDay, l2, L2_Color, MinorWidth, MinorStyle);
|
|
CreateSegment("L3", suffix, dayStart, nextDay, l3, L3_Color, MajorWidth, MajorStyle);
|
|
CreateSegment("L4", suffix, dayStart, nextDay, l4, L4_Color, MajorWidth, MajorStyle);
|
|
CreateSegment("L5", suffix, dayStart, nextDay, l5, L5_Color, ExtremeWidth, ExtremeStyle);
|
|
CreateSegment("H1", suffix, dayStart, nextDay, h1, H1_Color, MinorWidth, MinorStyle);
|
|
CreateSegment("H2", suffix, dayStart, nextDay, h2, H2_Color, MinorWidth, MinorStyle);
|
|
CreateSegment("H3", suffix, dayStart, nextDay, h3, H3_Color, MajorWidth, MajorStyle);
|
|
CreateSegment("H4", suffix, dayStart, nextDay, h4, H4_Color, MajorWidth, MajorStyle);
|
|
CreateSegment("H5", suffix, dayStart, nextDay, h5, H5_Color, ExtremeWidth, ExtremeStyle);
|
|
|
|
if(cacheCurrent)
|
|
{
|
|
g_l1 = l1; g_l2 = l2; g_l3 = l3; g_l4 = l4; g_l5 = l5;
|
|
g_h1 = h1; g_h2 = h2; g_h3 = h3; g_h4 = h4; g_h5 = h5;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CreateSegment(const string tag,
|
|
const string suffix,
|
|
const datetime dayStart,
|
|
const datetime nextDay,
|
|
const double price,
|
|
const color lineColor,
|
|
const int width,
|
|
const ENUM_LINE_STYLE style)
|
|
{
|
|
if(price == EMPTY_VALUE || price <= 0.0)
|
|
return;
|
|
|
|
string lineName = PREFIX + tag + "_" + suffix + "_LINE";
|
|
if(ObjectFind(0, lineName) < 0)
|
|
ObjectCreate(0, lineName, OBJ_TREND, 0, dayStart, price, nextDay, price);
|
|
|
|
ObjectMove(0, lineName, 0, dayStart, price);
|
|
ObjectMove(0, lineName, 1, nextDay, price);
|
|
ObjectSetInteger(0, lineName, OBJPROP_COLOR, lineColor);
|
|
ObjectSetInteger(0, lineName, OBJPROP_WIDTH, width);
|
|
ObjectSetInteger(0, lineName, OBJPROP_STYLE, style);
|
|
ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, false);
|
|
ObjectSetInteger(0, lineName, OBJPROP_RAY_LEFT, false);
|
|
ObjectSetInteger(0, lineName, OBJPROP_BACK, false);
|
|
ObjectSetInteger(0, lineName, OBJPROP_SELECTABLE, true);
|
|
ObjectSetInteger(0, lineName, OBJPROP_HIDDEN, false);
|
|
|
|
UpdateLabel(tag, suffix, nextDay, price, lineColor);
|
|
}
|
|
|
|
void UpdateLabel(const string tag,
|
|
const string suffix,
|
|
const datetime nextDay,
|
|
const double price,
|
|
const color textColor)
|
|
{
|
|
string labelName = PREFIX + tag + "_" + suffix + "_LABEL";
|
|
if(!ShowLabels || price == EMPTY_VALUE || price <= 0.0)
|
|
{
|
|
if(ObjectFind(0, labelName) >= 0)
|
|
ObjectDelete(0, labelName);
|
|
return;
|
|
}
|
|
|
|
datetime anchorTime = nextDay - (datetime)(PeriodSeconds(PERIOD_CURRENT) * 2);
|
|
if(anchorTime <= 0)
|
|
anchorTime = TimeCurrent();
|
|
|
|
if(ObjectFind(0, labelName) < 0)
|
|
ObjectCreate(0, labelName, OBJ_TEXT, 0, anchorTime, price);
|
|
|
|
ObjectMove(0, labelName, 0, anchorTime, price);
|
|
ObjectSetString(0, labelName, OBJPROP_TEXT, tag + " " + DoubleToString(price, _Digits));
|
|
ObjectSetInteger(0, labelName, OBJPROP_COLOR, LabelColor == clrNONE ? textColor : LabelColor);
|
|
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, LabelFontSize);
|
|
ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_RIGHT);
|
|
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, false);
|
|
}
|
|
|
|
void AlignCurrentLabels()
|
|
{
|
|
string suffix = TimeToString(iTime(_Symbol, PERIOD_D1, 0), TIME_DATE);
|
|
datetime nextDay = iTime(_Symbol, PERIOD_D1, 0) + 86400;
|
|
UpdateLabel("L1", suffix, nextDay, g_l1, L1_Color);
|
|
UpdateLabel("L2", suffix, nextDay, g_l2, L2_Color);
|
|
UpdateLabel("L3", suffix, nextDay, g_l3, L3_Color);
|
|
UpdateLabel("L4", suffix, nextDay, g_l4, L4_Color);
|
|
UpdateLabel("L5", suffix, nextDay, g_l5, L5_Color);
|
|
UpdateLabel("H1", suffix, nextDay, g_h1, H1_Color);
|
|
UpdateLabel("H2", suffix, nextDay, g_h2, H2_Color);
|
|
UpdateLabel("H3", suffix, nextDay, g_h3, H3_Color);
|
|
UpdateLabel("H4", suffix, nextDay, g_h4, H4_Color);
|
|
UpdateLabel("H5", suffix, nextDay, g_h5, H5_Color);
|
|
}
|
|
|
|
void DeleteAllObjects()
|
|
{
|
|
for(int i = ObjectsTotal(0, 0, -1) - 1; i >= 0; i--)
|
|
{
|
|
string name = ObjectName(0, i);
|
|
if(StringFind(name, PREFIX) == 0)
|
|
ObjectDelete(0, name);
|
|
}
|
|
}
|
|
|
|
int Side(const double price, const double level, const int tolerancePoints)
|
|
{
|
|
if(level == EMPTY_VALUE || level <= 0.0)
|
|
return 0;
|
|
|
|
double tol = tolerancePoints * _Point;
|
|
if(price > level + tol)
|
|
return 1;
|
|
if(price < level - tol)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
void ResetAlertSides()
|
|
{
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(bid <= 0.0)
|
|
return;
|
|
|
|
g_lastSideL3 = Side(bid, g_l3, TouchTolerancePoints);
|
|
g_lastSideL4 = Side(bid, g_l4, TouchTolerancePoints);
|
|
g_lastSideL5 = Side(bid, g_l5, TouchTolerancePoints);
|
|
g_lastSideH3 = Side(bid, g_h3, TouchTolerancePoints);
|
|
g_lastSideH4 = Side(bid, g_h4, TouchTolerancePoints);
|
|
g_lastSideH5 = Side(bid, g_h5, TouchTolerancePoints);
|
|
}
|
|
|
|
bool CanAlertThisBar(const datetime barTime)
|
|
{
|
|
return(g_lastAlertBar != barTime);
|
|
}
|
|
|
|
void MarkAlert(const datetime barTime)
|
|
{
|
|
g_lastAlertBar = barTime;
|
|
}
|
|
|
|
void CheckLevelAlert(const datetime barTime,
|
|
const string tag,
|
|
const double bid,
|
|
const double level,
|
|
int &lastSide)
|
|
{
|
|
if(level == EMPTY_VALUE || level <= 0.0)
|
|
return;
|
|
|
|
double tol = TouchTolerancePoints * _Point;
|
|
if(AlertOnTouch && MathAbs(bid - level) <= tol && CanAlertThisBar(barTime))
|
|
{
|
|
string msg = StringFormat("%s %s touch @ %s", _Symbol, tag, DoubleToString(level, _Digits));
|
|
Alert(msg);
|
|
Print(msg);
|
|
MarkAlert(barTime);
|
|
}
|
|
|
|
if(!AlertOnCross)
|
|
return;
|
|
|
|
int sideNow = Side(bid, level, TouchTolerancePoints);
|
|
if(sideNow == 0)
|
|
return;
|
|
|
|
if(lastSide == 0)
|
|
{
|
|
lastSide = sideNow;
|
|
return;
|
|
}
|
|
|
|
if(sideNow != lastSide && CanAlertThisBar(barTime))
|
|
{
|
|
string dir = (sideNow > lastSide ? "cross up" : "cross down");
|
|
string msg = StringFormat("%s %s %s @ %s", _Symbol, tag, dir, DoubleToString(level, _Digits));
|
|
Alert(msg);
|
|
Print(msg);
|
|
MarkAlert(barTime);
|
|
}
|
|
lastSide = sideNow;
|
|
}
|
|
|
|
void CheckAlerts(const datetime &time[])
|
|
{
|
|
if(!EnableAlerts)
|
|
return;
|
|
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(bid <= 0.0)
|
|
return;
|
|
|
|
datetime barTime = time[0];
|
|
CheckLevelAlert(barTime, "L3", bid, g_l3, g_lastSideL3);
|
|
CheckLevelAlert(barTime, "L4", bid, g_l4, g_lastSideL4);
|
|
CheckLevelAlert(barTime, "L5", bid, g_l5, g_lastSideL5);
|
|
CheckLevelAlert(barTime, "H3", bid, g_h3, g_lastSideH3);
|
|
CheckLevelAlert(barTime, "H4", bid, g_h4, g_lastSideH4);
|
|
CheckLevelAlert(barTime, "H5", bid, g_h5, g_lastSideH5);
|
|
} |