287 lines
13 KiB
C#
287 lines
13 KiB
C#
|
#region Using declarations
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.ComponentModel;
|
||
|
using System.ComponentModel.DataAnnotations;
|
||
|
using System.Linq;
|
||
|
using System.Windows.Media;
|
||
|
using NinjaTrader.Cbi;
|
||
|
using NinjaTrader.Gui;
|
||
|
using NinjaTrader.Gui.Chart;
|
||
|
using NinjaTrader.NinjaScript;
|
||
|
using NinjaTrader.Data;
|
||
|
using NinjaTrader.Gui.Tools;
|
||
|
using SharpDX;
|
||
|
using SharpDX.DirectWrite;
|
||
|
using System.Xml.Serialization;
|
||
|
#endregion
|
||
|
|
||
|
namespace NinjaTrader.NinjaScript.Indicators
|
||
|
{
|
||
|
public class RSIDashboard : Indicator
|
||
|
{
|
||
|
private const int MTFBarsStartIndex = 1;
|
||
|
|
||
|
private Dictionary<string, RSI> RSIIndicators = new Dictionary<string, RSI>();
|
||
|
|
||
|
private string[] TimeFrames = new[] { "1 Minute", "5 Minutes", "30 Minutes", "1 Hour", "4 Hours", "Daily", "Weekly" };
|
||
|
private double[] RSIValues = new double[7];
|
||
|
|
||
|
protected override void OnStateChange()
|
||
|
{
|
||
|
if (State == State.SetDefaults)
|
||
|
{
|
||
|
Description = @"Displays the RSI values of multiple time frames.";
|
||
|
Name = "RSI Dashboard";
|
||
|
Calculate = Calculate.OnBarClose;
|
||
|
IsOverlay = true;
|
||
|
RSIPeriod = 14;
|
||
|
RSISmoothing = 3;
|
||
|
UpperThreshold = 65;
|
||
|
LowerThreshold = 35;
|
||
|
AboveThresholdColor = Brushes.LimeGreen;
|
||
|
BelowThresholdColor = Brushes.Red;
|
||
|
BetweenThresholdsColor = Brushes.Transparent;
|
||
|
HeaderColor = Brushes.Yellow;
|
||
|
}
|
||
|
else if (State == State.Configure)
|
||
|
{
|
||
|
for (int i = 0; i < TimeFrames.Length; i++)
|
||
|
{
|
||
|
if (TimeFrames[i].Contains("Daily"))
|
||
|
AddDataSeries(Instrument.FullName, new BarsPeriod { BarsPeriodType = BarsPeriodType.Day, Value = 1 },
|
||
|
365, Instrument.MasterInstrument.TradingHours.Name, Bars.IsResetOnNewTradingDay);
|
||
|
else if (TimeFrames[i].Contains("Weekly"))
|
||
|
AddDataSeries(Instrument.FullName, new BarsPeriod { BarsPeriodType = BarsPeriodType.Week, Value = 1 },
|
||
|
52, Instrument.MasterInstrument.TradingHours.Name, Bars.IsResetOnNewTradingDay);
|
||
|
else
|
||
|
AddDataSeries(Instrument.FullName, BarsPeriodType.Minute, ConvertTimeFrameToMinutes(TimeFrames[i]));
|
||
|
}
|
||
|
}
|
||
|
else if (State == State.DataLoaded)
|
||
|
{
|
||
|
int i = MTFBarsStartIndex;
|
||
|
foreach (string timeFrame in TimeFrames)
|
||
|
{
|
||
|
// BarsArray[0] is the chart bar series, BarsArray[1] is the 1 minute bar series, etc.
|
||
|
RSI rsi = RSI(BarsArray[i], RSIPeriod, RSISmoothing);
|
||
|
RSIIndicators[timeFrame] = rsi;
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
else if (State == State.Historical)
|
||
|
{
|
||
|
SetZOrder(-1); // Display behind bars on chart.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private int ConvertTimeFrameToMinutes(string timeFrame)
|
||
|
{
|
||
|
var numberPart = timeFrame.Split(' ')[0];
|
||
|
int timeFrameValue;
|
||
|
if (int.TryParse(numberPart, out timeFrameValue))
|
||
|
{
|
||
|
if (timeFrame.Contains("Minute"))
|
||
|
return timeFrameValue;
|
||
|
else if (timeFrame.Contains("Hour"))
|
||
|
return timeFrameValue * 60;
|
||
|
}
|
||
|
|
||
|
throw new ArgumentException("Invalid time frame format: " + timeFrame);
|
||
|
}
|
||
|
|
||
|
protected override void OnBarUpdate()
|
||
|
{
|
||
|
RSI rsiValue;
|
||
|
for (int i = MTFBarsStartIndex; i < TimeFrames.Length + 1; i++)
|
||
|
{
|
||
|
if (CurrentBars[i] < 1)
|
||
|
return;
|
||
|
|
||
|
if (RSIIndicators.TryGetValue(TimeFrames[i - 1], out rsiValue))
|
||
|
RSIValues[i - 1] = rsiValue.Value[0];
|
||
|
else
|
||
|
RSIValues[i - 1] = 0; // Default value.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
|
||
|
{
|
||
|
base.OnRender(chartControl, chartScale);
|
||
|
|
||
|
// Calculate maximum column width based on time frame text.
|
||
|
int maxColWidth = 0;
|
||
|
foreach (var timeFrame in TimeFrames)
|
||
|
{
|
||
|
var size = MeasureString(timeFrame, new TextFormat(Core.Globals.DirectWriteFactory, "Arial", 12));
|
||
|
maxColWidth = Math.Max(maxColWidth, size.Width);
|
||
|
}
|
||
|
|
||
|
maxColWidth += 40; // Adding padding of 40 pixels (20 on each side).
|
||
|
|
||
|
int x = ChartPanel.W - (maxColWidth * TimeFrames.Length) - 20; // Adjust starting position due to padding.
|
||
|
int y = 10;
|
||
|
int rowHeight = 20;
|
||
|
int headerHeight = 20;
|
||
|
|
||
|
// Draw header row.
|
||
|
foreach (var timeFrame in TimeFrames)
|
||
|
{
|
||
|
RectangleF headerRect = new RectangleF(x, y, maxColWidth, headerHeight);
|
||
|
RenderTarget.FillRectangle(headerRect, HeaderColor.ToDxBrush(RenderTarget));
|
||
|
|
||
|
var textFormat = new TextFormat(Core.Globals.DirectWriteFactory, "Arial", FontWeight.Normal, FontStyle.Normal, 12);
|
||
|
|
||
|
var size = MeasureString(timeFrame, textFormat);
|
||
|
int textX = x + (maxColWidth - size.Width) / 2; // Center the text horizontally.
|
||
|
int textY = y + (headerHeight - size.Height) / 2; // Center the text vertically.
|
||
|
|
||
|
DrawText(timeFrame, textX, textY, Brushes.Black, textFormat, headerHeight);
|
||
|
x += maxColWidth;
|
||
|
}
|
||
|
|
||
|
// Reset x coordinate for RSI values.
|
||
|
x = ChartPanel.W - (maxColWidth * TimeFrames.Length) - 20;
|
||
|
y += headerHeight;
|
||
|
|
||
|
// Draw RSI value row.
|
||
|
for (int i = 0; i < RSIValues.Length; i++)
|
||
|
{
|
||
|
var rsiText = RSIValues[i].ToString("F2"); // Format RSI value to two decimal places.
|
||
|
var size = MeasureString(rsiText, new TextFormat(Core.Globals.DirectWriteFactory, "Arial", 12));
|
||
|
int textX = x + (maxColWidth - size.Width) / 2; // Center the text horizontally.
|
||
|
int textY = y + (rowHeight - size.Height) / 2; // Center the text vertically.
|
||
|
|
||
|
Brush bgColor = BetweenThresholdsColor;
|
||
|
if (RSIValues[i] > UpperThreshold) bgColor = AboveThresholdColor;
|
||
|
else if (RSIValues[i] < LowerThreshold) bgColor = BelowThresholdColor;
|
||
|
|
||
|
RectangleF rect = new RectangleF(x, y, maxColWidth, rowHeight);
|
||
|
RenderTarget.FillRectangle(rect, bgColor.ToDxBrush(RenderTarget));
|
||
|
DrawText(rsiText, textX, textY, Brushes.White, new TextFormat(Core.Globals.DirectWriteFactory, "Arial", 12), rowHeight);
|
||
|
x += maxColWidth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Size2 MeasureString(string text, TextFormat textFormat)
|
||
|
{
|
||
|
using (var textLayout = new TextLayout(Core.Globals.DirectWriteFactory, text, textFormat, float.PositiveInfinity, float.PositiveInfinity))
|
||
|
{
|
||
|
return new Size2((int)Math.Ceiling(textLayout.Metrics.Width), (int)Math.Ceiling(textLayout.Metrics.Height));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawText(string text, int x, int y, Brush brush, TextFormat textFormat, int height)
|
||
|
{
|
||
|
TextLayout textLayout = new TextLayout(Core.Globals.DirectWriteFactory, text, textFormat, 500, height);
|
||
|
Vector2 textOrigin = new Vector2(x, y);
|
||
|
RenderTarget.DrawTextLayout(textOrigin, textLayout, brush.ToDxBrush(RenderTarget));
|
||
|
textLayout.Dispose();
|
||
|
}
|
||
|
|
||
|
public override string DisplayName
|
||
|
{
|
||
|
get { return Name; }
|
||
|
}
|
||
|
|
||
|
#region Properties
|
||
|
[NinjaScriptProperty]
|
||
|
[Range(1, int.MaxValue)]
|
||
|
[Display(Name = "RSI Period", GroupName = "RSI Dashboard", Order = 1)]
|
||
|
public int RSIPeriod { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[Range(1, int.MaxValue)]
|
||
|
[Display(Name = "RSI Smoothing", GroupName = "RSI Dashboard", Order = 2)]
|
||
|
public int RSISmoothing { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[Range(0, 100)]
|
||
|
[Display(Name = "Upper Threshold", GroupName = "RSI Dashboard", Order = 3)]
|
||
|
public double UpperThreshold { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[Range(0, 100)]
|
||
|
[Display(Name = "Lower Threshold", GroupName = "RSI Dashboard", Order = 4)]
|
||
|
public double LowerThreshold { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[XmlIgnore]
|
||
|
[Display(Name = "Above Threshold Color", Order = 5, GroupName = "RSI Dashboard")]
|
||
|
public Brush AboveThresholdColor { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[XmlIgnore]
|
||
|
[Display(Name = "Below Threshold Color", Order = 6, GroupName = "RSI Dashboard")]
|
||
|
public Brush BelowThresholdColor { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[XmlIgnore]
|
||
|
[Display(Name = "Between Thresholds Color", Order = 7, GroupName = "RSI Dashboard")]
|
||
|
public Brush BetweenThresholdsColor { get; set; }
|
||
|
|
||
|
[NinjaScriptProperty]
|
||
|
[XmlIgnore]
|
||
|
[Display(Name = "Header Color", Order = 8, GroupName = "RSI Dashboard")]
|
||
|
public Brush HeaderColor { get; set; }
|
||
|
#endregion
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#region NinjaScript generated code. Neither change nor remove.
|
||
|
|
||
|
namespace NinjaTrader.NinjaScript.Indicators
|
||
|
{
|
||
|
public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
|
||
|
{
|
||
|
private RSIDashboard[] cacheRSIDashboard;
|
||
|
public RSIDashboard RSIDashboard(int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
return RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor);
|
||
|
}
|
||
|
|
||
|
public RSIDashboard RSIDashboard(ISeries<double> input, int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
if (cacheRSIDashboard != null)
|
||
|
for (int idx = 0; idx < cacheRSIDashboard.Length; idx++)
|
||
|
if (cacheRSIDashboard[idx] != null && cacheRSIDashboard[idx].RSIPeriod == rSIPeriod && cacheRSIDashboard[idx].RSISmoothing == rSISmoothing && cacheRSIDashboard[idx].UpperThreshold == upperThreshold && cacheRSIDashboard[idx].LowerThreshold == lowerThreshold && cacheRSIDashboard[idx].AboveThresholdColor == aboveThresholdColor && cacheRSIDashboard[idx].BelowThresholdColor == belowThresholdColor && cacheRSIDashboard[idx].BetweenThresholdsColor == betweenThresholdsColor && cacheRSIDashboard[idx].HeaderColor == headerColor && cacheRSIDashboard[idx].EqualsInput(input))
|
||
|
return cacheRSIDashboard[idx];
|
||
|
return CacheIndicator<RSIDashboard>(new RSIDashboard(){ RSIPeriod = rSIPeriod, RSISmoothing = rSISmoothing, UpperThreshold = upperThreshold, LowerThreshold = lowerThreshold, AboveThresholdColor = aboveThresholdColor, BelowThresholdColor = belowThresholdColor, BetweenThresholdsColor = betweenThresholdsColor, HeaderColor = headerColor }, input, ref cacheRSIDashboard);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
|
||
|
{
|
||
|
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
|
||
|
{
|
||
|
public Indicators.RSIDashboard RSIDashboard(int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
return indicator.RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor);
|
||
|
}
|
||
|
|
||
|
public Indicators.RSIDashboard RSIDashboard(ISeries<double> input , int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
return indicator.RSIDashboard(input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace NinjaTrader.NinjaScript.Strategies
|
||
|
{
|
||
|
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
|
||
|
{
|
||
|
public Indicators.RSIDashboard RSIDashboard(int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
return indicator.RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor);
|
||
|
}
|
||
|
|
||
|
public Indicators.RSIDashboard RSIDashboard(ISeries<double> input , int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor)
|
||
|
{
|
||
|
return indicator.RSIDashboard(input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|