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.
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.