Automatic Rectangle Plot Indicator

I am not much of a programmer. I was hoping someone could help with an indicator that could plot a rectangle on the current trading day, has a user specified start and end time and a user specified height. The anchor point of the rectangle would be the midline and anchored to the open of the start time bar. Thanks!

Right click on a chart
Select DRAWING TOOLS
Select RECTANGLE
You can now adapt the rectangle at your wishes.

You can also use the shortcut: Ctrl + F12

I already do it manually. I use the ruler to get the height of the rectangle just right and then place the rectangle on the midline at the time I am needing it. I was hoping for a way to cut out the measuring process and just have an indicator do that process for me. Its more of a convenience thing really.

1 Like

I went down the AI rabbit hole but I can’t get it to work correctly. Any help would be much appreciated.

#region Using declarations
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui.Tools;
using NinjaTrader.NinjaScript.DrawingTools;
using NinjaTrader.Data;
using NinjaTrader.Core;
using NinjaTrader.NinjaScript;
#endregion

namespace NinjaTrader.NinjaScript.Indicators
{
public class TimeRangeRectangle : Indicator
{
// User-configurable start and end times
[NinjaScriptProperty]
[Display(Name=“Start Time”, Order=1, GroupName=“Rectangle Settings”)]
public TimeSpan StartTime { get; set; } = new TimeSpan(10, 0, 0); // Default 10:00

    [NinjaScriptProperty]
    [Display(Name="End Time", Order=2, GroupName="Rectangle Settings")]
    public TimeSpan EndTime { get; set; } = new TimeSpan(10, 30, 0); // Default 10:30

    [NinjaScriptProperty]
    [Range(0.1, double.MaxValue)]
    [Display(Name="Rectangle Height", Order=3, GroupName="Rectangle Settings")]
    public double RectangleHeight { get; set; } = 10; // Default height

    [NinjaScriptProperty]
    [Range(-1000.0, 1000.0)]
    [Display(Name="Midline Offset", Order=4, GroupName="Rectangle Settings")]
    public double MidlineOffset { get; set; } = 0; // Optional offset from midline

    [NinjaScriptProperty]
    [XmlIgnore]
    [Display(Name="Rectangle Color", Order=5, GroupName="Appearance")]
    public Brush RectangleBrush { get; set; } = Brushes.LightBlue;

    [Browsable(false)]
    public string RectangleBrushSerializable
    {
        get { return BrushSerialization.BrushToString(RectangleBrush); }
        set { RectangleBrush = BrushSerialization.StringToBrush(value); }
    }

    [NinjaScriptProperty]
    [XmlIgnore]
    [Display(Name="Rectangle Outline", Order=6, GroupName="Appearance")]
    public Brush RectangleOutline { get; set; } = Brushes.Blue;

    [Browsable(false)]
    public string RectangleOutlineSerializable
    {
        get { return BrushSerialization.BrushToString(RectangleOutline); }
        set { RectangleOutline = BrushSerialization.StringToBrush(value); }
    }

    [NinjaScriptProperty]
    [Range(1, 10)]
    [Display(Name="Outline Width", Order=7, GroupName="Appearance")]
    public int OutlineWidth { get; set; } = 2;

    private DateTime lastDrawDate = DateTime.MinValue;

    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Description = "Draws a rectangle between specified times daily, originating from the open of start time.";
            Name = "TimeRangeRectangle";
            Calculate = Calculate.OnBarClose;
            IsOverlay = true;
            DisplayInDataBox = true;
            DrawOnPricePanel = true;
            ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
            IsSuspendedWhileInactive = true;
        }
        else if (State == State.DataLoaded)
        {
            // Clear any existing drawings
            RemoveDrawObject("Rectangle_" + DateTime.Now.ToString("yyyyMMdd"));
            lastDrawDate = DateTime.MinValue;
        }
    }

   protected override void OnBarUpdate()
	{
	    if (CurrentBar < 20) return; // Ensure enough bars
	
	    DateTime currentBarDate = Time[0].Date;
	
	    // Check if we've already drawn today
	    if (lastDrawDate != currentBarDate)
	    {
	        DateTime targetStartTime = currentBarDate + StartTime;
	
	        int startIdx = -1;
	
	        // Find the bar closest to or after the start time
	        for (int i = 0; i < Bars.Count; i++)
	        {
	            if (Time[i] >= targetStartTime)
	            {
	                startIdx = i;
	                break;
	            }
	        }
	
	        if (startIdx != -1)
	        {
	            double openPrice = Opens[startIdx][0];
	
	            double midLine = openPrice + MidlineOffset;
	            double top = midLine + (RectangleHeight / 2);
	            double bottom = midLine - (RectangleHeight / 2);
	
	            string rectTag = "TimeRect_" + currentBarDate.ToString("yyyyMMdd");
	
	            // Remove previous rectangle for the day
	            RemoveDrawObject(rectTag);
	
	            // Draw rectangle from start time to current bar (or latest bar)
	            // Using current bar index as end to extend until now
	            int endIdx = CurrentBar;
	
	            Draw.Rectangle(this, rectTag, false, startIdx, top, endIdx, bottom, RectangleBrush, RectangleOutline, OutlineWidth);
	
	            Print($"Rectangle started at {StartTime} for {currentBarDate:yyyy-MM-dd} from bar {startIdx} to current ({endIdx})");
	            lastDrawDate = currentBarDate;
	        }
	    }
	}

    private double? GetPriceAtTime(DateTime targetTime)
    {
        // Search backwards from current bar to find the closest bar at or after targetTime
        int lookbackLimit = Math.Min(CurrentBar, 1000);
        for (int i = 0; i <= lookbackLimit; i++)
        {
            DateTime barTime = Time[i];
            if (barTime.Date == targetTime.Date)
            {
                if (barTime >= targetTime)
                    return Opens[i][0];

                // If this is the last bar checked on that day
                if (i == lookbackLimit || Time[i + 1].Date != targetTime.Date)
                    return Opens[i][0];
            }
            if (barTime.Date < targetTime.Date)
                break;
        }
        return null; // no suitable bar found
    }
}

// Utility class for Brush serialization
public static class BrushSerialization
{
    public static string BrushToString(Brush brush)
    {
        if (brush == null)
            return string.Empty;

        if (brush is SolidColorBrush scb)
        {
            return scb.Color.ToString(); // e.g., "#FF0000FF"
        }
        // Extend for other Brush types if needed
        return brush.ToString();
    }

    public static Brush StringToBrush(string s)
    {
        try
        {
            if (string.IsNullOrEmpty(s))
                return Brushes.Transparent;

            var color = (Color)ColorConverter.ConvertFromString(s);
            return new SolidColorBrush(color);
        }
        catch
        {
            return Brushes.Transparent;
        }
    }
}

}

This indicator already exists in the Ninjatrader user repository. Go there, search through the items and find it.