mirror of
https://github.com/softwaredevelop/mql5.git
synced 2026-08-26 02:38:05 +00:00
refactor: Modernized: Full Workspace Ray Engine, Custom Session Support & Clean GC
This commit is contained in:
@@ -3,69 +3,193 @@
|
|||||||
//| Copyright 2026, xxxxxxxx|
|
//| Copyright 2026, xxxxxxxx|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#property copyright "Copyright 2026, xxxxxxxx"
|
#property copyright "Copyright 2026, xxxxxxxx"
|
||||||
#property version "2.11" // BUGFIX: Prevent array out of range on TF switch
|
#property version "3.00" // Modernized: Full Workspace Ray Engine, Custom Session Support & Clean GC
|
||||||
#property description "Draws Historical VWAP Close Levels as Support/Resistance."
|
#property description "Projects Historical Closing VWAP Levels (Daily, Weekly, Monthly, Custom) as S/R Rays."
|
||||||
|
|
||||||
#property indicator_chart_window
|
#property indicator_chart_window
|
||||||
#property indicator_plots 0 // No buffers shown
|
#property indicator_plots 0
|
||||||
|
|
||||||
|
//--- Included Engines
|
||||||
#include <MyIncludes\VWAP_Calculator.mqh>
|
#include <MyIncludes\VWAP_Calculator.mqh>
|
||||||
|
|
||||||
//--- Input Parameters
|
//--- Enum for selecting candle source ---
|
||||||
input group "Daily Levels"
|
#ifndef ENUM_CANDLE_SOURCE_DEFINED
|
||||||
input bool InpShowDaily = true;
|
#define ENUM_CANDLE_SOURCE_DEFINED
|
||||||
input int InpDailyCount = 3; // Keep last N Levels
|
enum ENUM_CANDLE_SOURCE
|
||||||
input color InpDailyColor = clrDeepPink;
|
{
|
||||||
|
CANDLE_STANDARD, // Use standard OHLC data
|
||||||
|
CANDLE_HEIKIN_ASHI // Use Heikin Ashi smoothed data
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
input group "Weekly Levels"
|
//--- Input Parameters ---
|
||||||
input bool InpShowWeekly = true;
|
input group "--- Calculation & Source Settings ---"
|
||||||
input int InpWeeklyCount = 3;
|
input ENUM_APPLIED_VOLUME InpVolumeType = VOLUME_TICK; // Volume Type
|
||||||
input color InpWeeklyColor = clrDodgerBlue;
|
input ENUM_CANDLE_SOURCE InpCandleSource = CANDLE_STANDARD; // Candle Source
|
||||||
|
input int InpTzShift = 0; // Timezone Shift in hours vs Broker Time
|
||||||
input group "Monthly Levels"
|
|
||||||
input bool InpShowMonthly = true;
|
|
||||||
input int InpMonthlyCount = 2;
|
|
||||||
input color InpMonthlyColor = clrMediumTurquoise;
|
|
||||||
|
|
||||||
//--- Internal Calculators & Buffers (Hidden)
|
|
||||||
CVWAPCalculator *g_vwap_d;
|
|
||||||
CVWAPCalculator *g_vwap_w;
|
|
||||||
CVWAPCalculator *g_vwap_m;
|
|
||||||
|
|
||||||
double calc_daily[], calc_weekly[], calc_monthly[];
|
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Init |
|
//| |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
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 |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
int OnInit()
|
int OnInit()
|
||||||
{
|
{
|
||||||
// Init Calculators
|
// 1. Initialize Daily Calculator
|
||||||
g_vwap_d = new CVWAPCalculator();
|
if(InpShowDaily)
|
||||||
g_vwap_d.Init(PERIOD_SESSION, VOLUME_TICK, 0, 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;
|
||||||
|
}
|
||||||
|
|
||||||
g_vwap_w = new CVWAPCalculator();
|
// 2. Initialize Weekly Calculator
|
||||||
g_vwap_w.Init(PERIOD_WEEK, VOLUME_TICK, 0, InpShowWeekly);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
g_vwap_m = new CVWAPCalculator();
|
// 3. Initialize Monthly Calculator
|
||||||
g_vwap_m.Init(PERIOD_MONTH, VOLUME_TICK, 0, InpShowMonthly);
|
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);
|
||||||
|
|
||||||
IndicatorSetString(INDICATOR_SHORTNAME, "VWAP Hist Levels");
|
|
||||||
return(INIT_SUCCEEDED);
|
return(INIT_SUCCEEDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Deinit |
|
//| Custom Indicator Deinitialization |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void OnDeinit(const int r)
|
void OnDeinit(const int reason)
|
||||||
|
{
|
||||||
|
if(CheckPointer(g_vwap_d) != POINTER_INVALID)
|
||||||
{
|
{
|
||||||
ObjectsDeleteAll(0, "VLevel_");
|
|
||||||
delete g_vwap_d;
|
delete g_vwap_d;
|
||||||
|
g_vwap_d = NULL;
|
||||||
|
}
|
||||||
|
if(CheckPointer(g_vwap_w) != POINTER_INVALID)
|
||||||
|
{
|
||||||
delete g_vwap_w;
|
delete g_vwap_w;
|
||||||
|
g_vwap_w = NULL;
|
||||||
|
}
|
||||||
|
if(CheckPointer(g_vwap_m) != POINTER_INVALID)
|
||||||
|
{
|
||||||
delete g_vwap_m;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Calculate |
|
//| 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 |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
int OnCalculate(const int rates_total,
|
int OnCalculate(const int rates_total,
|
||||||
const int prev_calculated,
|
const int prev_calculated,
|
||||||
@@ -81,186 +205,200 @@ int OnCalculate(const int rates_total,
|
|||||||
if(rates_total < 2)
|
if(rates_total < 2)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// OPTIMIZATION: Limit Lookback
|
// Chronological Safety
|
||||||
// We only show last N days/weeks. We don't need to calc VWAP from 1990.
|
ArraySetAsSeries(time, false);
|
||||||
// Let's safe-limit to last ~5000 bars (usually enough for monthly VWAP on M5).
|
ArraySetAsSeries(open, false);
|
||||||
// Or dynamically based on Period.
|
ArraySetAsSeries(high, false);
|
||||||
int limit_bars = 5000;
|
ArraySetAsSeries(low, false);
|
||||||
if(InpShowMonthly)
|
ArraySetAsSeries(close, false);
|
||||||
limit_bars = 40000; // Need more history for months on M1
|
ArraySetAsSeries(tick_volume, false);
|
||||||
|
ArraySetAsSeries(volume, false);
|
||||||
|
|
||||||
int start_calc = rates_total - limit_bars;
|
|
||||||
if(start_calc < 0)
|
|
||||||
start_calc = 0;
|
|
||||||
|
|
||||||
// Respect prev_calculated but apply floor limit
|
|
||||||
int start_loop = (prev_calculated > 0) ? prev_calculated - 1 : start_calc;
|
|
||||||
if(start_loop < start_calc)
|
|
||||||
start_loop = start_calc; // Force start if full recalc requested but limit history
|
|
||||||
|
|
||||||
// Resize buffers
|
|
||||||
if(ArraySize(calc_daily) != rates_total)
|
|
||||||
{
|
|
||||||
ArrayResize(calc_daily, rates_total);
|
|
||||||
ArrayResize(calc_weekly, rates_total);
|
|
||||||
ArrayResize(calc_monthly, rates_total);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Calculate VWAP History
|
|
||||||
// Note: The Calculator Engine iterates safely from 0 if required.
|
|
||||||
double odd[], even[];
|
double odd[], even[];
|
||||||
|
|
||||||
|
// 1. Calculate History Buffers
|
||||||
|
if(InpShowDaily && CheckPointer(g_vwap_d) != POINTER_INVALID)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(InpShowWeekly && CheckPointer(g_vwap_w) != POINTER_INVALID)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(InpShowMonthly && CheckPointer(g_vwap_m) != POINTER_INVALID)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(InpShowCustom && CheckPointer(g_vwap_c) != POINTER_INVALID)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
if(InpShowDaily)
|
||||||
{
|
{
|
||||||
g_vwap_d.Calculate(rates_total, start_loop, time, open, high, low, close, tick_volume, volume, odd, even);
|
datetime curr_t = time[i] + (datetime)(InpTzShift * 3600);
|
||||||
MergeBuffers(rates_total, odd, even, calc_daily, start_loop);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Weekly Transitions (PW-VWAP) ---
|
||||||
if(InpShowWeekly)
|
if(InpShowWeekly)
|
||||||
{
|
{
|
||||||
g_vwap_w.Calculate(rates_total, start_loop, time, open, high, low, close, tick_volume, volume, odd, even);
|
MqlDateTime dt_c, dt_p;
|
||||||
MergeBuffers(rates_total, odd, even, calc_weekly, start_loop);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Monthly Transitions (PM-VWAP) ---
|
||||||
if(InpShowMonthly)
|
if(InpShowMonthly)
|
||||||
{
|
{
|
||||||
g_vwap_m.Calculate(rates_total, start_loop, time, open, high, low, close, tick_volume, volume, odd, even);
|
MqlDateTime dt_c, dt_p;
|
||||||
MergeBuffers(rates_total, odd, even, calc_monthly, start_loop);
|
TimeToStruct(time[i], dt_c);
|
||||||
}
|
TimeToStruct(time[i - 1], dt_p);
|
||||||
|
|
||||||
// 2. Manage Objects
|
if(dt_c.mon != dt_p.mon || dt_c.year != dt_p.year)
|
||||||
// OPTIMIZATION: Don't check objects on every tick if not needed.
|
|
||||||
// Only check if new candle or full recalc.
|
|
||||||
|
|
||||||
// CRITICAL FIX: To prevent "Array out of range" when querying [i-1],
|
|
||||||
// the object scanner must start at minimum index 1.
|
|
||||||
int start_obj = (start_loop == 0) ? 1 : start_loop;
|
|
||||||
|
|
||||||
for(int i = start_obj; i < rates_total; i++)
|
|
||||||
{
|
{
|
||||||
MqlDateTime dt, dt_prev;
|
CaptureHistoricLevel(time[i - 1], calc_monthly[i - 1], "PM-VWAP", InpMonthlyColor, InpMonthlyStyle, InpMonthlyWidth, InpMonthlyCount);
|
||||||
TimeToStruct(time[i], dt);
|
}
|
||||||
TimeToStruct(time[i-1], dt_prev);
|
}
|
||||||
|
|
||||||
// Daily
|
// --- Custom Session Transitions (PS-VWAP) ---
|
||||||
if(InpShowDaily && dt.day_of_year != dt_prev.day_of_year)
|
if(InpShowCustom && CheckPointer(g_vwap_c) != POINTER_INVALID)
|
||||||
{
|
{
|
||||||
CreateLevelObject(time[i-1], calc_daily[i-1], "D", InpDailyColor, InpDailyCount);
|
bool is_inside_now = g_vwap_c.IsTimeInSession(time[i]);
|
||||||
}
|
bool is_inside_prev = g_vwap_c.IsTimeInSession(time[i - 1]);
|
||||||
|
|
||||||
// Weekly
|
// Session just closed
|
||||||
datetime t_week = iTime(_Symbol, PERIOD_W1, iBarShift(_Symbol, PERIOD_W1, time[i]));
|
if(!is_inside_now && is_inside_prev)
|
||||||
datetime t_p_week = iTime(_Symbol, PERIOD_W1, iBarShift(_Symbol, PERIOD_W1, time[i-1]));
|
|
||||||
|
|
||||||
if(InpShowWeekly && t_week != t_p_week)
|
|
||||||
{
|
{
|
||||||
CreateLevelObject(time[i-1], calc_weekly[i-1], "W", InpWeeklyColor, InpWeeklyCount);
|
CaptureHistoricLevel(time[i - 1], calc_custom[i - 1], "PS-VWAP", InpCustomColor, InpCustomStyle, InpCustomWidth, InpCustomCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monthly
|
|
||||||
if(InpShowMonthly && dt.mon != dt_prev.mon)
|
|
||||||
{
|
|
||||||
CreateLevelObject(time[i-1], calc_monthly[i-1], "M", InpMonthlyColor, InpMonthlyCount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return(rates_total);
|
return rates_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Helpers |
|
//| Capture and Project Historic VWAP Level Ray |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
|
void CaptureHistoricLevel(const datetime time_close,
|
||||||
// Updated MergeBuffer to respect start index
|
const double price,
|
||||||
void MergeBuffers(int total, const double &src1[], const double &src2[], double &dst[], int start)
|
const string tag,
|
||||||
|
const color clr,
|
||||||
|
const ENUM_LINE_STYLE style,
|
||||||
|
const int width,
|
||||||
|
const int limit_count)
|
||||||
{
|
{
|
||||||
for(int i=start; i<total; i++)
|
if(price <= 0.0 || price == EMPTY_VALUE)
|
||||||
{
|
|
||||||
if(src1[i] != EMPTY_VALUE && src1[i] != 0)
|
|
||||||
dst[i] = src1[i];
|
|
||||||
else
|
|
||||||
dst[i] = src2[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
|
||||||
//| |
|
|
||||||
//+------------------------------------------------------------------+
|
|
||||||
void MergeBuffers(int total, const double &src1[], const double &src2[], double &dst[])
|
|
||||||
{
|
|
||||||
for(int i=0; i<total; i++)
|
|
||||||
{
|
|
||||||
if(src1[i] != EMPTY_VALUE && src1[i] != 0)
|
|
||||||
dst[i] = src1[i];
|
|
||||||
else
|
|
||||||
dst[i] = src2[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
|
||||||
//| Object Creation Logic |
|
|
||||||
//+------------------------------------------------------------------+
|
|
||||||
void CreateLevelObject(datetime time_start, double price, string suffix, color clr, int limit_count)
|
|
||||||
{
|
|
||||||
if(price == 0 || price == EMPTY_VALUE)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Name includes specific Date to be unique
|
string name_prefix = g_prefix_line + tag + "_";
|
||||||
string name_prefix = "VLevel_" + suffix + "_";
|
string obj_name = name_prefix + TimeToString(time_close, TIME_DATE | TIME_MINUTES);
|
||||||
string name = name_prefix + TimeToString(time_start, TIME_DATE); // Unique per day/week
|
|
||||||
|
|
||||||
if(ObjectFind(0, name) >= 0)
|
if(ObjectFind(0, obj_name) >= 0)
|
||||||
return; // Already exists
|
return; // Already registered
|
||||||
|
|
||||||
// Cleanup before creating new
|
// Clean older rays before creating new one
|
||||||
CleanOldObjects(name_prefix, limit_count);
|
CleanOldObjects(name_prefix, limit_count);
|
||||||
|
|
||||||
ObjectCreate(0, name, OBJ_TREND, 0, time_start, price, TimeCurrent()+PeriodSeconds()*100, price);
|
datetime t_end = time_close + PeriodSeconds() * 100;
|
||||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
|
||||||
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
|
|
||||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
|
||||||
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
|
|
||||||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
||||||
|
|
||||||
// Label
|
// 1. Create Full-Workspace Extended Ray Line
|
||||||
string text_name = name + "_txt";
|
ObjectCreate(0, obj_name, OBJ_TREND, 0, time_close, price, t_end, price);
|
||||||
ObjectCreate(0, text_name, OBJ_TEXT, 0, time_start, price);
|
ObjectSetInteger(0, obj_name, OBJPROP_RAY_RIGHT, true);
|
||||||
ObjectSetString(0, text_name, OBJPROP_TEXT, " " + suffix + " " + DoubleToString(price, _Digits));
|
ObjectSetInteger(0, obj_name, OBJPROP_RAY_LEFT, false);
|
||||||
ObjectSetInteger(0, text_name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
|
ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);
|
||||||
ObjectSetInteger(0, text_name, OBJPROP_COLOR, clr);
|
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clr);
|
||||||
ObjectSetInteger(0, text_name, OBJPROP_FONTSIZE, 8);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Garbage Collection (Clears old lines to keep chart clean) |
|
//| High-Performance Garbage Collector (Pre-allocated Array GC) |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void CleanOldObjects(string prefix, int max_count)
|
void CleanOldObjects(const string prefix, const int max_count)
|
||||||
{
|
{
|
||||||
string objects[];
|
int total_objs = ObjectsTotal(0);
|
||||||
int count = 0;
|
if(total_objs < 1)
|
||||||
int total = ObjectsTotal(0);
|
return;
|
||||||
|
|
||||||
// Collect
|
string objects[];
|
||||||
for(int i=0; i<total; i++)
|
ArrayResize(objects, total_objs);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for(int i = 0; i < total_objs; i++)
|
||||||
{
|
{
|
||||||
string n = ObjectName(0, i);
|
string n = ObjectName(0, i);
|
||||||
if(StringFind(n, prefix) == 0 && StringFind(n, "_txt") == -1)
|
if(StringFind(n, prefix) == 0 && StringFind(n, g_prefix_lbl) == -1)
|
||||||
{
|
{
|
||||||
ArrayResize(objects, count+1);
|
|
||||||
objects[count] = n;
|
objects[count] = n;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete Oldest
|
|
||||||
if(count >= max_count)
|
if(count >= max_count)
|
||||||
{
|
{
|
||||||
ArraySort(objects); // String sort works for YYYY.MM.DD
|
ArrayResize(objects, count);
|
||||||
|
ArraySort(objects); // Lexicographical sort by Date/Time string
|
||||||
|
|
||||||
int to_delete = count - max_count + 1;
|
int to_delete = count - max_count + 1;
|
||||||
for(int i=0; i<to_delete; i++)
|
for(int i = 0; i < to_delete; i++)
|
||||||
{
|
{
|
||||||
ObjectDelete(0, objects[i]);
|
string line_name = objects[i];
|
||||||
ObjectDelete(0, objects[i] + "_txt");
|
string lbl_name = line_name;
|
||||||
|
StringReplace(lbl_name, g_prefix_line, g_prefix_lbl);
|
||||||
|
|
||||||
|
ObjectDelete(0, line_name);
|
||||||
|
ObjectDelete(0, lbl_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user