ninjatrader/indicators/RSIDashboard.cs

350 lines
18 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;
private double[] RSIValues;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"Displays the RSI values of multiple time frames.";
Name = "RSI Dashboard";
Calculate = Calculate.OnPriceChange;
IsOverlay = true;
TimeFrames = new[] { "1 Minute", "5 Minutes", "30 Minutes", "1 Hour", "4 Hours", "Daily", "Weekly" };
RSIValues = new double[TimeFrames.Length];
RSIPeriod = 14;
RSISmoothing = 3;
UpperThreshold = 65;
LowerThreshold = 35;
AboveThresholdColor = Brushes.LimeGreen;
BelowThresholdColor = Brushes.Red;
BetweenThresholdsColor = Brushes.Transparent;
HeaderColor = Brushes.Yellow;
HeaderTextColor = Brushes.Black;
RSIValuesTextColor = Brushes.White;
HeaderFont = new SimpleFont("Arial", 12);
RSIValuesFont = new SimpleFont("Arial", 12);
HorizontalPadding = 20;
VerticalPadding = 5;
YOffset = 0;
}
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);
int maxColWidth = 0;
int maxHeaderHeight = 0;
int maxRowHeight = 0;
using (TextFormat headerTextFormat = HeaderFont.ToDirectWriteTextFormat(),
rsiTextFormat = RSIValuesFont.ToDirectWriteTextFormat())
{
// Calculate maximum header text size.
foreach (var timeFrame in TimeFrames)
{
var headerSize = MeasureString(timeFrame, headerTextFormat);
maxColWidth = Math.Max(maxColWidth, headerSize.Width);
maxHeaderHeight = Math.Max(maxHeaderHeight, headerSize.Height);
}
// Calculate maximum RSI value text size.
foreach (var rsiValue in RSIValues)
{
var rsiText = rsiValue.ToString("F2");
var rsiSize = MeasureString(rsiText, rsiTextFormat);
maxColWidth = Math.Max(maxColWidth, rsiSize.Width);
maxRowHeight = Math.Max(maxRowHeight, rsiSize.Height);
}
}
maxColWidth += HorizontalPadding * 2;
int headerHeight = maxHeaderHeight + (VerticalPadding * 2);
int rowHeight = maxRowHeight + (VerticalPadding * 2);
int x = ChartPanel.W - (maxColWidth * TimeFrames.Length) - HorizontalPadding;
int y = YOffset;
// Draw header row.
using (TextFormat headerTextFormat = HeaderFont.ToDirectWriteTextFormat())
{
foreach (var timeFrame in TimeFrames)
{
RectangleF headerRect = new RectangleF(x, y, maxColWidth, headerHeight);
RenderTarget.FillRectangle(headerRect, HeaderColor.ToDxBrush(RenderTarget));
var size = MeasureString(timeFrame, headerTextFormat);
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, HeaderTextColor, headerTextFormat, headerHeight);
x += maxColWidth;
}
}
// Reset x coordinate for RSI values.
x = ChartPanel.W - (maxColWidth * TimeFrames.Length) - HorizontalPadding;
y += headerHeight;
// Draw RSI value row.
using (TextFormat rsiTextFormat = RSIValuesFont.ToDirectWriteTextFormat())
{
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, rsiTextFormat);
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, RSIValuesTextColor, rsiTextFormat, 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", GroupName = "RSI Dashboard", Order = 5)]
public Brush AboveThresholdColor { get; set; }
[NinjaScriptProperty]
[XmlIgnore]
[Display(Name = "Below Threshold", GroupName = "RSI Dashboard", Order = 6)]
public Brush BelowThresholdColor { get; set; }
[NinjaScriptProperty]
[XmlIgnore]
[Display(Name = "Between Thresholds", GroupName = "RSI Dashboard", Order = 7)]
public Brush BetweenThresholdsColor { get; set; }
[NinjaScriptProperty]
[XmlIgnore]
[Display(Name = "Header Row", GroupName = "RSI Dashboard", Order = 8)]
public Brush HeaderColor { get; set; }
[NinjaScriptProperty]
[XmlIgnore]
[Display(Name = "Header Text", GroupName = "RSI Dashboard", Order = 9)]
public Brush HeaderTextColor { get; set; }
[NinjaScriptProperty]
[XmlIgnore]
[Display(Name = "RSI Values", GroupName = "RSI Dashboard", Order = 10)]
public Brush RSIValuesTextColor { get; set; }
[NinjaScriptProperty]
[Display(Name = "Header Font", GroupName = "RSI Dashboard", Order = 11)]
public SimpleFont HeaderFont { get; set; }
[NinjaScriptProperty]
[Display(Name = "RSI Values Font", GroupName = "RSI Dashboard", Order = 12)]
public SimpleFont RSIValuesFont { get; set; }
[NinjaScriptProperty]
[Range(0, int.MaxValue)]
[Display(Name = "Horizontal Padding", GroupName = "RSI Dashboard", Order = 13)]
public int HorizontalPadding { get; set; }
[NinjaScriptProperty]
[Range(0, int.MaxValue)]
[Display(Name = "Vertical Padding", GroupName = "RSI Dashboard", Order = 14)]
public int VerticalPadding { get; set; }
[NinjaScriptProperty]
[Range(0, int.MaxValue)]
[Display(Name = "Y-Offset", GroupName = "RSI Dashboard", Order = 17)]
public int YOffset { 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, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
return RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor, headerTextColor, rSIValuesTextColor, headerFont, rSIValuesFont, horizontalPadding, verticalPadding, yOffset);
}
public RSIDashboard RSIDashboard(ISeries<double> input, int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
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].HeaderTextColor == headerTextColor && cacheRSIDashboard[idx].RSIValuesTextColor == rSIValuesTextColor && cacheRSIDashboard[idx].HeaderFont == headerFont && cacheRSIDashboard[idx].RSIValuesFont == rSIValuesFont && cacheRSIDashboard[idx].HorizontalPadding == horizontalPadding && cacheRSIDashboard[idx].VerticalPadding == verticalPadding && cacheRSIDashboard[idx].YOffset == yOffset && 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, HeaderTextColor = headerTextColor, RSIValuesTextColor = rSIValuesTextColor, HeaderFont = headerFont, RSIValuesFont = rSIValuesFont, HorizontalPadding = horizontalPadding, VerticalPadding = verticalPadding, YOffset = yOffset }, 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, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
return indicator.RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor, headerTextColor, rSIValuesTextColor, headerFont, rSIValuesFont, horizontalPadding, verticalPadding, yOffset);
}
public Indicators.RSIDashboard RSIDashboard(ISeries<double> input , int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
return indicator.RSIDashboard(input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor, headerTextColor, rSIValuesTextColor, headerFont, rSIValuesFont, horizontalPadding, verticalPadding, yOffset);
}
}
}
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, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
return indicator.RSIDashboard(Input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor, headerTextColor, rSIValuesTextColor, headerFont, rSIValuesFont, horizontalPadding, verticalPadding, yOffset);
}
public Indicators.RSIDashboard RSIDashboard(ISeries<double> input , int rSIPeriod, int rSISmoothing, double upperThreshold, double lowerThreshold, Brush aboveThresholdColor, Brush belowThresholdColor, Brush betweenThresholdsColor, Brush headerColor, Brush headerTextColor, Brush rSIValuesTextColor, SimpleFont headerFont, SimpleFont rSIValuesFont, int horizontalPadding, int verticalPadding, int yOffset)
{
return indicator.RSIDashboard(input, rSIPeriod, rSISmoothing, upperThreshold, lowerThreshold, aboveThresholdColor, belowThresholdColor, betweenThresholdsColor, headerColor, headerTextColor, rSIValuesTextColor, headerFont, rSIValuesFont, horizontalPadding, verticalPadding, yOffset);
}
}
}
#endregion