Files
mql5/Indicators/MyIndicators/VWAP_History_Levels.mq5
T

407 lines
16 KiB
Plaintext
Raw Normal View History

2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
//| VWAP_History_Levels.mq5 |
//| Copyright 2026, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, xxxxxxxx"
#property version "3.00" // Modernized: Full Workspace Ray Engine, Custom Session Support & Clean GC
#property description "Projects Historical Closing VWAP Levels (Daily, Weekly, Monthly, Custom) as S/R Rays."
2026-03-05 10:29:37 +01:00
#property indicator_chart_window
#property indicator_plots 0
2026-03-05 10:29:37 +01:00
//--- Included Engines
2026-03-05 10:29:37 +01:00
#include <MyIncludes\VWAP_Calculator.mqh>
//--- Enum for selecting candle source ---
#ifndef ENUM_CANDLE_SOURCE_DEFINED
#define ENUM_CANDLE_SOURCE_DEFINED
enum ENUM_CANDLE_SOURCE
{
CANDLE_STANDARD, // Use standard OHLC data
CANDLE_HEIKIN_ASHI // Use Heikin Ashi smoothed data
};
#endif
2026-03-05 10:29:37 +01:00
//--- Input Parameters ---
input group "--- Calculation & Source Settings ---"
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK; // Volume Type
input ENUM_CANDLE_SOURCE InpCandleSource = CANDLE_STANDARD; // Candle Source
input int InpTzShift = 0; // Timezone Shift in hours vs Broker Time
2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
input group "--- Daily Historical Levels (PD-VWAP) ---"
input bool InpShowDaily = true; // Show Prior Daily VWAP Levels?
input int InpDailyCount = 3; // Number of Daily Levels to Keep
input color InpDailyColor = clrDeepPink; // Daily Level Color
input ENUM_LINE_STYLE InpDailyStyle = STYLE_SOLID; // Daily Level Style
input int InpDailyWidth = 1; // Daily Level Width
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
input group "--- Weekly Historical Levels (PW-VWAP) ---"
input bool InpShowWeekly = true; // Show Prior Weekly VWAP Levels?
input int InpWeeklyCount = 3; // Number of Weekly Levels to Keep
input color InpWeeklyColor = clrDodgerBlue; // Weekly Level Color
input ENUM_LINE_STYLE InpWeeklyStyle = STYLE_SOLID; // Weekly Level Style
input int InpWeeklyWidth = 1; // Weekly Level Width
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
input group "--- Monthly Historical Levels (PM-VWAP) ---"
input bool InpShowMonthly = true; // Show Prior Monthly VWAP Levels?
input int InpMonthlyCount = 2; // Number of Monthly Levels to Keep
input color InpMonthlyColor = clrMediumTurquoise;// Monthly Level Color
input ENUM_LINE_STYLE InpMonthlyStyle = STYLE_SOLID; // Monthly Level Style
input int InpMonthlyWidth = 1; // Monthly Level Width
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
input group "--- Custom Session Levels (e.g., Prior London Session) ---"
input bool InpShowCustom = false; // Show Prior Custom Session VWAP?
input string InpCustomStart = "08:00"; // Custom Session Start (HH:MM)
input string InpCustomEnd = "16:30"; // Custom Session End (HH:MM)
input int InpCustomCount = 2; // Number of Custom Levels to Keep
input color InpCustomColor = clrGold; // Custom Level Color
input ENUM_LINE_STYLE InpCustomStyle = STYLE_SOLID; // Custom Level Style
input int InpCustomWidth = 1; // Custom Level Width
input group "--- Visual & Label Settings ---"
input bool InpShowLabels = true; // Show Text Labels on Chart?
input int InpLabelShift = 8; // Label Shift (Bars Into Workspace)
input int InpFontSize = 8; // Font Size
//--- Internal State Buffers
double calc_daily[];
double calc_weekly[];
double calc_monthly[];
double calc_custom[];
//--- Calculator Objects
CVWAPCalculator *g_vwap_d = NULL;
CVWAPCalculator *g_vwap_w = NULL;
CVWAPCalculator *g_vwap_m = NULL;
CVWAPCalculator *g_vwap_c = NULL;
const string g_prefix_line = "VLevel_";
const string g_prefix_lbl = "VLevelLbl_";
//+------------------------------------------------------------------+
//| Custom Indicator Initialization |
2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
int OnInit()
{
// 1. Initialize Daily Calculator
if(InpShowDaily)
{
g_vwap_d = (InpCandleSource == CANDLE_HEIKIN_ASHI) ? new CVWAPCalculator_HA() : new CVWAPCalculator();
if(CheckPointer(g_vwap_d) == POINTER_INVALID || !g_vwap_d.Init(PERIOD_SESSION, InpVolumeType, InpTzShift, true, InpDailyCount * 5))
return INIT_FAILED;
}
2026-03-05 10:29:37 +01:00
// 2. Initialize Weekly Calculator
if(InpShowWeekly)
{
g_vwap_w = (InpCandleSource == CANDLE_HEIKIN_ASHI) ? new CVWAPCalculator_HA() : new CVWAPCalculator();
if(CheckPointer(g_vwap_w) == POINTER_INVALID || !g_vwap_w.Init(PERIOD_WEEK, InpVolumeType, 0, true, InpWeeklyCount * 14))
return INIT_FAILED;
}
2026-03-05 10:29:37 +01:00
// 3. Initialize Monthly Calculator
if(InpShowMonthly)
{
g_vwap_m = (InpCandleSource == CANDLE_HEIKIN_ASHI) ? new CVWAPCalculator_HA() : new CVWAPCalculator();
if(CheckPointer(g_vwap_m) == POINTER_INVALID || !g_vwap_m.Init(PERIOD_MONTH, InpVolumeType, 0, true, InpMonthlyCount * 60))
return INIT_FAILED;
}
// 4. Initialize Custom Session Calculator
if(InpShowCustom)
{
g_vwap_c = (InpCandleSource == CANDLE_HEIKIN_ASHI) ? new CVWAPCalculator_HA() : new CVWAPCalculator();
if(CheckPointer(g_vwap_c) == POINTER_INVALID || !g_vwap_c.Init(InpCustomStart, InpCustomEnd, InpVolumeType, true, InpCustomCount * 5, InpTzShift))
return INIT_FAILED;
}
string ha_tag = (InpCandleSource == CANDLE_HEIKIN_ASHI) ? " HA" : "";
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("VWAP Hist Levels%s", ha_tag));
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
2026-03-05 10:29:37 +01:00
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom Indicator Deinitialization |
2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
2026-03-05 10:29:37 +01:00
{
if(CheckPointer(g_vwap_d) != POINTER_INVALID)
{
delete g_vwap_d;
g_vwap_d = NULL;
}
if(CheckPointer(g_vwap_w) != POINTER_INVALID)
{
delete g_vwap_w;
g_vwap_w = NULL;
}
if(CheckPointer(g_vwap_m) != POINTER_INVALID)
{
delete g_vwap_m;
g_vwap_m = NULL;
}
if(CheckPointer(g_vwap_c) != POINTER_INVALID)
{
delete g_vwap_c;
g_vwap_c = NULL;
}
ObjectsDeleteAll(0, g_prefix_line);
ObjectsDeleteAll(0, g_prefix_lbl);
ChartRedraw(0);
2026-03-05 10:29:37 +01:00
}
//+------------------------------------------------------------------+
//| Helper: Merge Odd/Even buffers into a single linear cache |
//+------------------------------------------------------------------+
void MergeOddEvenBuffers(const int total, const double &odd[], const double &even[], double &dst[])
{
if(ArraySize(dst) != total)
{
ArrayResize(dst, total);
ArraySetAsSeries(dst, false);
}
for(int i = 0; i < total; i++)
{
if(odd[i] != EMPTY_VALUE && odd[i] > 0.0)
dst[i] = odd[i];
else
if(even[i] != EMPTY_VALUE && even[i] > 0.0)
dst[i] = even[i];
else
dst[i] = EMPTY_VALUE;
}
}
//+------------------------------------------------------------------+
//| Custom Indicator Calculation Loop |
2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
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 < 2)
return 0;
// Chronological Safety
ArraySetAsSeries(time, false);
ArraySetAsSeries(open, false);
ArraySetAsSeries(high, false);
ArraySetAsSeries(low, false);
ArraySetAsSeries(close, false);
ArraySetAsSeries(tick_volume, false);
ArraySetAsSeries(volume, false);
2026-03-05 10:29:37 +01:00
double odd[], even[];
// 1. Calculate History Buffers
if(InpShowDaily && CheckPointer(g_vwap_d) != POINTER_INVALID)
2026-03-05 10:29:37 +01:00
{
g_vwap_d.Calculate(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, odd, even);
MergeOddEvenBuffers(rates_total, odd, even, calc_daily);
2026-03-05 10:29:37 +01:00
}
if(InpShowWeekly && CheckPointer(g_vwap_w) != POINTER_INVALID)
2026-03-05 10:29:37 +01:00
{
g_vwap_w.Calculate(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, odd, even);
MergeOddEvenBuffers(rates_total, odd, even, calc_weekly);
2026-03-05 10:29:37 +01:00
}
if(InpShowMonthly && CheckPointer(g_vwap_m) != POINTER_INVALID)
2026-03-05 10:29:37 +01:00
{
g_vwap_m.Calculate(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, odd, even);
MergeOddEvenBuffers(rates_total, odd, even, calc_monthly);
2026-03-05 10:29:37 +01:00
}
if(InpShowCustom && CheckPointer(g_vwap_c) != POINTER_INVALID)
2026-03-05 10:29:37 +01:00
{
g_vwap_c.Calculate(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, odd, even);
MergeOddEvenBuffers(rates_total, odd, even, calc_custom);
}
2026-03-05 10:29:37 +01:00
// 2. Scan Period Transitions & Project S/R Ray Levels
int scan_start = (prev_calculated == 0) ? 1 : (prev_calculated - 1);
if(scan_start < 1)
scan_start = 1;
for(int i = scan_start; i < rates_total; i++)
{
// --- Daily Transitions (PD-VWAP) ---
if(InpShowDaily)
2026-03-05 10:29:37 +01:00
{
datetime curr_t = time[i] + (datetime)(InpTzShift * 3600);
datetime prev_t = time[i - 1] + (datetime)(InpTzShift * 3600);
MqlDateTime dt_c, dt_p;
TimeToStruct(curr_t, dt_c);
TimeToStruct(prev_t, dt_p);
if(dt_c.day_of_year != dt_p.day_of_year || dt_c.year != dt_p.year)
{
CaptureHistoricLevel(time[i - 1], calc_daily[i - 1], "PD-VWAP", InpDailyColor, InpDailyStyle, InpDailyWidth, InpDailyCount);
}
2026-03-05 10:29:37 +01:00
}
// --- Weekly Transitions (PW-VWAP) ---
if(InpShowWeekly)
2026-03-05 10:29:37 +01:00
{
MqlDateTime dt_c, dt_p;
TimeToStruct(time[i], dt_c);
TimeToStruct(time[i - 1], dt_p);
if(dt_c.day_of_week < dt_p.day_of_week)
{
CaptureHistoricLevel(time[i - 1], calc_weekly[i - 1], "PW-VWAP", InpWeeklyColor, InpWeeklyStyle, InpWeeklyWidth, InpWeeklyCount);
}
2026-03-05 10:29:37 +01:00
}
// --- Monthly Transitions (PM-VWAP) ---
if(InpShowMonthly)
2026-03-05 10:29:37 +01:00
{
MqlDateTime dt_c, dt_p;
TimeToStruct(time[i], dt_c);
TimeToStruct(time[i - 1], dt_p);
if(dt_c.mon != dt_p.mon || dt_c.year != dt_p.year)
{
CaptureHistoricLevel(time[i - 1], calc_monthly[i - 1], "PM-VWAP", InpMonthlyColor, InpMonthlyStyle, InpMonthlyWidth, InpMonthlyCount);
}
}
// --- Custom Session Transitions (PS-VWAP) ---
if(InpShowCustom && CheckPointer(g_vwap_c) != POINTER_INVALID)
{
bool is_inside_now = g_vwap_c.IsTimeInSession(time[i]);
bool is_inside_prev = g_vwap_c.IsTimeInSession(time[i - 1]);
// Session just closed
if(!is_inside_now && is_inside_prev)
{
CaptureHistoricLevel(time[i - 1], calc_custom[i - 1], "PS-VWAP", InpCustomColor, InpCustomStyle, InpCustomWidth, InpCustomCount);
}
2026-03-05 10:29:37 +01:00
}
}
return rates_total;
2026-03-05 10:29:37 +01:00
}
//+------------------------------------------------------------------+
//| Capture and Project Historic VWAP Level Ray |
//+------------------------------------------------------------------+
void CaptureHistoricLevel(const datetime time_close,
const double price,
const string tag,
const color clr,
const ENUM_LINE_STYLE style,
const int width,
const int limit_count)
{
if(price <= 0.0 || price == EMPTY_VALUE)
2026-03-05 10:29:37 +01:00
return;
string name_prefix = g_prefix_line + tag + "_";
string obj_name = name_prefix + TimeToString(time_close, TIME_DATE | TIME_MINUTES);
2026-03-05 10:29:37 +01:00
if(ObjectFind(0, obj_name) >= 0)
return; // Already registered
2026-03-05 10:29:37 +01:00
// Clean older rays before creating new one
2026-03-05 10:29:37 +01:00
CleanOldObjects(name_prefix, limit_count);
datetime t_end = time_close + PeriodSeconds() * 100;
2026-03-05 10:29:37 +01:00
// 1. Create Full-Workspace Extended Ray Line
ObjectCreate(0, obj_name, OBJ_TREND, 0, time_close, price, t_end, price);
ObjectSetInteger(0, obj_name, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, obj_name, OBJPROP_RAY_LEFT, false);
ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, obj_name, OBJPROP_STYLE, style);
ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true);
// 2. Create Text Label in Chart Shift Workspace
if(InpShowLabels)
{
string lbl_name = g_prefix_lbl + tag + "_" + TimeToString(time_close, TIME_DATE | TIME_MINUTES);
ObjectCreate(0, lbl_name, OBJ_TEXT, 0, 0, 0);
ObjectSetInteger(0, lbl_name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
ObjectSetInteger(0, lbl_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, lbl_name, OBJPROP_HIDDEN, true);
datetime lbl_time = iTime(_Symbol, Period(), 0) + PeriodSeconds(Period()) * InpLabelShift;
string label_text = StringFormat(" %s (%s)", tag, DoubleToString(price, _Digits));
ObjectSetString(0, lbl_name, OBJPROP_TEXT, label_text);
ObjectSetDouble(0, lbl_name, OBJPROP_PRICE, price);
ObjectSetInteger(0, lbl_name, OBJPROP_TIME, lbl_time);
ObjectSetInteger(0, lbl_name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, lbl_name, OBJPROP_FONTSIZE, InpFontSize);
}
2026-03-05 10:29:37 +01:00
}
//+------------------------------------------------------------------+
//| High-Performance Garbage Collector (Pre-allocated Array GC) |
2026-03-05 10:29:37 +01:00
//+------------------------------------------------------------------+
void CleanOldObjects(const string prefix, const int max_count)
2026-03-05 10:29:37 +01:00
{
int total_objs = ObjectsTotal(0);
if(total_objs < 1)
return;
2026-03-05 10:29:37 +01:00
string objects[];
ArrayResize(objects, total_objs);
int count = 0;
for(int i = 0; i < total_objs; i++)
2026-03-05 10:29:37 +01:00
{
string n = ObjectName(0, i);
if(StringFind(n, prefix) == 0 && StringFind(n, g_prefix_lbl) == -1)
2026-03-05 10:29:37 +01:00
{
objects[count] = n;
count++;
}
}
if(count >= max_count)
{
ArrayResize(objects, count);
ArraySort(objects); // Lexicographical sort by Date/Time string
2026-03-05 10:29:37 +01:00
int to_delete = count - max_count + 1;
for(int i = 0; i < to_delete; i++)
2026-03-05 10:29:37 +01:00
{
string line_name = objects[i];
string lbl_name = line_name;
StringReplace(lbl_name, g_prefix_line, g_prefix_lbl);
ObjectDelete(0, line_name);
ObjectDelete(0, lbl_name);
2026-03-05 10:29:37 +01:00
}
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+