using System; using System.Collections.Generic; using TSLab.DataSource; namespace TSLab.Script.Handlers { #region Base public abstract class MarketPositionBase : IFootPrintMaker { [HandlerParameter(true, "3", Min = "1", Max = "10", Step = "1")] public double ColorPower { get; set; } [HandlerParameter(true, "1", Min = "1", Max = "10", Step = "1")] public double CombineSteps { get; set; } private FootPrint m_fp; protected Trade m_lastTrade; public FootPrint Execute(ISecurity source) { var count = source.Bars.Count; m_fp = new FootPrint(source, CombineSteps, AddTick); for (int i = 0; i < count; i++) { var bar = m_fp[i]; var trades = source.GetTrades(i); ProcessTrades(bar, trades); } for (int i = 0; i < count; i++) { var bar = m_fp[i]; UpdateColor(bar); } return m_fp; } private void ProcessTrades(FootPrint.Bar bar, IEnumerable trades) { foreach (var trade in trades) { ProcessTrade(bar, trade); m_lastTrade = trade; } } private void AddTick(FootPrint.Bar bar, IEnumerable barTrades) { bar.Lines.Clear(); ProcessTrades(bar, barTrades); UpdateColor(bar); } protected double ConvertPriceValue(double v) { return ColorPower <= 0 ? v : Math.Log(v, ColorPower); } protected abstract void ProcessTrade(FootPrint.Bar bar, Trade trade); protected abstract void UpdateColor(FootPrint.Bar bar); protected abstract FootPrint.Line GetLine(FootPrint.Bar bar, Trade trade); } #endregion #region Volume [HandlerName("Volume Footprint")] [HandlerCategory("Market Position")] public class VolumeMarketPosition : MarketPositionBase { public class VolumeLine : FootPrint.Line { public double Volume { get; set; } public override string ToString() { return Volume.ToString("F0"); } } protected override void ProcessTrade(FootPrint.Bar bar, Trade trade) { var line = (VolumeLine)GetLine(bar, trade); line.Volume += trade.Quantity; } protected override void UpdateColor(FootPrint.Bar bar) { double maxVolume = 0; foreach (VolumeLine line in bar.Lines) { maxVolume = Math.Max(maxVolume, line.Volume); } var logMax = ConvertPriceValue(maxVolume); foreach (VolumeLine line in bar.Lines) { var coef = line.Volume == 0 ? 0 : ConvertPriceValue(line.Volume) / logMax; var intCoef = Math.Min(120, (int)(coef * 120)); line.Color = new Color(50, 50, 255 - intCoef); } } protected override FootPrint.Line GetLine(FootPrint.Bar bar, Trade trade) { return bar.GetLine(trade.Price); } } #endregion #region Bid/Ask [HandlerName("Bid/Ask Footprint")] [HandlerCategory("Market Position")] public class BidAskMarketPosition : MarketPositionBase { public class BidAskLine : FootPrint.Line { public double AskVolume { get; set; } public double BidVolume { get; set; } public override string ToString() { return String.Format("{0:F0}x{1:F0}", AskVolume, BidVolume); } } #region ProcessTrade private bool m_lastWasAsk; protected override void ProcessTrade(FootPrint.Bar bar, Trade trade) { var line = (BidAskLine)GetLine(bar, trade); bool isProcessed = false; if(trade is TradeWithBidAsk) { var trd = (TradeWithBidAsk) trade; if(trd.AskPrice > 0) { line.AskVolume += trade.Quantity; isProcessed = true; } else if (trd.BidPrice > 0) { line.BidVolume += trd.Quantity; isProcessed = true; } } if(!isProcessed) { var lastPrice = m_lastTrade == null ? 0.0 : m_lastTrade.Price; if(trade.Price > lastPrice) { line.AskVolume += trade.Quantity; m_lastWasAsk = true; } else if (trade.Price < lastPrice) { line.BidVolume += trade.Quantity; m_lastWasAsk = false; } else { if(m_lastWasAsk) { line.AskVolume += trade.Quantity; } else { line.BidVolume += trade.Quantity; } } } } #endregion #region UpdateColor protected override void UpdateColor(FootPrint.Bar bar) { double maxVolume = 0; foreach (BidAskLine line in bar.Lines) { maxVolume = Math.Max(maxVolume, line.AskVolume); maxVolume = Math.Max(maxVolume, line.BidVolume); } var logMax = ConvertPriceValue(maxVolume); foreach (BidAskLine line in bar.Lines) { int r = 50; int g = 50; if(line.AskVolume >= line.BidVolume) { var coef = line.AskVolume == 0 ? 0 : ConvertPriceValue(line.AskVolume) / logMax; g = 255 - Math.Min(120, (int)(coef * 120)); } if (line.AskVolume <= line.BidVolume) { var coef = line.BidVolume == 0 ? 0 : ConvertPriceValue(line.BidVolume) / logMax; r = 255 - Math.Min(120, (int)(coef * 120)); } line.Color = new Color(r, g, 50); } } #endregion protected override FootPrint.Line GetLine(FootPrint.Bar bar, Trade trade) { return bar.GetLine(trade.Price); } } #endregion #region Delta [HandlerName("Delta Footprint")] [HandlerCategory("Market Position")] public class DeltaMarketPosition : BidAskMarketPosition { public class DeltaLine : BidAskLine { public override string ToString() { return String.Format("{0:F0}", AskVolume - BidVolume); } } protected override FootPrint.Line GetLine(FootPrint.Bar bar, Trade trade) { return bar.GetLine(trade.Price); } } #endregion }