From e11aa6d3fdb6e47436b22222bbd547f61aa259ab Mon Sep 17 00:00:00 2001 From: EarnForex <48102957+EarnForex@users.noreply.github.com> Date: Wed, 7 Dec 2022 20:50:09 +0100 Subject: [PATCH] 1.20 1. Added historical arrow alerts. 2. Fixed bugs with rays and alerts. 3. Changed the default value for the AlertCheckBar input parameter from Current to Previous. --- MarketProfile.cs | 253 +++++++++++++++++++++++++++++++-------- MarketProfile.mq4 | 299 ++++++++++++++++++++++++++++++++++------------ MarketProfile.mq5 | 296 +++++++++++++++++++++++++++++++++------------ 3 files changed, 646 insertions(+), 202 deletions(-) diff --git a/MarketProfile.cs b/MarketProfile.cs index 75503c4..c65f42e 100644 --- a/MarketProfile.cs +++ b/MarketProfile.cs @@ -8,7 +8,7 @@ // Intraday - should be attached to M1-M15 timeframes. M5 is recommended. // Designed for major currency pairs, but should work also with exotic pairs, CFDs, or commodities. // -// Version 1.19 +// Version 1.20 // Copyright 2010-2022, EarnForex.com // https://www.earnforex.com/metatrader-indicators/MarketProfile/ // ------------------------------------------------------------------------------- @@ -191,7 +191,7 @@ public class MarketProfile : Indicator [Parameter("AlertArrows: draw chart arrows on alerts.", DefaultValue = false)] public bool AlertArrows { get; set; } - [Parameter("AlertCheckBar: which bar to check for alerts?", DefaultValue = alert_check_bar.CheckCurrentBar)] + [Parameter("AlertCheckBar: which bar to check for alerts?", DefaultValue = alert_check_bar.CheckPreviousBar)] public alert_check_bar AlertCheckBar { get; set; } [Parameter("AlertForValueArea: alerts for Value Area (VAH, VAL) rays.", DefaultValue = false)] @@ -212,6 +212,15 @@ public class MarketProfile : Indicator [Parameter("AlertOnGapCross: bar gap above/below the ray.", DefaultValue = false)] public bool AlertOnGapCross { get; set; } + [Parameter("AlertArrowColorPB: arrow color for price break alerts.", DefaultValue = "Red")] + public string AlertArrowColorPB { get; set; } + + [Parameter("AlertArrowColorCC: arrow color for candle close alerts.", DefaultValue = "Blue")] + public string AlertArrowColorCC { get; set; } + + [Parameter("AlertArrowColorGC: arrow color for gap crossover alerts.", DefaultValue = "Yellow")] + public string AlertArrowColorGC { get; set; } + [Parameter("=== Intraday settings", DefaultValue = "=================")] public string IntradaySettings { get; set; } @@ -290,15 +299,6 @@ public class MarketProfile : Indicator [Output("Developing POC 2", LineColor = "Green", LineStyle = LineStyle.Solid, PlotType = PlotType.DiscontinuousLine, Thickness = 5)] public IndicatorDataSeries DevelopingPOC_2 { get; set; } - [Output("Price break", LineColor = "Red", PlotType = PlotType.Points, Thickness = 5)] - public IndicatorDataSeries ArrowsPB { get; set; } - - [Output("Candle close crossover", LineColor = "Blue", PlotType = PlotType.Points, Thickness = 5)] - public IndicatorDataSeries ArrowsCC { get; set; } - - [Output("Gap crossover", LineColor = "Yellow", PlotType = PlotType.Points, Thickness = 5)] - public IndicatorDataSeries ArrowsGC { get; set; } - #endregion #region Enums @@ -407,6 +407,7 @@ private class SessionInfo public double Max; public double Min; public DateTime Start; + public DateTime End; public string Suffix; public SessionInfo() { } @@ -488,6 +489,7 @@ public Intraday() private DateTime LastAlertTime_GapCross = DateTime.MinValue; // For CheckCurrentBar alerts. private DateTime LastAlertTime = DateTime.MinValue; // For CheckPreviousBar alerts; private double Close_prev = double.NaN; // Previous price value for Price Break alerts. + private int ArrowsCounter = 0; // Counter for naming of alert arrows. // Used for ColorBullBear. private bar_direction CurrentBarDirection = bar_direction.Neutral; @@ -893,7 +895,7 @@ public override void Calculate(int index) } } - if (ShowValueAreaRays != sessions_to_draw_rays.None || ShowMedianRays != sessions_to_draw_rays.None) + if ((ShowValueAreaRays != sessions_to_draw_rays.None) || (ShowMedianRays != sessions_to_draw_rays.None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); FirstRunDone = true; @@ -937,15 +939,12 @@ protected override void OnTimer() if (Session == session_period.Intraday) FirstRunDone = false; // Turn off because FirstRunDone should be false for Intraday sessions to draw properly in the past. - if (EnableDevelopingPOC || AlertArrows) + if (EnableDevelopingPOC) { for (int i = Bars.Count - 1; i >= 0; i--) // Clean indicator buffers. { DevelopingPOC_1[i] = double.NaN; DevelopingPOC_2[i] = double.NaN; - ArrowsPB[i] = double.NaN; - ArrowsCC[i] = double.NaN; - ArrowsGC[i] = double.NaN; } } } @@ -1633,7 +1632,7 @@ private void CheckRectangles() for (int i = 0; i < MPR_Array.Count; i++) MPR_Process(i); - if (ShowValueAreaRays != sessions_to_draw_rays.None || ShowMedianRays != sessions_to_draw_rays.None) + if ((ShowValueAreaRays != sessions_to_draw_rays.None) || (ShowMedianRays != sessions_to_draw_rays.None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); LastRecalculationTime = DateTime.Now; // Remember last calculation time. @@ -1806,7 +1805,10 @@ private void MPR_Process(int i) RememberSession.Add(new SessionInfo()); RememberSession[RememberSession.Count - 1].Start = mp.RectangleTimeMin; - + // Used only for Arrows: + if (Bars[Bars.Count - 1].OpenTime < mp.RectangleTimeMax) RememberSession[RememberSession.Count - 1].End = Bars[Bars.Count - 1].OpenTime; + else RememberSession[RememberSession.Count - 1].End = mp.RectangleTimeMax; + if (!new_bars_are_not_within_rectangle || current_bar_changed_within_boundaries || rectangle_changed_and_recalc_is_due || (EnableDevelopingPOC && rectangle_changed) || (mp.Number != i && RaysUntilIntersection != ways_to_stop_rays.Stop_No_Rays && (ShowMedianRays != sessions_to_draw_rays.None || ShowValueAreaRays != sessions_to_draw_rays.None))) @@ -1844,7 +1846,9 @@ void ObjectCleanup(string rectangle_prefix = "") if (chart_texts[i].Name.StartsWith(rectangle_prefix)) Chart.RemoveObject(chart_texts[i].Name); } - } + + DeleteArrowsByPrefix(rectangle_prefix); +} #endregion @@ -2007,6 +2011,7 @@ private bool ProcessSession(int sessionstart, int sessionend, int i, CRectangleM RememberSession[session_counter].Max = SessionMax; RememberSession[session_counter].Min = SessionMin; RememberSession[session_counter].Start = Bars[sessionstart].OpenTime; + RememberSession[session_counter].End = Bars[sessionend].OpenTime; // Used only for Arrows. RememberSession[session_counter].Suffix = Suffix; // Reset PreviousSessionMax when a new session becomes the 'latest one'. @@ -2879,32 +2884,187 @@ private void CheckRays() Chart.RemoveObject(va_lowray_name); } - if (RaysUntilIntersection == ways_to_stop_rays.Stop_No_Rays) - continue; - - if (((ShowMedianRays == sessions_to_draw_rays.Previous || ShowMedianRays == sessions_to_draw_rays.PreviousCurrent) && SessionsNumber - i == 2) || - ((ShowMedianRays == sessions_to_draw_rays.AllPrevious || ShowMedianRays == sessions_to_draw_rays.All) && SessionsNumber - i >= 2)) + if (RaysUntilIntersection != ways_to_stop_rays.Stop_No_Rays) { - if (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays || - (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays_Except_Prev_Session && SessionsNumber - i > 2) || - (RaysUntilIntersection == ways_to_stop_rays.Stop_Only_Previous_Session && SessionsNumber - i == 2)) - CheckRayIntersections(median_ray_name, i + 1); + if (((ShowMedianRays == sessions_to_draw_rays.Previous || ShowMedianRays == sessions_to_draw_rays.PreviousCurrent) && SessionsNumber - i == 2) || + ((ShowMedianRays == sessions_to_draw_rays.AllPrevious || ShowMedianRays == sessions_to_draw_rays.All) && SessionsNumber - i >= 2)) + { + if (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays || + (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays_Except_Prev_Session && SessionsNumber - i > 2) || + (RaysUntilIntersection == ways_to_stop_rays.Stop_Only_Previous_Session && SessionsNumber - i == 2)) + CheckRayIntersections(median_ray_name, i + 1); + } + + if (((ShowValueAreaRays == sessions_to_draw_rays.Previous || ShowValueAreaRays == sessions_to_draw_rays.PreviousCurrent) && SessionsNumber - i == 2) || + ((ShowValueAreaRays == sessions_to_draw_rays.AllPrevious || ShowValueAreaRays == sessions_to_draw_rays.All) && SessionsNumber - i >= 2)) + { + if (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays || + (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays_Except_Prev_Session && SessionsNumber - i > 2) || + (RaysUntilIntersection == ways_to_stop_rays.Stop_Only_Previous_Session && SessionsNumber - i == 2)) + { + CheckRayIntersections(va_highray_name, i + 1); + CheckRayIntersections(va_lowray_name, i + 1); + } + } } - if (((ShowValueAreaRays == sessions_to_draw_rays.Previous || ShowValueAreaRays == sessions_to_draw_rays.PreviousCurrent) && SessionsNumber - i == 2) || - ((ShowValueAreaRays == sessions_to_draw_rays.AllPrevious || ShowValueAreaRays == sessions_to_draw_rays.All) && SessionsNumber - i >= 2)) + // Historical arrow placement. + // Here we are inside a cycle through all sessions. + // For each session, we will pass its rays and add arrows if they are visible. + // Before that, it's best to check if any arrows have already been created for this session. If they have, skip. + if (AlertArrows) { - if (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays || - (RaysUntilIntersection == ways_to_stop_rays.Stop_All_Rays_Except_Prev_Session && SessionsNumber - i > 2) || - (RaysUntilIntersection == ways_to_stop_rays.Stop_Only_Previous_Session && SessionsNumber - i == 2)) + // We will start checking from the next bar after session's end because previous crosses could be at different places in an uncompleted session. + int bar_start = Bars.OpenTimes.GetIndexByTime(RememberSession[i].End) + 1; // Same for all rays of this session. + + // Single Print rays. + if (AlertForSinglePrint) + { + foreach (var ctl in Chart.FindAllObjects()) + { + string obj_name = ctl.Name; + string mpspr_prefix = rec_name + "MPSPR" + suffix + last_name; + if (!obj_name.StartsWith(mpspr_prefix)) continue; // Not a Single Print ray (or not this seesion's). + if (ctl.Color != Color.Transparent) // Visible. + { + // Proceed only if no arrow has been found for this ray. + if (!FindAtLeastOneArrowForRay(mpspr_prefix)) + { + for (int k = bar_start; k < Bars.Count; k++) // Check all bars for the given single print ray. + { + CheckAndDrawArrow(k, ctl.Y1, mpspr_prefix); + } + } + } + else // Invisible. + { + DeleteArrowsByPrefix(mpspr_prefix); // Delete all arrows generated by this ray. + } + } + } + + // Value Area rays. + if (AlertForValueArea) { - CheckRayIntersections(va_highray_name, i + 1); - CheckRayIntersections(va_lowray_name, i + 1); + string obj_prefix = rec_name + "Value Area HighRay" + suffix + last_name; + ChartTrendLine va_highray_tl = Chart.FindObject(obj_prefix) as ChartTrendLine; + if (va_highray_tl != null) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + obj_prefix = rec_name + "Value Area LowRay" + suffix + last_name; + ChartTrendLine va_lowray_tl = Chart.FindObject(obj_prefix) as ChartTrendLine; + if (va_lowray_tl != null) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } + + // Median rays. + if (AlertForMedian) + { + string obj_prefix = rec_name + "Median Ray" + suffix + last_name; + ChartTrendLine va_medianray_tl = Chart.FindObject(obj_prefix) as ChartTrendLine; + if (va_medianray_tl != null) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } } } } } + private void DeleteArrowsByPrefix(string prefix) + { + var chart_icons = Chart.FindAllObjects(ChartObjectType.Icon); + for (int i = chart_icons.Length - 1; i >= 0; i--) + { + if ((chart_icons[i].Name.StartsWith("ArrPB" + prefix)) || + (chart_icons[i].Name.StartsWith("ArrCC" + prefix)) || + (chart_icons[i].Name.StartsWith("ArrGC" + prefix))) + Chart.RemoveObject(chart_icons[i].Name); + } + } + + // Returns true if at least one arrow is found, false otherwise. + private bool FindAtLeastOneArrowForRay(string ray_name) + { + var chart_icons = Chart.FindAllObjects(ChartObjectType.Icon); + for (int i = chart_icons.Length - 1; i >= 0; i--) + { + if (chart_icons[i].Name.Contains(ray_name)) return true; // Found an rrrow for a given ray. + } + return false; // No arrows found for the ray. + } + + // Checks and draws historical arrow alerts for Median or Value Area ray. + private void CheckHistoricalArrowsForNonMPSPRRays(int bar_start, string ray_name) + { + int end_bar = Bars.Count - 1; + ChartTrendLine ray = Chart.FindObject(ray_name) as ChartTrendLine; + if (ray.ExtendToInfinity != true) // Ray was stopped, need to find its end. + { + DateTime end_time = ray.Time2; + end_bar = Bars.OpenTimes.GetIndexByTime(end_time) - 1; // End before the new session starts. + } + for (int k = bar_start; k <= end_bar; k++) // Check all bars for the given ray. + { + CheckAndDrawArrow(k, ray.Y1, ray_name); + } + } + + // Checks if any of the arrow alerts triggered on a given candle (n) with a given ray's level and places a chart object using name. + private void CheckAndDrawArrow(int n, double level, string ray_name) + { + // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. + if (AlertOnPriceBreak) // Price break alerts. + { + if (((Bars[n].High >= level) && (Bars[n].Close < level) && (Bars[n - 1].Close < level)) || ((Bars[n].Low <= level) && (Bars[n].Close > level) && (Bars[n - 1].Close > level))) + { + // Draw arrow object: + string obj_name = "ArrPB" + ray_name; + CreateArrowObject(obj_name, Bars[n].OpenTime, Bars[n].Close, AlertArrowColorPB, ChartIconType.Circle); + } + } + if (AlertOnCandleClose) // Candle close alerts. + { + if (((Bars[n].Close >= level) && (Bars[n - 1].Close < level)) || ((Bars[n].Close <= level) && (Bars[n - 1].Close > level))) + { + // Draw arrow object: + string obj_name = "ArrCC" + ray_name; + CreateArrowObject(obj_name, Bars[n].OpenTime, Bars[n].Close, AlertArrowColorCC, ChartIconType.Square); + } + } + if (AlertOnGapCross) // Gap cross alerts. + { + if (((Bars[n].Low > level) && (Bars[n - 1].High < level)) || ((Bars[n - 1].Low > level) && (Bars[n].High < level))) + { + string obj_name = "ArrGC" + ray_name; + CreateArrowObject(obj_name, Bars[n].OpenTime, level, AlertArrowColorGC, ChartIconType.Diamond); + } + } + } + + // Creates an arrow object and sets its properties. + void CreateArrowObject(string name, DateTime time, double price, string colour, ChartIconType type) + { + string obj_name = name + ArrowsCounter.ToString(); + ArrowsCounter++; + Chart.DrawIcon(obj_name, type, time, price, Color.FromName(colour)); + } + #endregion #region CheckRayIntersections @@ -3103,6 +3263,7 @@ private void RedrawLastSession() sessionstart = FindSessionStart(sessionend); } + SessionsNumber = 0; // Reset previously remembered sessions as there won't be any need for them. } // We begin from the oldest session coming to the current session or to StartFromDate. @@ -3154,7 +3315,7 @@ private void RedrawLastSession() } } - if (ShowValueAreaRays != sessions_to_draw_rays.None || ShowMedianRays != sessions_to_draw_rays.None) + if ((ShowValueAreaRays != sessions_to_draw_rays.None) || (ShowMedianRays != sessions_to_draw_rays.None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); } @@ -3263,7 +3424,7 @@ private void CalculateDevelopingPOC(int sessionstart, int sessionend, CRectangle private void CheckAlerts(int index) { // No need to check further if no alert method is chosen. - if (!AlertNative && !AlertEmail)// && !AlertPush) + if (!AlertNative && !AlertEmail && !AlertArrows)// && !AlertPush) return; // Skip alerts if alerts are disabled for Median, for Value Area, and for Single Print rays. @@ -3300,10 +3461,8 @@ private void CheckAlerts(int index) if (!double.IsNaN(Close_prev) && ((Bars[index].Close >= level && Close_prev < level) || (Bars[index].Close <= level && Close_prev > level))) { DoAlerts(alert_types.PriceBreak, object_name); - ArrowsPB[index] = Bars[index].Close; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Bars[index].OpenTime, Bars[index].Close, AlertArrowColorPB, ChartIconType.Circle); } - else - ArrowsPB[0] = double.NaN; Close_prev = Bars[index].Close; } @@ -3312,10 +3471,8 @@ private void CheckAlerts(int index) if ((Bars[index].Close >= level && Bars[index - 1].Close < level) || (Bars[index].Close <= level && Bars[index - 1].Close > level)) { DoAlerts(alert_types.CandleCloseCrossover, object_name); - ArrowsCC[index] = Bars[index].Close; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Bars[index].OpenTime, Bars[index].Close, AlertArrowColorCC, ChartIconType.Square); } - else - ArrowsCC[index] = double.NaN; } if (AlertOnGapCross) // Gap cross alerts. @@ -3323,10 +3480,8 @@ private void CheckAlerts(int index) if ((Bars[index].Open > level && Bars[index - 1].High < level) || (Bars[index].Open < level && Bars[index - 1].Low > level)) { DoAlerts(alert_types.GapCrossover, object_name); - ArrowsGC[index] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Bars[index].OpenTime, level, AlertArrowColorGC, ChartIconType.Diamond); } - else - ArrowsGC[index] = double.NaN; } } // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. @@ -3338,7 +3493,7 @@ private void CheckAlerts(int index) (Bars[index - 1].Low <= level && Bars[index - 1].Close > level && Bars[index - 2].Close > level)) { DoAlerts(alert_types.PriceBreak, object_name); - ArrowsPB[index - 1] = Bars[index - 1].Close; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Bars[index - 1].OpenTime, Bars[index - 1].Close, AlertArrowColorPB, ChartIconType.Circle); } } @@ -3347,7 +3502,7 @@ private void CheckAlerts(int index) if ((Bars[index - 1].Close >= level && Bars[index - 2].Close < level) || (Bars[index - 1].Close <= level && Bars[index - 2].Close > level)) { DoAlerts(alert_types.CandleCloseCrossover, object_name); - ArrowsCC[index - 1] = Bars[index - 1].Close; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Bars[index - 1].OpenTime, Bars[index - 1].Close, AlertArrowColorCC, ChartIconType.Square); } } @@ -3356,7 +3511,7 @@ private void CheckAlerts(int index) if ((Bars[index - 1].Low > level && Bars[index - 2].High < level) || (Bars[index - 2].Low > level && Bars[index - 1].High < level)) { DoAlerts(alert_types.GapCrossover, object_name); - ArrowsGC[index - 1] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Bars[index - 1].OpenTime, level, AlertArrowColorGC, ChartIconType.Diamond); } } diff --git a/MarketProfile.mq4 b/MarketProfile.mq4 index 75d0c7e..cd2edc9 100644 --- a/MarketProfile.mq4 +++ b/MarketProfile.mq4 @@ -5,7 +5,7 @@ //+------------------------------------------------------------------+ #property copyright "EarnForex.com" #property link "https://www.earnforex.com/metatrader-indicators/MarketProfile/" -#property version "1.19" +#property version "1.20" #property strict #property description "Displays the Market Profile indicator for intraday, daily, weekly, or monthly trading sessions." @@ -20,31 +20,18 @@ //+------------------------------------------------------------------+ #property indicator_chart_window // Two buffers are used for the Developing POC display because a single buffer wouldn't support an interrupting line. -// Three more buffers are for arrow alerts. -#property indicator_plots 5 -#property indicator_buffers 5 +#property indicator_plots 2 +#property indicator_buffers 2 #property indicator_color1 clrGreen #property indicator_color2 clrGreen -#property indicator_color3 clrRed -#property indicator_color4 clrBlue -#property indicator_color5 clrYellow #property indicator_width1 5 #property indicator_width2 5 -#property indicator_width3 5 -#property indicator_width4 5 -#property indicator_width5 5 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE -#property indicator_type3 DRAW_ARROW -#property indicator_type4 DRAW_ARROW -#property indicator_type5 DRAW_ARROW #property indicator_style1 STYLE_SOLID #property indicator_style2 STYLE_SOLID #property indicator_label1 "Developing POC" #property indicator_label2 "Developing POC" -#property indicator_label3 "Price break" -#property indicator_label4 "Candle close crossover" -#property indicator_label5 "Gap crossover" enum color_scheme { @@ -173,7 +160,7 @@ input bool AlertNative = false; // AlertNative: input bool AlertEmail = false; // AlertEmail: issue email alerts. input bool AlertPush = false; // AlertPush: issue push-notification alerts. input bool AlertArrows = false; // AlertArrows: draw chart arrows on alerts. -input alert_check_bar AlertCheckBar = CheckCurrentBar; // AlertCheckBar: which bar to check for alerts? +input alert_check_bar AlertCheckBar = CheckPreviousBar;// AlertCheckBar: which bar to check for alerts? input bool AlertForValueArea = false; // AlertForValueArea: alerts for Value Area (VAH, VAL) rays. input bool AlertForMedian = false; // AlertForMedian: alerts for POC (Median) rays' crossing. input bool AlertForSinglePrint = false; // AlertForSinglePrint: alerts for single print rays' crossing. @@ -183,6 +170,12 @@ input bool AlertOnGapCross = false; // AlertOnGapCr input int AlertArrowCodePB = 108; // AlertArrowCodePB: arrow code for price break alerts. input int AlertArrowCodeCC = 110; // AlertArrowCodeCC: arrow code for candle close alerts. input int AlertArrowCodeGC = 117; // AlertArrowCodeGC: arrow code for gap crossover alerts. +input color AlertArrowColorPB = clrRed; // AlertArrowColorPB: arrow color for price break alerts. +input color AlertArrowColorCC = clrBlue; // AlertArrowColorCC: arrow color for candle close alerts. +input color AlertArrowColorGC = clrYellow; // AlertArrowColorGC: arrow color for gap crossover alerts. +input int AlertArrowWidthPB = 1; // AlertArrowWidthPB: arrow width for price break alerts. +input int AlertArrowWidthCC = 1; // AlertArrowWidthCC: arrow width for candle close alerts. +input int AlertArrowWidthGC = 1; // AlertArrowWidthGC: arrow width for gap crossover alerts. input group "Intraday settings" input bool EnableIntradaySession1 = true; @@ -226,6 +219,7 @@ double ValueAreaPercentage_double = 0.7; // Will be calculated based on the inpu datetime LastAlertTime_CandleCross = 0, LastAlertTime_GapCross = 0; // For CheckCurrentBar alerts. datetime LastAlertTime = 0; // For CheckPreviousBar alerts; double Close_prev = EMPTY_VALUE; // Previous price value for Price Break alerts. +int ArrowsCounter = 0; // Counter for naming of alert arrows. // Used for ColorBullBear. bar_direction CurrentBarDirection = Neutral; @@ -249,6 +243,7 @@ int IntradayCrossSessionDefined = -1; // For special case used only with Ignore_ // These are used also when RaysUntilIntersection == Stop_No_Rays for Intraday sessions counting. double RememberSessionMax[], RememberSessionMin[]; datetime RememberSessionStart[]; +datetime RememberSessionEnd[]; // Used only for Arrows. string RememberSessionSuffix[]; int SessionsNumber = 0; // Different from _SessionsToCount when working with Intraday sessions and for RaysUntilIntersection != Stop_No_Rays. @@ -274,7 +269,6 @@ int mpr_total = 0; uint LastRecalculationTime = 0; double DevelopingPOC_1[], DevelopingPOC_2[]; // Indicator buffers for Developing POC. -double ArrowsPB[], ArrowsCC[], ArrowsGC[]; // Indicator buffers for alert arrows. //+------------------------------------------------------------------+ //| Custom indicator initialization function | @@ -410,15 +404,6 @@ int OnInit() PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); SetIndexBuffer(1, DevelopingPOC_2); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); - SetIndexBuffer(2, ArrowsPB); - PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(2, PLOT_ARROW, AlertArrowCodePB); - SetIndexBuffer(3, ArrowsCC); - PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(3, PLOT_ARROW, AlertArrowCodeCC); - SetIndexBuffer(4, ArrowsGC); - PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(4, PLOT_ARROW, AlertArrowCodeGC); ValueAreaPercentage_double = ValueAreaPercentage * 0.01; @@ -464,16 +449,13 @@ int OnCalculate(const int rates_total, } // New bars arrived? - if (((EnableDevelopingPOC) || (AlertArrows)) && (rates_total - prev_calculated > 1) && (CleanedUpOn != rates_total)) + if ((EnableDevelopingPOC) && (rates_total - prev_calculated > 1) && (CleanedUpOn != rates_total)) { // Initialize the indicator buffers. for (int i = prev_calculated; i < rates_total; i++) { DevelopingPOC_1[i] = EMPTY_VALUE; DevelopingPOC_2[i] = EMPTY_VALUE; - ArrowsPB[i] = EMPTY_VALUE; - ArrowsCC[i] = EMPTY_VALUE; - ArrowsGC[i] = EMPTY_VALUE; } CleanedUpOn = rates_total; // To prevent cleaning up the buffers again and again when the platform just starts. } @@ -596,7 +578,7 @@ int OnCalculate(const int rates_total, } } - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); FirstRunDone = true; @@ -1047,39 +1029,43 @@ datetime PutDot(const double price, const int start_bar, const int range, const void ObjectCleanup(string rectangle_prefix = "") { // Delete all rectangles with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "MP" + Suffix, EMPTY, OBJ_RECTANGLE); - ObjectsDeleteAll(0, rectangle_prefix + "Median" + Suffix, EMPTY, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_LeftSide" + Suffix, EMPTY, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_RightSide" + Suffix, EMPTY, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_Top" + Suffix, EMPTY, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_Bottom" + Suffix, EMPTY, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "MP" + Suffix, 0, OBJ_RECTANGLE); + ObjectsDeleteAll(0, rectangle_prefix + "Median" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_LeftSide" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_RightSide" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_Top" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_Bottom" + Suffix, 0, OBJ_TREND); if (ShowValueAreaRays != None) { // Delete all trendlines with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "Value Area HighRay" + Suffix, EMPTY, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "Value Area LowRay" + Suffix, EMPTY, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Value Area HighRay" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Value Area LowRay" + Suffix, 0, OBJ_TREND); } if (ShowMedianRays != None) { // Delete all trendlines with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "Median Ray" + Suffix, EMPTY, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Median Ray" + Suffix, 0, OBJ_TREND); } if (ShowKeyValues) { // Delete all text labels with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "VAH" + Suffix, EMPTY, OBJ_TEXT); - ObjectsDeleteAll(0, rectangle_prefix + "VAL" + Suffix, EMPTY, OBJ_TEXT); - ObjectsDeleteAll(0, rectangle_prefix + "POC" + Suffix, EMPTY, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "VAH" + Suffix, 0, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "VAL" + Suffix, 0, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "POC" + Suffix, 0, OBJ_TEXT); } if (ShowSinglePrint) { // Delete all Single Print marks. - ObjectsDeleteAll(0, rectangle_prefix + "MPSP" + Suffix, EMPTY, OBJ_RECTANGLE); + ObjectsDeleteAll(0, rectangle_prefix + "MPSP" + Suffix, 0, OBJ_RECTANGLE); } if (SinglePrintRays) { // Delete all Single Print rays. - ObjectsDeleteAll(0, rectangle_prefix + "MPSPR" + Suffix, EMPTY, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "MPSPR" + Suffix, 0, OBJ_TREND); + } + if (AlertArrows) + { + DeleteArrowsByPrefix(rectangle_prefix); } } @@ -1211,6 +1197,7 @@ bool ProcessSession(const int sessionstart, const int sessionend, const int i, C ArrayResize(RememberSessionMin, SessionsNumber); ArrayResize(RememberSessionStart, SessionsNumber); ArrayResize(RememberSessionSuffix, SessionsNumber); + ArrayResize(RememberSessionEnd, SessionsNumber); // Used only for Arrows. } } @@ -1221,6 +1208,7 @@ bool ProcessSession(const int sessionstart, const int sessionend, const int i, C RememberSessionMax[session_counter] = SessionMax; RememberSessionMin[session_counter] = SessionMin; RememberSessionStart[session_counter] = Time[sessionstart]; + RememberSessionEnd[session_counter] = Time[sessionend]; // Used only for Arrows. RememberSessionSuffix[session_counter] = Suffix; // Used to make sure that SessionMax increments only by 'onetick' increments. @@ -1979,7 +1967,6 @@ void CheckRays() double va_high_price = ObjectGetDouble(0, rec_name + "VA_Top" + suffix + last_name, OBJPROP_PRICE, 0); double va_low_price = ObjectGetDouble(0, rec_name + "VA_Bottom" + suffix + last_name, OBJPROP_PRICE, 0); datetime va_time = (datetime)ObjectGetInteger(0, rec_name + "VA_Top" + suffix + last_name, OBJPROP_TIME, 1); - // Create the rays only if the value area doesn't end behind the screen's edge. if (!((HideRaysFromInvisibleSessions) && (Time[WindowFirstVisibleBar()] >= va_time))) { @@ -2035,23 +2022,99 @@ void CheckRays() ObjectDelete(0, rec_name + "Value Area LowRay" + suffix + last_name); } - if (RaysUntilIntersection == Stop_No_Rays) continue; - - if ((((ShowMedianRays == Previous) || (ShowMedianRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowMedianRays == AllPrevious) || (ShowMedianRays == All)) && (SessionsNumber - i >= 2))) + if (RaysUntilIntersection != Stop_No_Rays) { - if ((RaysUntilIntersection == Stop_All_Rays) - || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) - || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) - CheckRayIntersections(rec_name + "Median Ray" + suffix + last_name, i + 1); + if ((((ShowMedianRays == Previous) || (ShowMedianRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowMedianRays == AllPrevious) || (ShowMedianRays == All)) && (SessionsNumber - i >= 2))) + { + if ((RaysUntilIntersection == Stop_All_Rays) + || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) + || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + CheckRayIntersections(rec_name + "Median Ray" + suffix + last_name, i + 1); + } + if ((((ShowValueAreaRays == Previous) || (ShowValueAreaRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowValueAreaRays == AllPrevious) || (ShowValueAreaRays == All)) && (SessionsNumber - i >= 2))) + { + if ((RaysUntilIntersection == Stop_All_Rays) + || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) + || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + { + CheckRayIntersections(rec_name + "Value Area HighRay" + suffix + last_name, i + 1); + CheckRayIntersections(rec_name + "Value Area LowRay" + suffix + last_name, i + 1); + } + } } - if ((((ShowValueAreaRays == Previous) || (ShowValueAreaRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowValueAreaRays == AllPrevious) || (ShowValueAreaRays == All)) && (SessionsNumber - i >= 2))) + + // Historical arrow placement. + // Here we are inside a cycle through all sessions. + // For each session, we will pass its rays and add arrows if they are visible. + // Before that, it's best to check if any arrows have already been created for this session. If they have, skip. + if (AlertArrows) { - if ((RaysUntilIntersection == Stop_All_Rays) - || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) - || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + // We will start checking from the next bar after session's end because previous crosses could be at different places in an uncompleted session. + int bar_start = iBarShift(Symbol(), Period(), RememberSessionEnd[i]) - 1; // Same for all rays of this session. + + // Single Print rays. + if (AlertForSinglePrint) + { + int obj_total = ObjectsTotal(ChartID(), 0, OBJ_TREND); + for (int j = 0; j < obj_total; j++) + { + string obj_name = ObjectName(ChartID(), j, 0, OBJ_TREND); + string obj_prefix = rec_name + "MPSPR" + suffix + last_name; + if (StringSubstr(obj_name, 0, StringLen(obj_prefix)) != obj_prefix) continue; // Not a Single Print ray (or not this seesion's). + if ((color)ObjectGetInteger(ChartID(), obj_name, OBJPROP_COLOR) != clrNONE) // Visible. + { + // Proceed only if no arrow has been found for this ray. + if (!FindAtLeastOneArrowForRay(obj_prefix)) + { + for (int k = bar_start; k >= 0; k--) // Check all bars for the given single print ray. + { + CheckAndDrawArrow(k, ObjectGetDouble(ChartID(), obj_name, OBJPROP_PRICE, 0), obj_prefix); + } + } + } + else // Invisible. + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } + } + + // Value Area rays. + if (AlertForValueArea) { - CheckRayIntersections(rec_name + "Value Area HighRay" + suffix + last_name, i + 1); - CheckRayIntersections(rec_name + "Value Area LowRay" + suffix + last_name, i + 1); + string obj_prefix = rec_name + "Value Area HighRay" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + + obj_prefix = rec_name + "Value Area LowRay" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } + + // Median rays. + if (AlertForMedian) + { + string obj_prefix = rec_name + "Median Ray" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } } } } @@ -2059,6 +2122,87 @@ void CheckRays() ChartRedraw(); } +// Delete arrows by prefix (complete or incomplete). +void DeleteArrowsByPrefix(const string prefix) +{ + // Delete all arrows. + ObjectsDeleteAll(ChartID(), "ArrCC" + prefix, 0, OBJ_ARROW); + ObjectsDeleteAll(ChartID(), "ArrGC" + prefix, 0, OBJ_ARROW); + ObjectsDeleteAll(ChartID(), "ArrPB" + prefix, 0, OBJ_ARROW); +} + +// Returns true if at least one arrow is found, false otherwise. +bool FindAtLeastOneArrowForRay(const string ray_name) +{ + int objects_total = ObjectsTotal(ChartID(), 0, OBJ_ARROW); + for (int i = 0; i < objects_total; i++) + { + string obj_name = ObjectName(ChartID(), i, 0, OBJ_ARROW); + if (StringFind(obj_name, ray_name) != -1) return true; // Found an rrrow for a given ray. + } + return false; // No arrows found for the ray. +} + +// Checks and draws historical arrow alerts for Median or Value Area ray. +void CheckHistoricalArrowsForNonMPSPRRays(const int bar_start, const string ray_name) +{ + int end_bar = 0; + if (ObjectGetInteger(ChartID(), ray_name, OBJPROP_RAY) != true) // Ray was stopped, need to find its end. + { + datetime end_time = (datetime)ObjectGetInteger(ChartID(), ray_name, OBJPROP_TIME, 1); + end_bar = iBarShift(Symbol(), Period(), end_time) + 1; // End before the new session starts. + } + for (int k = bar_start; k >= end_bar; k--) // Check all bars for the given ray. + { + CheckAndDrawArrow(k, ObjectGetDouble(ChartID(), ray_name, OBJPROP_PRICE, 0), ray_name); + } +} + +// Checks if any of the arrow alerts triggered on a given candle (n) with a given ray's level and places a chart object using name. +void CheckAndDrawArrow(const int n, const double level, const string ray_name) +{ + // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. + if (AlertOnPriceBreak) // Price break alerts. + { + if (((High[n] >= level) && (Close[n] < level) && (Close[n + 1] < level)) || ((Low[n] <= level) && (Close[n] > level) && (Close[n + 1] > level))) + { + // Draw arrow object: + string obj_name = "ArrPB" + ray_name; + CreateArrowObject(obj_name, Time[n], Close[n], AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); + } + } + if (AlertOnCandleClose) // Candle close alerts. + { + if (((Close[n] >= level) && (Close[n + 1] < level)) || ((Close[n] <= level) && (Close[n + 1] > level))) + { + // Draw arrow object: + string obj_name = "ArrCC" + ray_name; + CreateArrowObject(obj_name, Time[n], Close[n], AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); + } + } + if (AlertOnGapCross) // Gap cross alerts. + { + if (((Low[n] > level) && (High[n + 1] < level)) || ((Low[n + 1] > level) && (High[n] < level))) + { + string obj_name = "ArrGC" + ray_name; + CreateArrowObject(obj_name, Time[n], level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); + } + } +} + +// Creates an arrow object and sets its properties. +void CreateArrowObject(const string name, const datetime time, const double price, const int code, const color colour, const int width, const string tooltip) +{ + string obj_name = name + IntegerToString(ArrowsCounter); + ArrowsCounter++; + ObjectCreate(ChartID(), obj_name, OBJ_ARROW, 0, time, price); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_ARROWCODE, code); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_COLOR, colour); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_ANCHOR, ANCHOR_CENTER); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_WIDTH, width); + ObjectSetString(ChartID(), obj_name, OBJPROP_TOOLTIP, tooltip); +} + //+------------------------------------------------------------------+ //| Checks price intersection and cuts a ray for a given object. | //+------------------------------------------------------------------+ @@ -2099,7 +2243,6 @@ void ValuePrintOut(const string obj_name, const datetime time, const double pric ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, anchor); - //ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER); } // Should be updated anyway. ObjectSetString(0, obj_name, OBJPROP_TEXT, DoubleToString(price, _Digits)); @@ -2257,15 +2400,12 @@ void OnTimer() { ObjectCleanup(); // Delete everything to make sure there are no leftover sessions behind the screen. if (Session == Intraday) FirstRunDone = false; // Turn off because FirstRunDone should be false for Intraday sessions to draw properly in the past. - if ((EnableDevelopingPOC) || (AlertArrows)) + if (EnableDevelopingPOC) { for (int i = 0; i < Bars; i++) // Clean indicator buffers. { DevelopingPOC_1[i] = EMPTY_VALUE; DevelopingPOC_2[i] = EMPTY_VALUE; - ArrowsPB[i] = EMPTY_VALUE; - ArrowsCC[i] = EMPTY_VALUE; - ArrowsGC[i] = EMPTY_VALUE; } } } @@ -2400,13 +2540,14 @@ void CheckRectangles() ArrayResize(RememberSessionMin, SessionsNumber); ArrayResize(RememberSessionStart, SessionsNumber); ArrayResize(RememberSessionSuffix, SessionsNumber); + ArrayResize(RememberSessionEnd, SessionsNumber); // Used only for Arrows. } // Process each rectangle. for (int i = 0; i < mpr_total; i++) MPR_Array[i].Process(i); - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); LastRecalculationTime = GetTickCount(); // Remember last calculation time. } @@ -2564,6 +2705,10 @@ void CRectangleMP::Process(const int i) if (sessionstart < 0) return; // Rectangle is drawn in the future. RememberSessionStart[i] = RectangleTimeMin; + // Used only for Arrows: + if (Time[0] < RectangleTimeMax) RememberSessionEnd[i] = Time[0]; + else RememberSessionEnd[i] = RectangleTimeMax; + if ((!new_bars_are_not_within_rectangle) || (current_bar_changed_within_boundaries) || (rectangle_changed_and_recalc_is_due) || ((Number != i) && ((RaysUntilIntersection != Stop_No_Rays) && ((ShowMedianRays != None) || (ShowValueAreaRays != None))))) ProcessSession(sessionstart, sessionend, i, &this); @@ -2683,6 +2828,7 @@ void RedrawLastSession() } sessionstart = FindSessionStart(sessionend); } + SessionsNumber = 0; // Reset previously remembered sessions as there won't be any need for them. } // We begin from the oldest session coming to the current session or to StartFromDate. @@ -2723,7 +2869,7 @@ void RedrawLastSession() } } - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); } //+------------------------------------------------------------------+ @@ -2861,7 +3007,7 @@ void OnChartEvent(const int id, const long& lparam, const double& dparam, const void CheckAlerts() { // No need to check further if no alert method is chosen. - if ((!AlertNative) && (!AlertEmail) && (!AlertPush)) return; + if ((!AlertNative) && (!AlertEmail) && (!AlertPush) && (!AlertArrows)) return; // Skip alerts if alerts are disabled for Median, for Value Area, and for Single Print rays. if ((!AlertForMedian) && (!AlertForValueArea) && (!AlertForSinglePrint)) return; // Skip alerts if no cross type is chosen. @@ -2878,7 +3024,7 @@ void CheckAlerts() // Skip if it is either a non-ray or if this particular ray shouldn't get alerted. if (!(((AlertForMedian) && (StringFind(object_name, "Median Ray") > -1)) || ((AlertForValueArea) && ((StringFind(object_name, "Value Area HighRay") > -1) || (StringFind(object_name, "Value Area LowRay") > -1))) || - ((AlertForSinglePrint)&& (StringFind(object_name, "MPSPR") > -1) && (ObjectGetInteger(ChartID(), object_name, OBJPROP_COLOR) != clrNONE)))) continue; + ((AlertForSinglePrint)&& (StringFind(object_name, "MPSPR") > -1) && ((color)ObjectGetInteger(ChartID(), object_name, OBJPROP_COLOR) != clrNONE)))) continue; // If everything is fine, go on: @@ -2892,9 +3038,8 @@ void CheckAlerts() if ((Close_prev != EMPTY_VALUE) && (((Close[0] >= level) && (Close_prev < level)) || ((Close[0] <= level) && (Close_prev > level)))) { DoAlerts(PriceBreak, object_name); - ArrowsPB[0] = Close[0]; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Time[0], Close[0], AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); } - else ArrowsPB[0] = EMPTY_VALUE; Close_prev = Close[0]; } if (AlertOnCandleClose) // Candle close alerts. @@ -2902,18 +3047,16 @@ void CheckAlerts() if (((Close[0] >= level) && (Close[1] < level)) || ((Close[0] <= level) && (Close[1] > level))) { DoAlerts(CandleCloseCrossover, object_name); - ArrowsCC[0] = Close[0]; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Time[0], Close[0], AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); } - else ArrowsCC[0] = EMPTY_VALUE; } if (AlertOnGapCross) // Gap cross alerts. { if (((Open[0] > level) && (High[1] < level)) || ((Open[0] < level) && (Low[1] > level))) { DoAlerts(GapCrossover, object_name); - ArrowsGC[0] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Time[0], level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); } - else ArrowsGC[0] = EMPTY_VALUE; } } // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. @@ -2924,7 +3067,7 @@ void CheckAlerts() if (((High[1] >= level) && (Close[1] < level) && (Close[2] < level)) || ((Low[1] <= level) && (Close[1] > level) && (Close[2] > level))) { DoAlerts(PriceBreak, object_name); - ArrowsPB[1] = Close[1]; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Time[1], Close[1], AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); } } if (AlertOnCandleClose) // Candle close alerts. @@ -2932,7 +3075,7 @@ void CheckAlerts() if (((Close[1] >= level) && (Close[2] < level)) || ((Close[1] <= level) && (Close[2] > level))) { DoAlerts(CandleCloseCrossover, object_name); - ArrowsCC[1] = Close[1]; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Time[1], Close[1], AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); } } if (AlertOnGapCross) // Gap cross alerts. @@ -2940,7 +3083,7 @@ void CheckAlerts() if (((Low[1] > level) && (High[2] < level)) || ((Low[2] > level) && (High[1] < level))) { DoAlerts(GapCrossover, object_name); - ArrowsGC[1] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Time[1], level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); } } LastAlertTime = Time[0]; diff --git a/MarketProfile.mq5 b/MarketProfile.mq5 index 7439c17..9ba5bd4 100644 --- a/MarketProfile.mq5 +++ b/MarketProfile.mq5 @@ -5,7 +5,7 @@ //+------------------------------------------------------------------+ #property copyright "EarnForex.com" #property link "https://www.earnforex.com/metatrader-indicators/MarketProfile/" -#property version "1.19" +#property version "1.20" #property description "Displays the Market Profile indicator for intraday, daily, weekly, or monthly trading sessions." #property description "Daily - should be attached to M5-M30 timeframes. M30 is recommended." @@ -19,30 +19,18 @@ //+------------------------------------------------------------------+ #property indicator_chart_window // Two buffers are used for the Developing POC display because a single buffer wouldn't support an interrupting line. -#property indicator_plots 5 -#property indicator_buffers 5 +#property indicator_plots 2 +#property indicator_buffers 2 #property indicator_color1 clrGreen #property indicator_color2 clrGreen -#property indicator_color3 clrRed -#property indicator_color4 clrBlue -#property indicator_color5 clrYellow #property indicator_width1 5 #property indicator_width2 5 -#property indicator_width3 5 -#property indicator_width4 5 -#property indicator_width5 5 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE -#property indicator_type3 DRAW_ARROW -#property indicator_type4 DRAW_ARROW -#property indicator_type5 DRAW_ARROW #property indicator_style1 STYLE_SOLID #property indicator_style2 STYLE_SOLID #property indicator_label1 "Developing POC" #property indicator_label2 "Developing POC" -#property indicator_label3 "Price break" -#property indicator_label4 "Candle close crossover" -#property indicator_label5 "Gap crossover" enum color_scheme { @@ -171,7 +159,7 @@ input bool AlertNative = false; // AlertNative: input bool AlertEmail = false; // AlertEmail: issue email alerts. input bool AlertPush = false; // AlertPush: issue push-notification alerts. input bool AlertArrows = false; // AlertArrows: draw chart arrows on alerts. -input alert_check_bar AlertCheckBar = CheckCurrentBar; // AlertCheckBar: which bar to check for alerts? +input alert_check_bar AlertCheckBar = CheckPreviousBar;// AlertCheckBar: which bar to check for alerts? input bool AlertForValueArea = false; // AlertForValueArea: alerts for Value Area (VAH, VAL) rays. input bool AlertForMedian = false; // AlertForMedian: alerts for POC (Median) rays' crossing. input bool AlertForSinglePrint = false; // AlertForSinglePrint: alerts for single print rays' crossing. @@ -181,6 +169,12 @@ input bool AlertOnGapCross = false; // AlertOnGapCr input int AlertArrowCodePB = 108; // AlertArrowCodePB: arrow code for price break alerts. input int AlertArrowCodeCC = 110; // AlertArrowCodeCC: arrow code for candle close alerts. input int AlertArrowCodeGC = 117; // AlertArrowCodeGC: arrow code for gap crossover alerts. +input color AlertArrowColorPB = clrRed; // AlertArrowColorPB: arrow color for price break alerts. +input color AlertArrowColorCC = clrBlue; // AlertArrowColorCC: arrow color for candle close alerts. +input color AlertArrowColorGC = clrYellow; // AlertArrowColorGC: arrow color for gap crossover alerts. +input int AlertArrowWidthPB = 1; // AlertArrowWidthPB: arrow width for price break alerts. +input int AlertArrowWidthCC = 1; // AlertArrowWidthCC: arrow width for candle close alerts. +input int AlertArrowWidthGC = 1; // AlertArrowWidthGC: arrow width for gap crossover alerts. input group "Intraday settings" input bool EnableIntradaySession1 = true; @@ -242,11 +236,13 @@ int IntradayCrossSessionDefined = -1; // For special case used only with Ignore_ datetime LastAlertTime_CandleCross = 0, LastAlertTime_GapCross = 0; // For CheckCurrentBar alerts. datetime LastAlertTime = 0; // For CheckPreviousBar alerts; double Close_prev = EMPTY_VALUE; // Previous price value for Price Break alerts. +int ArrowsCounter = 0; // Counter for naming of alert arrows. // We need to know where each session starts and its price range for when RaysUntilIntersection != Stop_No_Rays. // These are used also when RaysUntilIntersection == Stop_No_Rays for Intraday sessions counting. double RememberSessionMax[], RememberSessionMin[]; datetime RememberSessionStart[]; +datetime RememberSessionEnd[]; // Used only for Arrows. string RememberSessionSuffix[]; int SessionsNumber = 0; // Different from _SessionsToCount when working with Intraday sessions and for RaysUntilIntersection != Stop_No_Rays. @@ -272,7 +268,6 @@ int mpr_total = 0; uint LastRecalculationTime = 0; double DevelopingPOC_1[], DevelopingPOC_2[]; // Indicator buffers for Developing POC. -double ArrowsPB[], ArrowsCC[], ArrowsGC[]; // Indicator buffers for alert arrows. //+------------------------------------------------------------------+ //| Custom indicator initialization function | @@ -422,15 +417,6 @@ int OnInit() PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); SetIndexBuffer(1, DevelopingPOC_2); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); - SetIndexBuffer(2, ArrowsPB); - PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(2, PLOT_ARROW, AlertArrowCodePB); - SetIndexBuffer(3, ArrowsCC); - PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(3, PLOT_ARROW, AlertArrowCodeCC); - SetIndexBuffer(4, ArrowsGC); - PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); - PlotIndexSetInteger(4, PLOT_ARROW, AlertArrowCodeGC); ValueAreaPercentage_double = ValueAreaPercentage * 0.01; @@ -476,16 +462,13 @@ int OnCalculate(const int rates_total, } // New bars arrived? - if (((EnableDevelopingPOC) || (AlertArrows)) && (rates_total - prev_calculated > 1) && (CleanedUpOn != rates_total)) + if ((EnableDevelopingPOC) && (rates_total - prev_calculated > 1) && (CleanedUpOn != rates_total)) { // Initialize the indicator buffers. for (int i = prev_calculated; i < rates_total; i++) { DevelopingPOC_1[i] = EMPTY_VALUE; DevelopingPOC_2[i] = EMPTY_VALUE; - ArrowsPB[i] = EMPTY_VALUE; - ArrowsCC[i] = EMPTY_VALUE; - ArrowsGC[i] = EMPTY_VALUE; } CleanedUpOn = rates_total; // To prevent cleaning up the buffers again and again when the platform just starts. } @@ -621,7 +604,7 @@ int OnCalculate(const int rates_total, } } - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); FirstRunDone = true; @@ -1081,39 +1064,43 @@ datetime PutDot(const double price, const int start_bar, const int range, const void ObjectCleanup(string rectangle_prefix = "") { // Delete all rectangles with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "MP" + Suffix, -1, OBJ_RECTANGLE); - ObjectsDeleteAll(0, rectangle_prefix + "Median" + Suffix, -1, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_LeftSide" + Suffix, -1, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_RightSide" + Suffix, -1, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_Top" + Suffix, -1, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "VA_Bottom" + Suffix, -1, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "MP" + Suffix, 0, OBJ_RECTANGLE); + ObjectsDeleteAll(0, rectangle_prefix + "Median" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_LeftSide" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_RightSide" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_Top" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "VA_Bottom" + Suffix, 0, OBJ_TREND); if (ShowValueAreaRays != None) { // Delete all trendlines with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "Value Area HighRay" + Suffix, -1, OBJ_TREND); - ObjectsDeleteAll(0, rectangle_prefix + "Value Area LowRay" + Suffix, -1, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Value Area HighRay" + Suffix, 0, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Value Area LowRay" + Suffix, 0, OBJ_TREND); } if (ShowMedianRays != None) { // Delete all trendlines with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "Median Ray" + Suffix, -1, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "Median Ray" + Suffix, 0, OBJ_TREND); } if (ShowKeyValues) { // Delete all text labels with set prefix. - ObjectsDeleteAll(0, rectangle_prefix + "VAH" + Suffix, -1, OBJ_TEXT); - ObjectsDeleteAll(0, rectangle_prefix + "VAL" + Suffix, -1, OBJ_TEXT); - ObjectsDeleteAll(0, rectangle_prefix + "POC" + Suffix, -1, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "VAH" + Suffix, 0, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "VAL" + Suffix, 0, OBJ_TEXT); + ObjectsDeleteAll(0, rectangle_prefix + "POC" + Suffix, 0, OBJ_TEXT); } if (ShowSinglePrint) { // Delete all Single Print marks. - ObjectsDeleteAll(0, rectangle_prefix + "MPSP" + Suffix, -1, OBJ_RECTANGLE); + ObjectsDeleteAll(0, rectangle_prefix + "MPSP" + Suffix, 0, OBJ_RECTANGLE); } if (SinglePrintRays) { // Delete all Single Print rays. - ObjectsDeleteAll(0, rectangle_prefix + "MPSPR" + Suffix, -1, OBJ_TREND); + ObjectsDeleteAll(0, rectangle_prefix + "MPSPR" + Suffix, 0, OBJ_TREND); + } + if (AlertArrows) + { + DeleteArrowsByPrefix(rectangle_prefix); } } @@ -1246,6 +1233,7 @@ bool ProcessSession(const int sessionstart, const int sessionend, const int i, c ArrayResize(RememberSessionMin, SessionsNumber); ArrayResize(RememberSessionStart, SessionsNumber); ArrayResize(RememberSessionSuffix, SessionsNumber); + ArrayResize(RememberSessionEnd, SessionsNumber); // Used only for Arrows. } } @@ -1257,6 +1245,7 @@ bool ProcessSession(const int sessionstart, const int sessionend, const int i, c RememberSessionMin[session_counter] = SessionMin; RememberSessionStart[session_counter] = Time[sessionstart]; RememberSessionSuffix[session_counter] = Suffix; + RememberSessionEnd[session_counter] = Time[sessionend]; // Used only for Arrows. // Used to make sure that SessionMax increments only by 'onetick' increments. // This is needed only when updating the latest trading session and PointMultiplier_calculated > 1. @@ -2144,28 +2133,186 @@ void CheckRays() ObjectDelete(0, rec_name + "Value Area LowRay" + suffix + last_name); } - if (RaysUntilIntersection == Stop_No_Rays) continue; - - if ((((ShowMedianRays == Previous) || (ShowMedianRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowMedianRays == AllPrevious) || (ShowMedianRays == All)) && (SessionsNumber - i >= 2))) + if (RaysUntilIntersection != Stop_No_Rays) { - if ((RaysUntilIntersection == Stop_All_Rays) - || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) - || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) - CheckRayIntersections(rec_name + "Median Ray" + suffix + last_name, i + 1); + if ((((ShowMedianRays == Previous) || (ShowMedianRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowMedianRays == AllPrevious) || (ShowMedianRays == All)) && (SessionsNumber - i >= 2))) + { + if ((RaysUntilIntersection == Stop_All_Rays) + || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) + || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + CheckRayIntersections(rec_name + "Median Ray" + suffix + last_name, i + 1); + } + if ((((ShowValueAreaRays == Previous) || (ShowValueAreaRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowValueAreaRays == AllPrevious) || (ShowValueAreaRays == All)) && (SessionsNumber - i >= 2))) + { + if ((RaysUntilIntersection == Stop_All_Rays) + || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) + || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + { + CheckRayIntersections(rec_name + "Value Area HighRay" + suffix + last_name, i + 1); + CheckRayIntersections(rec_name + "Value Area LowRay" + suffix + last_name, i + 1); + } + } } - if ((((ShowValueAreaRays == Previous) || (ShowValueAreaRays == PreviousCurrent)) && (SessionsNumber - i == 2)) || (((ShowValueAreaRays == AllPrevious) || (ShowValueAreaRays == All)) && (SessionsNumber - i >= 2))) + + // Historical arrow placement. + // Here we are inside a cycle through all sessions. + // For each session, we will pass its rays and add arrows if they are visible. + // Before that, it's best to check if any arrows have already been created for this session. If they have, skip. + if (AlertArrows) { - if ((RaysUntilIntersection == Stop_All_Rays) - || ((RaysUntilIntersection == Stop_All_Rays_Except_Prev_Session) && (SessionsNumber - i > 2)) - || ((RaysUntilIntersection == Stop_Only_Previous_Session) && (SessionsNumber - i == 2))) + // We will start checking from the next bar after session's end because previous crosses could be at different places in an uncompleted session. + int bar_start = iBarShift(Symbol(), Period(), RememberSessionEnd[i]) - 1; // Same for all rays of this session. + + // Single Print rays. + // Single Print rays. + if (AlertForSinglePrint) { - CheckRayIntersections(rec_name + "Value Area HighRay" + suffix + last_name, i + 1); - CheckRayIntersections(rec_name + "Value Area LowRay" + suffix + last_name, i + 1); + int obj_total = ObjectsTotal(ChartID(), 0, OBJ_TREND); + for (int j = 0; j < obj_total; j++) + { + string obj_name = ObjectName(ChartID(), j, 0, OBJ_TREND); + string obj_prefix = rec_name + "MPSPR" + suffix + last_name; + if (StringSubstr(obj_name, 0, StringLen(obj_prefix)) != obj_prefix) continue; // Not a Single Print ray (or not this seesion's). + if ((color)ObjectGetInteger(ChartID(), obj_name, OBJPROP_COLOR) != clrNONE) // Visible. + { + // Proceed only if no arrow has been found for this ray. + if (!FindAtLeastOneArrowForRay(obj_prefix)) + { + for (int k = bar_start; k >= 0; k--) // Check all bars for the given single print ray. + { + CheckAndDrawArrow(k, ObjectGetDouble(ChartID(), obj_name, OBJPROP_PRICE, 0), obj_prefix); + } + } + } + else // Invisible. + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } } + + // Value Area rays. + if (AlertForValueArea) + { + string obj_prefix = rec_name + "Value Area HighRay" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + + obj_prefix = rec_name + "Value Area LowRay" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } + + // Median rays. + if (AlertForMedian) + { + string obj_prefix = rec_name + "Median Ray" + suffix + last_name; + if (ObjectFind(ChartID(), obj_prefix) >= 0) // Exists and visible. + { + if (!FindAtLeastOneArrowForRay(obj_prefix)) CheckHistoricalArrowsForNonMPSPRRays(bar_start, obj_prefix); + } + else + { + DeleteArrowsByPrefix(obj_prefix); // Delete all arrows generated by this ray. + } + } + } + } +} + +// Delete arrows by prefix (complete or incomplete). +void DeleteArrowsByPrefix(const string prefix) +{ + // Delete all arrows. + ObjectsDeleteAll(ChartID(), "ArrCC" + prefix, 0, OBJ_ARROW); + ObjectsDeleteAll(ChartID(), "ArrGC" + prefix, 0, OBJ_ARROW); + ObjectsDeleteAll(ChartID(), "ArrPB" + prefix, 0, OBJ_ARROW); +} + +// Returns true if at least one arrow is found, false otherwise. +bool FindAtLeastOneArrowForRay(const string ray_name) +{ + int objects_total = ObjectsTotal(ChartID(), 0, OBJ_ARROW); + for (int i = 0; i < objects_total; i++) + { + string obj_name = ObjectName(ChartID(), i, 0, OBJ_ARROW); + if (StringFind(obj_name, ray_name) != -1) return true; // Found an rrrow for a given ray. + } + return false; // No arrows found for the ray. +} + +// Checks and draws historical arrow alerts for Median or Value Area ray. +void CheckHistoricalArrowsForNonMPSPRRays(const int bar_start, const string ray_name) +{ + int end_bar = 0; + if (ObjectGetInteger(ChartID(), ray_name, OBJPROP_RAY_RIGHT) != true) // Ray was stopped, need to find its end. + { + datetime end_time = (datetime)ObjectGetInteger(ChartID(), ray_name, OBJPROP_TIME, 1); + end_bar = iBarShift(Symbol(), Period(), end_time) + 1; // End before the new session starts. + } + for (int k = bar_start; k >= end_bar; k--) // Check all bars for the given ray. + { + CheckAndDrawArrow(k, ObjectGetDouble(ChartID(), ray_name, OBJPROP_PRICE, 0), ray_name); + } +} + +// Checks if any of the arrow alerts triggered on a given candle (n) with a given ray's level and places a chart object using name. +void CheckAndDrawArrow(const int n, const double level, const string ray_name) +{ + // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. + if (AlertOnPriceBreak) // Price break alerts. + { + if (((iHigh(Symbol(), Period(), n) >= level) && (iClose(Symbol(), Period(), n) < level) && (iClose(Symbol(), Period(), n + 1) < level)) || ((iLow(Symbol(), Period(), n) <= level) && (iClose(Symbol(), Period(), n) > level) && (iClose(Symbol(), Period(), n + 1) > level))) + { + // Draw arrow object: + string obj_name = "ArrPB" + ray_name; + CreateArrowObject(obj_name, iTime(Symbol(), Period(), n), iClose(Symbol(), Period(), n), AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); + } + } + if (AlertOnCandleClose) // Candle close alerts. + { + if (((iClose(Symbol(), Period(), n) >= level) && (iClose(Symbol(), Period(), n + 1) < level)) || ((iClose(Symbol(), Period(), n) <= level) && (iClose(Symbol(), Period(), n + 1) > level))) + { + // Draw arrow object: + string obj_name = "ArrCC" + ray_name; + CreateArrowObject(obj_name, iTime(Symbol(), Period(), n), iClose(Symbol(), Period(), n), AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); + } + } + if (AlertOnGapCross) // Gap cross alerts. + { + if (((iLow(Symbol(), Period(), n) > level) && (iHigh(Symbol(), Period(), n + 1) < level)) || ((iLow(Symbol(), Period(), n + 1) > level) && (iHigh(Symbol(), Period(), n) < level))) + { + string obj_name = "ArrGC" + ray_name; + CreateArrowObject(obj_name, iTime(Symbol(), Period(), n), level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); } } } +// Creates an arrow object and sets its properties. +void CreateArrowObject(const string name, const datetime time, const double price, const int code, const color colour, const int width, const string tooltip) +{ + string obj_name = name + IntegerToString(ArrowsCounter); + ArrowsCounter++; + ObjectCreate(ChartID(), obj_name, OBJ_ARROW, 0, time, price); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_ARROWCODE, code); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_COLOR, colour); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_ANCHOR, ANCHOR_CENTER); + ObjectSetInteger(ChartID(), obj_name, OBJPROP_WIDTH, width); + ObjectSetString(ChartID(), obj_name, OBJPROP_TOOLTIP, tooltip); +} + //+------------------------------------------------------------------+ //| Checks price intersection and cuts a ray for a given object. | //+------------------------------------------------------------------+ @@ -2373,15 +2520,12 @@ void OnTimer() { ObjectCleanup(); // Delete everything to make sure there are no leftover sessions behind the screen. if (Session == Intraday) FirstRunDone = false; // Turn off because FirstRunDone should be false for Intraday sessions to draw properly in the past. - if ((EnableDevelopingPOC) || (AlertArrows)) + if (EnableDevelopingPOC) { for (int i = 0; i < Bars(Symbol(), Period()); i++) // Clean indicator buffers. { DevelopingPOC_1[i] = EMPTY_VALUE; DevelopingPOC_2[i] = EMPTY_VALUE; - ArrowsPB[i] = EMPTY_VALUE; - ArrowsCC[i] = EMPTY_VALUE; - ArrowsGC[i] = EMPTY_VALUE; } } } @@ -2525,13 +2669,14 @@ void CheckRectangles(const double& High[], const double& Low[], const datetime& ArrayResize(RememberSessionMin, SessionsNumber); ArrayResize(RememberSessionStart, SessionsNumber); ArrayResize(RememberSessionSuffix, SessionsNumber); + ArrayResize(RememberSessionEnd, SessionsNumber); // Used only for Arrows. } // Process each rectangle. for (int i = 0; i < mpr_total; i++) MPR_Array[i].Process(i, High, Low, Time, rates_total); - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); LastRecalculationTime = GetTickCount(); // Remember last calculation time. @@ -2699,6 +2844,9 @@ void CRectangleMP::Process(const int i, const double& High[], const double& Low[ if (sessionstart < 0) return; // Rectangle is drawn in the future. RememberSessionStart[i] = RectangleTimeMin; + // Used only for Arrows: + if (Time[0] < RectangleTimeMax) RememberSessionEnd[i] = Time[0]; + else RememberSessionEnd[i] = RectangleTimeMax; if ((!new_bars_are_not_within_rectangle) || (current_bar_changed_within_boundaries) || (rectangle_changed_and_recalc_is_due) || ((Number != i) && ((RaysUntilIntersection != Stop_No_Rays) && ((ShowMedianRays != None) || (ShowValueAreaRays != None))))) ProcessSession(sessionstart, sessionend, i, High, Low, Time, rates_total, &this); @@ -2819,6 +2967,7 @@ void RedrawLastSession(const double& High[], const double& Low[], const datetime } sessionstart = FindSessionStart(Time, sessionend, rates_total); } + SessionsNumber = 0; // Reset previously remembered sessions as there won't be any need for them. } // We begin from the oldest session coming to the current session or to StartFromDate. @@ -2859,7 +3008,7 @@ void RedrawLastSession(const double& High[], const double& Low[], const datetime } } - if ((ShowValueAreaRays != None) || (ShowMedianRays != None)) CheckRays(); + if ((ShowValueAreaRays != None) || (ShowMedianRays != None) || ((HideRaysFromInvisibleSessions) && (SinglePrintRays))) CheckRays(); } //+------------------------------------------------------------------+ @@ -2997,7 +3146,7 @@ void OnChartEvent(const int id, const long& lparam, const double& dparam, const void CheckAlerts(const double &Open[], const double &High[], const double &Low[], const double &Close[], const datetime &Time[]) { // No need to check further if no alert method is chosen. - if ((!AlertNative) && (!AlertEmail) && (!AlertPush)) return; + if ((!AlertNative) && (!AlertEmail) && (!AlertPush) && (!AlertArrows)) return; // Skip alerts if alerts are disabled for Median, for Value Area, and for Single Print rays. if ((!AlertForMedian) && (!AlertForValueArea) && (!AlertForSinglePrint)) return; // Skip alerts if no cross type is chosen. @@ -3014,7 +3163,7 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] // Skip if it is either a non-ray or if this particular ray shouldn't get alerted. if (!(((AlertForMedian) && (StringFind(object_name, "Median Ray") > -1)) || ((AlertForValueArea) && ((StringFind(object_name, "Value Area HighRay") > -1) || (StringFind(object_name, "Value Area LowRay") > -1))) || - ((AlertForSinglePrint)&& (StringFind(object_name, "MPSPR") > -1) && (ObjectGetInteger(ChartID(), object_name, OBJPROP_COLOR) != clrNONE)))) continue; + ((AlertForSinglePrint)&& (StringFind(object_name, "MPSPR") > -1) && ((color)ObjectGetInteger(ChartID(), object_name, OBJPROP_COLOR) != clrNONE)))) continue; // If everything is fine, go on: @@ -3028,9 +3177,8 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] if ((Close_prev != EMPTY_VALUE) && (((Close[0] >= level) && (Close_prev < level)) || ((Close[0] <= level) && (Close_prev > level)))) { DoAlerts(PriceBreak, object_name); - ArrowsPB[0] = Close[0]; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Time[0], Close[0], AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); } - else ArrowsPB[0] = EMPTY_VALUE; Close_prev = Close[0]; } if (AlertOnCandleClose) // Candle close alerts. @@ -3038,18 +3186,16 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] if (((Close[0] >= level) && (Close[1] < level)) || ((Close[0] <= level) && (Close[1] > level))) { DoAlerts(CandleCloseCrossover, object_name); - ArrowsCC[0] = Close[0]; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Time[0], Close[0], AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); } - else ArrowsCC[0] = EMPTY_VALUE; } if (AlertOnGapCross) // Gap cross alerts. { if (((Open[0] > level) && (High[1] < level)) || ((Open[0] < level) && (Low[1] > level))) { DoAlerts(GapCrossover, object_name); - ArrowsGC[0] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Time[0], level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); } - else ArrowsGC[0] = EMPTY_VALUE; } } // Price breaks (using pre-previous High and previous Close), candle closes, and gap crosses using Close[1]. @@ -3060,7 +3206,7 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] if (((High[1] >= level) && (Close[1] < level) && (Close[2] < level)) || ((Low[1] <= level) && (Close[1] > level) && (Close[2] > level))) { DoAlerts(PriceBreak, object_name); - ArrowsPB[1] = Close[1]; + if (AlertArrows) CreateArrowObject("ArrPB" + object_name, Time[1], Close[1], AlertArrowCodePB, AlertArrowColorPB, AlertArrowWidthPB, "Price Break"); } } if (AlertOnCandleClose) // Candle close alerts. @@ -3068,7 +3214,7 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] if (((Close[1] >= level) && (Close[2] < level)) || ((Close[1] <= level) && (Close[2] > level))) { DoAlerts(CandleCloseCrossover, object_name); - ArrowsCC[1] = Close[1]; + if (AlertArrows) CreateArrowObject("ArrCC" + object_name, Time[1], Close[1], AlertArrowCodeCC, AlertArrowColorCC, AlertArrowWidthCC, "Candle Close"); } } if (AlertOnGapCross) // Gap cross alerts. @@ -3076,7 +3222,7 @@ void CheckAlerts(const double &Open[], const double &High[], const double &Low[] if (((Low[1] > level) && (High[2] < level)) || ((Low[2] > level) && (High[1] < level))) { DoAlerts(GapCrossover, object_name); - ArrowsGC[1] = level; + if (AlertArrows) CreateArrowObject("ArrGC" + object_name, Time[1], level, AlertArrowCodeGC, AlertArrowColorGC, AlertArrowWidthGC, "Gap Cross"); } } LastAlertTime = Time[0];