Skip to content

Instantly share code, notes, and snippets.

@calvez
Created March 27, 2026 06:04
Show Gist options
  • Select an option

  • Save calvez/afe656dcd584a671e1ae8962265373b2 to your computer and use it in GitHub Desktop.

Select an option

Save calvez/afe656dcd584a671e1ae8962265373b2 to your computer and use it in GitHub Desktop.
EMAs + initial range + optional PDH/PDL for price-action context
//+------------------------------------------------------------------+
//| PriceActionContext.mq5 |
//| EMAs + initial range + optional PDH/PDL for price-action context |
//| Attach to chart symbol (e.g. GER40.cash, US30.cash). |
//+------------------------------------------------------------------+
#property copyright "mt5-python"
#property link ""
#property version "1.04"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_label1 "EMA fast"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_label2 "EMA slow"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrange
#property indicator_style2 STYLE_SOLID
#property indicator_width2 2
enum ENUM_RANGE_MODE
{
RANGE_MODE_NIGHT_HOURS = 0,
RANGE_MODE_OPENING_MINUTES = 1
};
enum ENUM_PIVOT_FORMULA
{
PIVOT_FORMULA_CLASSIC = 0,
PIVOT_FORMULA_FIBONACCI = 1
};
input group "EMA"
input int InpEmaFastPeriod = 20;
input int InpEmaSlowPeriod = 240;
input group "Initial range"
input ENUM_RANGE_MODE InpRangeMode = RANGE_MODE_NIGHT_HOURS;
input int InpNightStartHour = 0;
input int InpNightEndHour = 6;
input int InpSessionStartHour = 8;
input int InpOpeningRangeMinutes = 60;
input group "Yesterday D1 (optional)"
input bool InpShowYesterdayOhlc = true;
input color InpYHighColor = clrSilver;
input color InpYLowColor = clrDarkGray;
input color InpYOpenColor = clrDodgerBlue;
input color InpYCloseColor = clrOrange;
input int InpYLabelFontSize = 8;
input string InpYLabelFont = "Arial";
input int InpYLabelMarginRight = 8;
input group "Session line (optional)"
input bool InpShowSessionVline = false;
input int InpSessionVlineHour = 8;
input group "Daily pivots (prev D1 H/L/C)"
input bool InpShowDailyPivots = true;
input ENUM_PIVOT_FORMULA InpPivotFormula = PIVOT_FORMULA_CLASSIC;
input color InpPivotPPColor = clrGold;
input color InpPivotRColor = clrTomato;
input color InpPivotSColor = clrDodgerBlue;
input group "Weekly pivots — bias only (prev W1)"
input bool InpShowWeeklyPivots = false;
input group "Fibonacci — YH–YL range (disabled by default)"
input bool InpShowFibLevels = false;
input color InpFibColor = clrMagenta;
input int InpFibLabelFontSize = 7;
input group "Style"
input color InpRangeBorderColor = clrTeal;
input color InpRangeFillColor = C'0,128,128,45';
input int InpLineWidth = 1;
input ENUM_LINE_STYLE InpLineStyle = STYLE_DOT;
double g_buf_ema_fast[];
double g_buf_ema_slow[];
int g_h_ema_fast = INVALID_HANDLE;
int g_h_ema_slow = INVALID_HANDLE;
string g_prefix;
string ObjName(const string suffix)
{
return g_prefix + suffix;
}
void DeleteObjectsByPrefix(void)
{
const int total = ObjectsTotal(0, 0, -1);
for(int i = total - 1; i >= 0; i--)
{
const string name = ObjectName(0, i, 0, -1);
if(StringFind(name, g_prefix) == 0)
ObjectDelete(0, name);
}
}
bool ComputeNightRangeForDay(const datetime &time[], const double &high[], const double &low[],
const int rates_total, const int day_y, const int day_m, const int day_d,
double &out_high, double &out_low, bool &out_ok)
{
out_ok = false;
out_high = -1.0e100;
out_low = 1.0e100;
for(int i = 0; i < rates_total; i++)
{
MqlDateTime dt;
TimeToStruct(time[i], dt);
if(dt.year != day_y || dt.mon != day_m || dt.day != day_d)
continue;
const int h = dt.hour;
if(h < InpNightStartHour || h >= InpNightEndHour)
continue;
out_high = MathMax(out_high, high[i]);
out_low = MathMin(out_low, low[i]);
out_ok = true;
}
return out_ok;
}
bool ComputeOpeningRangeForDay(const datetime &time[], const double &high[], const double &low[],
const int rates_total, const int day_y, const int day_m, const int day_d,
double &out_high, double &out_low, bool &out_ok)
{
out_ok = false;
out_high = -1.0e100;
out_low = 1.0e100;
const int sec = InpOpeningRangeMinutes * 60;
if(sec <= 0)
return false;
for(int i = 0; i < rates_total; i++)
{
MqlDateTime dt;
TimeToStruct(time[i], dt);
if(dt.year != day_y || dt.mon != day_m || dt.day != day_d)
continue;
datetime day_start;
MqlDateTime d0;
d0.year = dt.year;
d0.mon = dt.mon;
d0.day = dt.day;
d0.hour = 0;
d0.min = 0;
d0.sec = 0;
day_start = StructToTime(d0);
const datetime win_open = day_start + (datetime)InpSessionStartHour * 3600;
const datetime win_close = win_open + sec;
if(time[i] < win_open || time[i] >= win_close)
continue;
out_high = MathMax(out_high, high[i]);
out_low = MathMin(out_low, low[i]);
out_ok = true;
}
return out_ok;
}
void GetRangeWindowTimes(const int day_y, const int day_m, const int day_d,
datetime &out_start, datetime &out_end)
{
MqlDateTime d;
d.year = day_y;
d.mon = day_m;
d.day = day_d;
d.min = 0;
d.sec = 0;
if(InpRangeMode == RANGE_MODE_NIGHT_HOURS)
{
d.hour = InpNightStartHour;
out_start = StructToTime(d);
d.hour = InpNightEndHour;
out_end = StructToTime(d);
}
else
{
d.hour = 0;
const datetime day_start = StructToTime(d);
out_start = day_start + (datetime)InpSessionStartHour * 3600;
out_end = out_start + (datetime)(InpOpeningRangeMinutes * 60);
}
}
void UpdateInitialRangeBox(const datetime &time[], const double &high[], const double &low[], const int rates_total)
{
if(rates_total < 2)
return;
MqlDateTime now_dt;
TimeToStruct(time[rates_total - 1], now_dt);
double rh, rl;
bool ok = false;
if(InpRangeMode == RANGE_MODE_NIGHT_HOURS)
ComputeNightRangeForDay(time, high, low, rates_total, now_dt.year, now_dt.mon, now_dt.day, rh, rl, ok);
else
ComputeOpeningRangeForDay(time, high, low, rates_total, now_dt.year, now_dt.mon, now_dt.day, rh, rl, ok);
const string box_name = ObjName("IR_BOX");
const string hi_name = ObjName("IR_HIGH");
const string lo_name = ObjName("IR_LOW");
ObjectDelete(0, hi_name);
ObjectDelete(0, lo_name);
if(!ok)
{
ObjectDelete(0, box_name);
return;
}
datetime t1, t2;
GetRangeWindowTimes(now_dt.year, now_dt.mon, now_dt.day, t1, t2);
if(t2 <= t1)
{
ObjectDelete(0, box_name);
return;
}
const double p_low = MathMin(rl, rh);
const double p_high = MathMax(rl, rh);
if(ObjectFind(0, box_name) < 0)
{
ObjectCreate(0, box_name, OBJ_RECTANGLE, 0, t1, p_low, t2, p_high);
ObjectSetString(0, box_name, OBJPROP_TEXT, "Initial range");
ObjectSetInteger(0, box_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, box_name, OBJPROP_HIDDEN, true);
}
else
{
ObjectMove(0, box_name, 0, t1, p_low);
ObjectMove(0, box_name, 1, t2, p_high);
}
ObjectSetInteger(0, box_name, OBJPROP_COLOR, InpRangeBorderColor);
ObjectSetInteger(0, box_name, OBJPROP_STYLE, InpLineStyle);
ObjectSetInteger(0, box_name, OBJPROP_WIDTH, InpLineWidth);
ObjectSetInteger(0, box_name, OBJPROP_FILL, true);
ObjectSetInteger(0, box_name, OBJPROP_BACK, true);
ObjectSetInteger(0, box_name, OBJPROP_BGCOLOR, InpRangeFillColor);
}
void HLineEnsure(const string name, const double price, const color clr, const ENUM_LINE_STYLE style)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWidth);
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
ObjectSetString(0, name, OBJPROP_TEXT, "");
}
void HLineStyled(const string name, const double price, const color clr, const ENUM_LINE_STYLE style, const int width)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
ObjectSetString(0, name, OBJPROP_TEXT, "");
}
void PriceTagRightSide(const string name, const double price, const color clr, const string text, const int font_size)
{
const datetime t = iTime(_Symbol, _Period, 0);
if(t == 0)
return;
int px, py;
const long chart_id = ChartID();
if(!ChartTimePriceToXY(chart_id, 0, t, price, px, py))
return;
if(ObjectFind(chart_id, name) < 0)
{
ObjectCreate(chart_id, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true);
}
const int fs = (font_size > 0) ? font_size : InpYLabelFontSize;
ObjectSetInteger(chart_id, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(chart_id, name, OBJPROP_ANCHOR, ANCHOR_RIGHT);
ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, InpYLabelMarginRight);
ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, py - fs / 2);
ObjectSetString(chart_id, name, OBJPROP_TEXT, text);
ObjectSetString(chart_id, name, OBJPROP_FONT, InpYLabelFont);
ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, fs);
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, clr);
}
void PriceTagRightSide(const string name, const double price, const color clr, const string text)
{
PriceTagRightSide(name, price, clr, text, -1);
}
void ComputePivotsClassic(const double H, const double L, const double C, double &pp, double &r1, double &s1, double &r2, double &s2)
{
pp = (H + L + C) / 3.0;
r1 = 2.0 * pp - L;
s1 = 2.0 * pp - H;
r2 = pp + (H - L);
s2 = pp - (H - L);
}
void ComputePivotsFibonacci(const double H, const double L, const double C, double &pp, double &r1, double &s1, double &r2, double &s2)
{
pp = (H + L + C) / 3.0;
const double range = H - L;
r1 = pp + 0.382 * range;
s1 = pp - 0.382 * range;
r2 = pp + 0.618 * range;
s2 = pp - 0.618 * range;
}
void DeleteDailyPivotObjects(void)
{
ObjectDelete(0, ObjName("D_PP"));
ObjectDelete(0, ObjName("D_R1"));
ObjectDelete(0, ObjName("D_S1"));
ObjectDelete(0, ObjName("D_R2"));
ObjectDelete(0, ObjName("D_S2"));
}
void DeleteWeeklyPivotObjects(void)
{
ObjectDelete(0, ObjName("W_PP"));
ObjectDelete(0, ObjName("W_R1"));
ObjectDelete(0, ObjName("W_S1"));
}
void DeleteFibObjects(void)
{
ObjectDelete(0, ObjName("FIB_382"));
ObjectDelete(0, ObjName("FIB_500"));
ObjectDelete(0, ObjName("FIB_618"));
ObjectDelete(0, ObjName("FIB_705"));
ObjectDelete(0, ObjName("FIB_100"));
ObjectDelete(0, ObjName("FIB_1272"));
ObjectDelete(0, ObjName("FIB_1618"));
ObjectDelete(0, ObjName("FIB_382_LBL"));
ObjectDelete(0, ObjName("FIB_500_LBL"));
ObjectDelete(0, ObjName("FIB_618_LBL"));
ObjectDelete(0, ObjName("FIB_705_LBL"));
ObjectDelete(0, ObjName("FIB_100_LBL"));
ObjectDelete(0, ObjName("FIB_1272_LBL"));
ObjectDelete(0, ObjName("FIB_1618_LBL"));
}
void UpdateDailyPivots(void)
{
if(!InpShowDailyPivots)
{
DeleteDailyPivotObjects();
return;
}
MqlRates d1[];
ArraySetAsSeries(d1, true);
if(CopyRates(_Symbol, PERIOD_D1, 0, 2, d1) < 2)
return;
const double H = d1[1].high;
const double L = d1[1].low;
const double C = d1[1].close;
double pp, r1, s1, r2, s2;
if(InpPivotFormula == PIVOT_FORMULA_CLASSIC)
ComputePivotsClassic(H, L, C, pp, r1, s1, r2, s2);
else
ComputePivotsFibonacci(H, L, C, pp, r1, s1, r2, s2);
HLineStyled(ObjName("D_PP"), pp, InpPivotPPColor, STYLE_SOLID, 2);
HLineStyled(ObjName("D_R1"), r1, InpPivotRColor, STYLE_DASH, 1);
HLineStyled(ObjName("D_S1"), s1, InpPivotSColor, STYLE_DASH, 1);
HLineStyled(ObjName("D_R2"), r2, InpPivotRColor, STYLE_DOT, 1);
HLineStyled(ObjName("D_S2"), s2, InpPivotSColor, STYLE_DOT, 1);
}
void UpdateWeeklyPivots(void)
{
if(!InpShowWeeklyPivots)
{
DeleteWeeklyPivotObjects();
return;
}
MqlRates w1[];
ArraySetAsSeries(w1, true);
if(CopyRates(_Symbol, PERIOD_W1, 0, 2, w1) < 2)
return;
const double H = w1[1].high;
const double L = w1[1].low;
const double C = w1[1].close;
double pp, r1, s1, r2, s2;
if(InpPivotFormula == PIVOT_FORMULA_CLASSIC)
ComputePivotsClassic(H, L, C, pp, r1, s1, r2, s2);
else
ComputePivotsFibonacci(H, L, C, pp, r1, s1, r2, s2);
HLineStyled(ObjName("W_PP"), pp, InpPivotPPColor, STYLE_DOT, 1);
HLineStyled(ObjName("W_R1"), r1, InpPivotRColor, STYLE_DOT, 1);
HLineStyled(ObjName("W_S1"), s1, InpPivotSColor, STYLE_DOT, 1);
}
void UpdateFibLevels(void)
{
if(!InpShowFibLevels)
{
DeleteFibObjects();
return;
}
MqlRates d1[];
ArraySetAsSeries(d1, true);
if(CopyRates(_Symbol, PERIOD_D1, 0, 2, d1) < 2)
return;
const double y_high = d1[1].high;
const double y_low = d1[1].low;
const double range = y_high - y_low;
if(range <= 0.0)
return;
double ratios[7];
ratios[0] = 0.382;
ratios[1] = 0.5;
ratios[2] = 0.618;
ratios[3] = 0.705;
ratios[4] = 1.0;
ratios[5] = 1.272;
ratios[6] = 1.618;
string tags[7];
tags[0] = "382";
tags[1] = "500";
tags[2] = "618";
tags[3] = "705";
tags[4] = "100";
tags[5] = "1272";
tags[6] = "1618";
string labels[7];
labels[0] = "Fib 0.382";
labels[1] = "Fib 0.5";
labels[2] = "Fib 0.618";
labels[3] = "Fib 0.705";
labels[4] = "Fib 1.0";
labels[5] = "Fib 1.272";
labels[6] = "Fib 1.618";
for(int i = 0; i < 7; i++)
{
const double price = y_low + ratios[i] * range;
const string hl = ObjName("FIB_" + tags[i]);
const string ll = ObjName("FIB_" + tags[i] + "_LBL");
HLineStyled(hl, price, InpFibColor, STYLE_DASHDOT, 1);
PriceTagRightSide(ll, price, InpFibColor, labels[i], InpFibLabelFontSize);
}
}
void UpdateYesterdayOhlc(void)
{
const string n_hi = ObjName("YDAY_HIGH");
const string n_lo = ObjName("YDAY_LOW");
const string n_op = ObjName("YDAY_OPEN");
const string n_cl = ObjName("YDAY_CLOSE");
const string n_hi_l = ObjName("YDAY_HIGH_LBL");
const string n_lo_l = ObjName("YDAY_LOW_LBL");
const string n_op_l = ObjName("YDAY_OPEN_LBL");
const string n_cl_l = ObjName("YDAY_CLOSE_LBL");
if(!InpShowYesterdayOhlc)
{
ObjectDelete(0, n_hi);
ObjectDelete(0, n_lo);
ObjectDelete(0, n_op);
ObjectDelete(0, n_cl);
ObjectDelete(0, n_hi_l);
ObjectDelete(0, n_lo_l);
ObjectDelete(0, n_op_l);
ObjectDelete(0, n_cl_l);
return;
}
MqlRates d1[];
ArraySetAsSeries(d1, true);
if(CopyRates(_Symbol, PERIOD_D1, 0, 2, d1) < 2)
return;
const double y_high = d1[1].high;
const double y_low = d1[1].low;
const double y_open = d1[1].open;
const double y_close = d1[1].close;
HLineEnsure(n_hi, y_high, InpYHighColor, STYLE_DASH);
HLineEnsure(n_lo, y_low, InpYLowColor, STYLE_DOT);
HLineEnsure(n_op, y_open, InpYOpenColor, STYLE_SOLID);
HLineEnsure(n_cl, y_close, InpYCloseColor, STYLE_DASHDOT);
PriceTagRightSide(n_hi_l, y_high, InpYHighColor, "Y High");
PriceTagRightSide(n_lo_l, y_low, InpYLowColor, "Y Low");
PriceTagRightSide(n_op_l, y_open, InpYOpenColor, "Y Open");
PriceTagRightSide(n_cl_l, y_close, InpYCloseColor, "Y Close");
}
void UpdateSessionVline(const datetime &time[], const int rates_total)
{
const string vname = ObjName("SESS_V");
if(!InpShowSessionVline || rates_total < 1)
{
ObjectDelete(0, vname);
return;
}
MqlDateTime t0;
TimeToStruct(time[rates_total - 1], t0);
MqlDateTime d0;
d0.year = t0.year;
d0.mon = t0.mon;
d0.day = t0.day;
d0.hour = InpSessionVlineHour;
d0.min = 0;
d0.sec = 0;
const datetime vtime = StructToTime(d0);
if(ObjectFind(0, vname) < 0)
{
ObjectCreate(0, vname, OBJ_VLINE, 0, vtime, 0);
ObjectSetInteger(0, vname, OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(0, vname, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(0, vname, OBJPROP_BACK, true);
ObjectSetString(0, vname, OBJPROP_TEXT, "Session");
}
else
ObjectMove(0, vname, 0, vtime, 0);
}
int OnInit(void)
{
SetIndexBuffer(0, g_buf_ema_fast, INDICATOR_DATA);
SetIndexBuffer(1, g_buf_ema_slow, INDICATOR_DATA);
PlotIndexSetString(0, PLOT_LABEL, "EMA " + IntegerToString(InpEmaFastPeriod));
PlotIndexSetString(1, PLOT_LABEL, "EMA " + IntegerToString(InpEmaSlowPeriod));
ArraySetAsSeries(g_buf_ema_fast, true);
ArraySetAsSeries(g_buf_ema_slow, true);
IndicatorSetString(INDICATOR_SHORTNAME, "PriceActionContext");
g_h_ema_fast = iMA(_Symbol, PERIOD_CURRENT, InpEmaFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_h_ema_slow = iMA(_Symbol, PERIOD_CURRENT, InpEmaSlowPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(g_h_ema_fast == INVALID_HANDLE || g_h_ema_slow == INVALID_HANDLE)
return INIT_FAILED;
g_prefix = StringFormat("PAC_%s_%d_", _Symbol, (int)_Period);
ObjectDelete(0, g_prefix + "PDH");
ObjectDelete(0, g_prefix + "PDL");
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
DeleteObjectsByPrefix();
if(g_h_ema_fast != INVALID_HANDLE)
IndicatorRelease(g_h_ema_fast);
if(g_h_ema_slow != INVALID_HANDLE)
IndicatorRelease(g_h_ema_slow);
}
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 < InpEmaSlowPeriod + 2)
return 0;
const int to_copy = rates_total;
if(CopyBuffer(g_h_ema_fast, 0, 0, to_copy, g_buf_ema_fast) <= 0)
return 0;
if(CopyBuffer(g_h_ema_slow, 0, 0, to_copy, g_buf_ema_slow) <= 0)
return 0;
UpdateInitialRangeBox(time, high, low, rates_total);
UpdateYesterdayOhlc();
UpdateDailyPivots();
UpdateWeeklyPivots();
UpdateFibLevels();
UpdateSessionVline(time, rates_total);
ChartRedraw(0);
return rates_total;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment