Right Extended Rectangle

I have been drawing rectangles and “region highlight y” to show different extended Value Areas as a chop zone reminder until price makes up it’s mind.
But I was wondering:
Does anyone know of free source for a draw region or rectangle tool that draws a right facing rectangle or region highlight that updates itself OnBarClose?
eg: MouseDownPoint(select x,y) , MouseDownPoint(select y) and then draws a region to the currentbar or to the edge of chart continuously OnBarClose ?

I’m kinda lazy and figure it’s already been done, otherwise I will write it this week.

Thanks!
Robotman

Here’s something like that. The code extends native Shapes code. May not be compatible with earlier versions, if NT changed the code. The shadow can have it’s own alpha and color.

// Extension to native Shapes file to draw extension of an rectangle
//
#region Using declarations
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
#endregion

namespace NinjaTrader.NinjaScript.DrawingTools
{
	/// <summary>
	/// Represents a rectangle IDrawingTool whose right side is extended to the current bar.
	/// The extended area is filled with a user-defined color and opacity.
	/// </summary>
	public class ExtendedRect : ShapeBase
	{
		private				int			extensionOpacity;
		private				Brush		extensionColor;
		private	readonly	DeviceBrush	extensionBrushDevice	= new();

		[XmlIgnore]
		[Display(Name = "Extension Color", Description = "Fill color of the area between the rectangle's right side and the current bar", GroupName = "NinjaScriptGeneral", Order = 4)]
		public Brush ExtensionColor
		{
			get => extensionColor;
			set
			{
				extensionColor = value;
				if (extensionColor != null)
				{
					if (extensionColor.IsFrozen)
						extensionColor = extensionColor.Clone();
					extensionColor.Freeze();
				}
				extensionBrushDevice.Brush = null;
			}
		}

		[Browsable(false)]
		public string ExtensionColorSerialize
		{
			get => Serialize.BrushToString(ExtensionColor);
			set => ExtensionColor = Serialize.StringToBrush(value);
		}

		[Range(0, 100)]
		[Display(Name = "Extension Opacity", Description = "Opacity of the extension fill, from 0 (transparent) to 100 (opaque)", GroupName = "NinjaScriptGeneral", Order = 5)]
		public int ExtensionOpacity
		{
			get => extensionOpacity;
			set
			{
				extensionOpacity = Math.Max(0, Math.Min(100, value));
				extensionBrushDevice.Brush = null;
			}
		}

		public override object Icon => Gui.Tools.Icons.DrawRectangle;

		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (extensionBrushDevice != null)
				extensionBrushDevice.RenderTarget = null;
		}

		public override void OnRender(ChartControl chartControl, ChartScale chartScale)
		{
			// render the extension first so the rectangle fill/outline stays on top of it
			if (!IsInHitTest)
				RenderExtension(chartControl, chartScale);

			base.OnRender(chartControl, chartScale);
		}

		private void RenderExtension(ChartControl chartControl, ChartScale chartScale)
		{
			if (ShapeType == ChartShapeType.Unset || StartAnchor == null || EndAnchor == null)
				return;

			ChartBars chartBars = GetPrimaryChartBars(chartControl);
			if (chartBars == null)
				return;

			ChartPanel	chartPanel	= chartControl.ChartPanels[PanelIndex];
			Point		startPoint	= StartAnchor.GetPoint(chartControl, chartPanel, chartScale);
			Point		endPoint	= EndAnchor.GetPoint(chartControl, chartPanel, chartScale);

			double right	= Math.Max(startPoint.X, endPoint.X);
			double top		= Math.Min(startPoint.Y, endPoint.Y);
			double bottom	= Math.Max(startPoint.Y, endPoint.Y);

			// extend from the rectangle's right edge all the way to the current bar
			double currentX = chartControl.GetXByBarIndex(chartBars, chartBars.ToIndex);
			if (currentX <= right || bottom <= top)
				return;

			if (ExtensionColor == null)
			{
				extensionBrushDevice.Brush		= null;
				extensionBrushDevice.RenderTarget = null;
				return;
			}

			if (extensionBrushDevice.Brush == null)
			{
				Brush brushCopy			= ExtensionColor.Clone();
				brushCopy.Opacity		= extensionOpacity / 100d;
				extensionBrushDevice.Brush = brushCopy;
			}
			extensionBrushDevice.RenderTarget = RenderTarget;

			if (extensionBrushDevice.BrushDX == null)
				return;

			// align to full pixel the same way the base class does
			double strokePixAdjust = OutlineStroke != null && OutlineStroke.Width % 2 == 0 ? 0.5d : 0d;
			SharpDX.RectangleF extensionRect = new(
				(float)(right + strokePixAdjust),
				(float)(top + strokePixAdjust),
				(float)(currentX - right),
				(float)(bottom - top));

			RenderTarget.FillRectangle(extensionRect, extensionBrushDevice.BrushDX);
		}

		public override bool IsVisibleOnChart(ChartControl chartControl, ChartScale chartScale, DateTime firstTimeOnChart, DateTime lastTimeOnChart)
		{
			if (base.IsVisibleOnChart(chartControl, chartScale, firstTimeOnChart, lastTimeOnChart))
				return true;

			ChartBars chartBars = GetPrimaryChartBars(chartControl);
			if (chartBars == null)
				return false;

			// the base check only looks at the anchors; the extension reaches the current bar,
			// so the tool can still be visible when the rectangle itself scrolled off to the left
			ChartPanel	chartPanel	= chartControl.ChartPanels[PanelIndex];
			float		minX		= float.MaxValue;
			foreach (Point pt in Anchors.Select(a => a.GetPoint(chartControl, chartPanel, chartScale)))
				minX = Math.Min(minX, (float)pt.X);

			DateTime	leftTime	= chartControl.GetTimeByX((int)minX);
			double		currentX	= chartControl.GetXByBarIndex(chartBars, chartBars.ToIndex);
			DateTime	rightTime	= chartControl.GetTimeByX((int)currentX);

			return leftTime <= lastTimeOnChart && rightTime >= firstTimeOnChart;
		}

		private static ChartBars GetPrimaryChartBars(ChartControl chartControl)
		{
			try
			{
				if (chartControl == null || chartControl.BarsArray == null || chartControl.BarsArray.Count == 0)
					return null;
				return chartControl.BarsArray[0];
			}
			catch
			{
				return null;
			}
		}

		protected override void OnStateChange()
		{
			base.OnStateChange();
			if (State == State.SetDefaults)
			{
				Name				= "ExtendedRect";
				ShapeType			= ChartShapeType.Rectangle;
				ExtensionColor		= Brushes.CornflowerBlue;
				ExtensionOpacity	= 40;
			}
		}
	}
}

1 Like

RectangleExtension indicator - does what you ask for. Have the week off :slight_smile:

#region Using declarations

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.ComponentModel.DataAnnotations;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Input;

using System.Windows.Media;

using System.Xml.Serialization;

using NinjaTrader.Cbi;

using NinjaTrader.Gui;

using NinjaTrader.Gui.Chart;

using NinjaTrader.Gui.SuperDom;

using NinjaTrader.Gui.Tools;

using NinjaTrader.Data;

using NinjaTrader.NinjaScript;

using NinjaTrader.Core.FloatingPoint;

using NinjaTrader.NinjaScript.DrawingTools;

using System.Reflection;

#endregion

//This namespace holds Indicators in this folder and is required. Do not change it.

namespace NinjaTrader.NinjaScript.Indicators.MA5

{

public class RectangleExtension : Indicator

{

// private DateTime curDate = DateTime.Parse(“2/26/2023 12:00:00 AM”);

private NinjaTrader.Gui.Chart.Chart chartWindow;

private new System.Windows.Controls.Button Expansion;

private bool IsToolBarButtonAdded;

private TimeSpan timeEndS;

private DateTime startTime;

private DateTime endTime;

private bool showAreaColor;

private Brush rectColor;

protected override void OnStateChange()

    {

if (State == State.SetDefaults)

        {

Description = @“Rectangle extender. Extend the selected rectangle to the end time of current day.”;

Name = “Rectangle Extension”;

Calculate = Calculate.OnBarClose;

IsOverlay = true;

DisplayInDataBox = true;

DrawOnPricePanel = true;

DrawHorizontalGridLines = true;

DrawVerticalGridLines = true;

PaintPriceMarkers = true;

ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;

IsSuspendedWhileInactive = true;

startTime = DateTime.Now;

endTime = startTime.Add(TimeEnd);

TimeEnd = new TimeSpan(23, 50, 00);

ShowAreaColor = true;

RectColor = Brushes.LightCyan;

        }

        #region Button //Real RealTime

else if (State == State.Realtime)

        {

if (ChartControl != null && !IsToolBarButtonAdded)

            {

ChartControl.Dispatcher.InvokeAsync((Action)(() =>

                {

AddButtonToToolbar();

                }));

            }

        }

else if (State == State.Terminated)

        {

if (chartWindow != null)

            {

ChartControl.Dispatcher.InvokeAsync((Action)(() =>

                {   

DisposeCleanUp();

                }));

            }

        }   

        #endregion

    }

    #region Button

private void AddButtonToToolbar()

    {

chartWindow = Window.GetWindow(this.ChartControl.Parent) as Chart;

if (chartWindow == null)

          {

Print(“chartWindow == null”);

return;

          }

Style btnStyle = new Style();

btnStyle.TargetType = typeof(System.Windows.Controls.Button);

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.FontSizeProperty, 11.0));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.FontFamilyProperty, new FontFamily(“Franklin Gothic Book”)));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.FontWeightProperty, FontWeights.Bold));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.MarginProperty, new Thickness(2, 0, 2, 0)));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.PaddingProperty, new Thickness(4, 2, 4, 2)));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.ForegroundProperty, Brushes.Cyan));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.BackgroundProperty, Brushes.Transparent));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.IsEnabledProperty, true));

btnStyle.Setters.Add(new Setter(System.Windows.Controls.Button.HorizontalAlignmentProperty, HorizontalAlignment.Center));

Expansion = new System.Windows.Controls.Button();

Expansion.Content = “Extension”;

Expansion.Style = btnStyle;

chartWindow.MainMenu.Add(Expansion);

Expansion.Visibility = Visibility.Visible;

Expansion.Click += ExpansionClick;

IsToolBarButtonAdded = true;

    }   

     #endregion

protected override void OnRender(ChartControl chartControl, ChartScale chartScale)

    {           

    }   

private void ExpansionClick(object sender, RoutedEventArgs e)

    {           

System.Windows.Controls.Button button = sender as System.Windows.Controls.Button;

if (button != null)

        {   

foreach(IDrawingTool drawObject in DrawObjects)

            {                   

if(drawObject.GetType().ToString().Contains(“Rectangle”))

                  { 

endTime = endTime.Add(TimeEnd);

dynamic rectangle = drawObject;

if(drawObject.IsSelected)

                      {                                

if (ShowAreaColor){ rectangle.AreaBrush = RectColor; }

                          ((Rectangle) drawObject).EndAnchor = new ChartAnchor(endTime, ((Rectangle) drawObject).EndAnchor.Price, ChartControl);    

                           // another option, which also works. 

// ((Rectangle) drawObject).EndAnchor = new ChartAnchor(curDate, ((Rectangle) drawObject).EndAnchor.Price, ChartControl);

                      }                            

                  }

            }                                   

chartWindow.ActiveChartControl.InvalidateVisual();

ForceRefresh();

        }

    }

private void DisposeCleanUp()

    {

if (Expansion != null) chartWindow.MainMenu.Remove(Expansion);

Expansion.Click -= ExpansionClick;

    }



    #region Properties

// [NinjaScriptProperty]

// [PropertyEditor(“NinjaTrader.Gui.Tools.TimeEditorKey”)]

// [Display(ResourceType = typeof(Custom.Resource), Name = “End Time”, GroupName = “Parameters”, Order = 04)]

// public DateTime CurDate

// { get; set; }

   \[XmlIgnore\]   

   \[Browsable(false)\] 

public TimeSpan TimeEnd

   { get; set; }

   \[NinjaScriptProperty\]

   \[Display(Name = "Time End", GroupName = "Parameters", Order = 01)\]

public string TimeEndSpanSerialize

    {

get { return TimeEnd.ToString(); }

set { TimeEnd = TimeSpan.Parse(value); }

    }

    \[Display(Name="Show Area Color", Order = 02, GroupName="Parameters")\]

public bool ShowAreaColor

    { get; set; }

    \[XmlIgnore\]

    \[Display(Name="Rectangle Color",  Order = 03, GroupName="Parameters")\]

public Brush RectColor

    { get; set; }



    \[Browsable(false)\]

public string RectColorSerializable

    {

get { return Serialize.BrushToString(RectColor); }

set { RectColor = Serialize.StringToBrush(value); }

    }       

    #endregion

    #region  Hide names

public override string DisplayName

    {

get { if (State == State.SetDefaults){ return GetIndDisplay();}

else{ return “”; }}}

protected string GetIndDisplay() {

try { string[] split = Instrument.FullName.Split(new char[] {’ '});

string CrSy = split[0]; return CrSy + “Rectangle Extension”; } catch (Exception ex) { return “”;}

    }

    #endregion

}

}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators

{

public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase

{

private MA5.RectangleExtension[] cacheRectangleExtension;

public MA5.RectangleExtension RectangleExtension(string timeEndSpanSerialize)

    {

return RectangleExtension(Input, timeEndSpanSerialize);

    }

public MA5.RectangleExtension RectangleExtension(ISeries input, string timeEndSpanSerialize)

    {

if (cacheRectangleExtension != null)

for (int idx = 0; idx < cacheRectangleExtension.Length; idx++)

if (cacheRectangleExtension[idx] != null && cacheRectangleExtension[idx].TimeEndSpanSerialize == timeEndSpanSerialize && cacheRectangleExtension[idx].EqualsInput(input))

return cacheRectangleExtension[idx];

return CacheIndicator<MA5.RectangleExtension>(new MA5.RectangleExtension(){ TimeEndSpanSerialize = timeEndSpanSerialize }, input, ref cacheRectangleExtension);

    }

}

}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns

{

public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase

{

public Indicators.MA5.RectangleExtension RectangleExtension(string timeEndSpanSerialize)

    {

return indicator.RectangleExtension(Input, timeEndSpanSerialize);

    }

public Indicators.MA5.RectangleExtension RectangleExtension(ISeries input , string timeEndSpanSerialize)

    {

return indicator.RectangleExtension(input, timeEndSpanSerialize);

    }

}

}

namespace NinjaTrader.NinjaScript.Strategies

{

public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase

{

public Indicators.MA5.RectangleExtension RectangleExtension(string timeEndSpanSerialize)

    {

return indicator.RectangleExtension(Input, timeEndSpanSerialize);

    }

public Indicators.MA5.RectangleExtension RectangleExtension(ISeries input , string timeEndSpanSerialize)

    {

return indicator.RectangleExtension(input, timeEndSpanSerialize);

    }

}

}

#endregion

1 Like

@BillMilton @brucerobinson

Wow, thank you both! I shall play with these today! Much, much appreciated.

Robotman