Camarilla_Levels/Camarilla_Levels.mq5
digitalrogue a2b4b109ae
2026-04-16 09:45:40 -05:00

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);
}