#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 SharpDX.DirectWrite; #endregion //This namespace holds Indicators in this folder and is required. Do not change it. namespace NinjaTrader.NinjaScript.Indicators { [CategoryOrder("Order Display", 1)] [CategoryOrder("Order Colors", 2)] [CategoryOrder("Text Formatting", 3)] public class Tape : Indicator { private const int DefaultOrderFilter = 1; // By default, all orders are shown. private const int DefaultMaxOrders = 50; private const int DefaultDisplayOffset = 225; private static readonly Brush DefaultBuyOrderColor = Brushes.LimeGreen; private static readonly Brush DefaultSellOrderColor = Brushes.Red; private const string DefaultTimestampFormat = "HH:mm:ss.fff"; private static readonly SimpleFont DefaultFontFamily = new SimpleFont("Arial", 12); private class OrderData { public DateTime Time { get; set; } public double Price { get; set; } public long Volume { get; set; } public double Ask { get; set; } public double Bid { get; set; } } private List recentOrders = new List(); protected override void OnStateChange() { if (State == State.SetDefaults) { Description = @"Displays the tape on the chart rather than in a separate window"; Name = "Tape"; Calculate = Calculate.OnEachTick; IsOverlay = true; DisplayInDataBox = true; DrawOnPricePanel = true; DrawHorizontalGridLines = true; DrawVerticalGridLines = true; PaintPriceMarkers = true; ScaleJustification = ScaleJustification.Right; IsSuspendedWhileInactive = false; OrderFilter = DefaultOrderFilter; CombineOrders = false; MaxDisplayOrders = DefaultMaxOrders; DisplayOrdersOffset = DefaultDisplayOffset; BuyOrderColor = DefaultBuyOrderColor; SellOrderColor = DefaultSellOrderColor; TimestampFormat = DefaultTimestampFormat; TextFontFamily = DefaultFontFamily; } else if (State == State.Configure) { } else if (State == State.Historical) { SetZOrder(-1); // Display behind bars on chart. } } protected override void OnMarketData(MarketDataEventArgs marketDataUpdate) { if (marketDataUpdate.MarketDataType == MarketDataType.Last && marketDataUpdate.Volume >= OrderFilter) { if (CombineOrders && recentOrders.Any(o => o.Time == marketDataUpdate.Time && o.Price == marketDataUpdate.Price)) { var existingOrder = recentOrders.First(o => o.Time == marketDataUpdate.Time && o.Price == marketDataUpdate.Price); existingOrder.Volume += marketDataUpdate.Volume; } else { if (recentOrders.Count >= MaxDisplayOrders) recentOrders.RemoveAt(0); // Remove the oldest order. recentOrders.Add(new OrderData { Time = marketDataUpdate.Time, Price = marketDataUpdate.Price, Volume = marketDataUpdate.Volume, Ask = marketDataUpdate.Ask, Bid = marketDataUpdate.Bid }); } } } protected override void OnRender(ChartControl chartControl, ChartScale chartScale) { base.OnRender(chartControl, chartScale); if (recentOrders.Count == 0) return; // No values to render int startY = ChartPanel.Y; int spaceBetween = 15; int currentY = startY; for (int i = recentOrders.Count - 1; i >= 0; i--) { var order = recentOrders[i]; string text = string.Format("{0:" + TimestampFormat + "} Price: {1:F2} Volume: {2}", order.Time, order.Price, order.Volume); SharpDX.Direct2D1.Brush brush; if (order.Price >= order.Ask) brush = BuyOrderColor.ToDxBrush(RenderTarget); else if (order.Price <= order.Bid) brush = SellOrderColor.ToDxBrush(RenderTarget); else brush = Brushes.White.ToDxBrush(RenderTarget); TextFormat format = TextFontFamily.ToDirectWriteTextFormat(); TextLayout layout = new TextLayout(Core.Globals.DirectWriteFactory, text, format, 500, format.FontSize); SharpDX.Vector2 textOrigin = new SharpDX.Vector2( ChartPanel.W - DisplayOrdersOffset, currentY ); RenderTarget.DrawTextLayout(textOrigin, layout, brush, SharpDX.Direct2D1.DrawTextOptions.NoSnap); currentY += (int)layout.Metrics.Height + spaceBetween; layout.Dispose(); format.Dispose(); brush.Dispose(); } } protected override void OnBarUpdate() {} public override string DisplayName { get { return Name; } } [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name = "Order Filter", Order = 1, GroupName = "Order Display")] public int OrderFilter { get; set; } [NinjaScriptProperty] [Display(Name = "Combine Orders", Order = 2, GroupName = "Order Display")] public bool CombineOrders { get; set; } [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name = "Max Orders", Order = 3, GroupName = "Order Display")] public int MaxDisplayOrders { get; set; } [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name = "Display Offset", Order = 4, GroupName = "Order Display")] public int DisplayOrdersOffset { get; set; } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Buy Order Color", Order = 1, GroupName = "Order Colors")] public Brush BuyOrderColor { get; set; } [Browsable(false)] public string BuyOrderColorSerialization { get { return Serialize.BrushToString(BuyOrderColor); } set { BuyOrderColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Sell Order Color", Order = 2, GroupName = "Order Colors")] public Brush SellOrderColor { get; set; } [Browsable(false)] public string SellOrderColorSerialization { get { return Serialize.BrushToString(SellOrderColor); } set { SellOrderColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Display(Name = "Timestamp Format", Order = 1, GroupName = "Text Formatting")] public string TimestampFormat { get; set; } [NinjaScriptProperty] [Display(Name = "Font Family", Order = 2, GroupName = "Text Formatting")] public SimpleFont TextFontFamily { get; set; } } } #region NinjaScript generated code. Neither change nor remove. namespace NinjaTrader.NinjaScript.Indicators { public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase { private Tape[] cacheTape; public Tape Tape(int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { return Tape(Input, orderFilter, combineOrders, maxDisplayOrders, displayOrdersOffset, buyOrderColor, sellOrderColor, timestampFormat, textFontFamily); } public Tape Tape(ISeries input, int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { if (cacheTape != null) for (int idx = 0; idx < cacheTape.Length; idx++) if (cacheTape[idx] != null && cacheTape[idx].OrderFilter == orderFilter && cacheTape[idx].CombineOrders == combineOrders && cacheTape[idx].MaxDisplayOrders == maxDisplayOrders && cacheTape[idx].DisplayOrdersOffset == displayOrdersOffset && cacheTape[idx].BuyOrderColor == buyOrderColor && cacheTape[idx].SellOrderColor == sellOrderColor && cacheTape[idx].TimestampFormat == timestampFormat && cacheTape[idx].TextFontFamily == textFontFamily && cacheTape[idx].EqualsInput(input)) return cacheTape[idx]; return CacheIndicator(new Tape(){ OrderFilter = orderFilter, CombineOrders = combineOrders, MaxDisplayOrders = maxDisplayOrders, DisplayOrdersOffset = displayOrdersOffset, BuyOrderColor = buyOrderColor, SellOrderColor = sellOrderColor, TimestampFormat = timestampFormat, TextFontFamily = textFontFamily }, input, ref cacheTape); } } } namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns { public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase { public Indicators.Tape Tape(int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { return indicator.Tape(Input, orderFilter, combineOrders, maxDisplayOrders, displayOrdersOffset, buyOrderColor, sellOrderColor, timestampFormat, textFontFamily); } public Indicators.Tape Tape(ISeries input , int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { return indicator.Tape(input, orderFilter, combineOrders, maxDisplayOrders, displayOrdersOffset, buyOrderColor, sellOrderColor, timestampFormat, textFontFamily); } } } namespace NinjaTrader.NinjaScript.Strategies { public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase { public Indicators.Tape Tape(int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { return indicator.Tape(Input, orderFilter, combineOrders, maxDisplayOrders, displayOrdersOffset, buyOrderColor, sellOrderColor, timestampFormat, textFontFamily); } public Indicators.Tape Tape(ISeries input , int orderFilter, bool combineOrders, int maxDisplayOrders, int displayOrdersOffset, Brush buyOrderColor, Brush sellOrderColor, string timestampFormat, SimpleFont textFontFamily) { return indicator.Tape(input, orderFilter, combineOrders, maxDisplayOrders, displayOrdersOffset, buyOrderColor, sellOrderColor, timestampFormat, textFontFamily); } } } #endregion