Category Other  Published on 06/07/2023

Depth of Market Profile

Description

This indicator shows the Depth of Market in the main window. There are two ways to show the DOM: Bid-Ask splitted and overlayed -see image below-

To reset the indicator you can choose any timeframe in the parameter 'Period': Daily (default), H1, M30, etc. -see below-

This indicator is very configurable: colors, opacity, position, color gradient, pips per bar, etc.

 

NOTE: If you change the parameters then the indicator will miss the saved information, so the data will be resetted. 

 


using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class DOMStudioV2 : Indicator
    {
        [Parameter("TimeFrame", Group = "Period", DefaultValue = "Daily")]
        public TimeFrame PeriodTimeFrame { get; set; }
        [Parameter("Width  (Even)", Group = "Grid", DefaultValue = 20, MinValue = 6, Step = 2)]
        public int Width { get; set; }
        [Parameter("Height (Even)", Group = "Grid", DefaultValue = 200, MinValue = 2, Step = 2)]
        public int Height { get; set; }
        [Parameter("Vertical Scale (PIPs)", Group = "Grid", DefaultValue = 2, MinValue = 1)]
        public int Scale { get; set; }
        [Parameter("Shift Back", Group = "Profile Configuration", DefaultValue = 10, MinValue = 10)]
        public int Shift { get; set; }
        [Parameter("Split Bid-Ask", Group = "Profile Configuration", DefaultValue = true)]
        public bool SplitBidAsk { get; set; }
        [Parameter("Color Gradient", Group = "Color Configuration", DefaultValue = false)]
        public bool ColorGradient { get; set; }
        [Parameter("Color Opacity", Group = "Color Configuration", DefaultValue = 200, MinValue = 0, MaxValue = 255)]
        public int ColorOpacity { get; set; }
        [Parameter("Bid Color", Group = "Color Configuration", DefaultValue = "Coral")]
        public Color BidColor { get; set; }
        [Parameter("Ask Color", Group = "Color Configuration", DefaultValue = "DeepSkyBlue")] 
        public Color AskColor { get; set; } 
        
        private struct PriceBidAsk
        {
            public double Price;
            public double Bid;
            public double Ask;
            public bool BidFilled;
            public bool AskFilled;
        }
        
        private const string ASK = "ASK";
        private const string BID = "BID";
        private const int VOLUMEDIVISOR = 1000000;
        
        private int initialindex, tflastindex, lastindex;
        double initialprice;
        private PriceBidAsk[] values;
        private MarketDepth marketDepth;
        private Bars bars; 

        protected override void Initialize()
        {
            try
            {
                lastindex = tflastindex = -1;
                initialprice = -1;
                initialindex = Bars.ClosePrices.Count - 1;
                
                //forcing even numbers
                if (Width % 2 != 0)
                    Width++;
                if (Height % 2 != 0)
                    Height++;
                
                values = new PriceBidAsk[Height];
                ResetArray();
                bars = MarketData.GetBars(PeriodTimeFrame);
                marketDepth = MarketData.GetMarketDepth(SymbolName);
                marketDepth.Updated += MarketDepthUpdated;
            } catch (Exception)
            {
                throw;
            }
        }

        public override void Calculate(int index)
        {
            try
            {
                if (!IsNumber(index))
                    return;
                
                if (index != lastindex)
                {
                    lastindex = index;
                    if (index >= initialindex)
                    {
                        int tfindex = GetTimeFrameIndex(index);
                        if (tfindex > 0 && tfindex != tflastindex)
                        {
                            tflastindex = tfindex;
                            initialprice = GetInitialPrice(index, Scale);
                            ResetArray();
                        }
                    }
                }
            } catch (Exception)
            {
                throw;
            }
        }
        
        void MarketDepthUpdated()
        {
            int midheight = (int)(Height / 2);
            
            foreach (var entry in marketDepth.BidEntries)
            {
                double dPrice = VAL(entry.Price);
                double dVolume = VAL(entry.VolumeInUnits) / VOLUMEDIVISOR;
                if (IsNumber(dPrice) && IsNumber(dVolume) && IsNumber(initialprice) && IsNumber(Symbol.PipSize))
                {
                    double pips = GetPIPs(dPrice, initialprice, Symbol.PipSize);
                    int pos = (int)(pips / Scale); 
                    if (values[midheight + pos].Price == 0)
                        values[midheight + pos].Price = initialprice + ((pos * Scale) * Symbol.PipSize);
                    values[midheight + pos].Bid += dVolume;
                    values[midheight + pos].BidFilled = true;
                }
            }

            foreach (var entry in marketDepth.AskEntries)
            {
                double dPrice = VAL(entry.Price);
                double dVolume = VAL(entry.VolumeInUnits) / VOLUMEDIVISOR;
                if (IsNumber(dPrice) && IsNumber(dVolume) && IsNumber(initialprice) && IsNumber(Symbol.PipSize))
                {
                    double pips = GetPIPs(dPrice, initialprice, Symbol.PipSize);
                    int pos = (int)(pips / Scale); 
                    if (values[midheight + pos].Price == 0)
                        values[midheight + pos].Price = initialprice + ((pos * Scale) * Symbol.PipSize);
                    values[midheight + pos].Ask += dVolume;
                    values[midheight + pos].AskFilled = true;
                }
            }
            
            double maxvalue = GetMaxValue();
            if (maxvalue > 0) ShowValues(Width, Height, maxvalue, Bars.ClosePrices.Count - 1);
        }
        
        private void ResetArray()
        {
            for (int i = 0; i < Height; i++)
            {
                values[i].Price = 0;
                values[i].Bid = 0;
                values[i].Ask = 0;
                values[i].BidFilled = false;
                values[i].AskFilled = false;
            }
        }
        
        private double GetInitialPrice(int index, int gridpip)
        {
            double ret = -1;
            
            if (IsNumber(Bars.ClosePrices[index]) && IsNumber(Symbol.PipSize) && IsNumber(gridpip))
            {
                double totalpips = GetPIPs(Bars.ClosePrices[index], Symbol.PipSize);
                if (IsNumber(totalpips) && (gridpip > 0))
                {
                    int div = (int)(totalpips / gridpip);
                    int intdiv = div * gridpip;
                    ret = Symbol.PipSize * intdiv;
                }
            }
            
            return ret;
        }
        
        private double GetMaxValue()
        {
            double max = 0;
            
            for (int i = 0; i < Height; i++)
            {
                if (IsNumber(values[i].Bid) && values[i].BidFilled)
                {
                    if (values[i].Bid > max) 
                        max = values[i].Bid;
                }
                
                if (IsNumber(values[i].Ask) && values[i].AskFilled)
                {
                    if (values[i].Ask > max) 
                        max = values[i].Ask;
                }
            }
            
            return max;
        }
        
        private int GetTimeFrameIndex(int index)
        {
            int timeIndex;
                
            if (IsDatetime(Bars.OpenTimes[index])) 
                timeIndex = bars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
            else return -1;
            
            if (!IsNumber(timeIndex))
                return -1;
                
            return timeIndex;
        }   

        private bool IsNumber(double val)
        {
            return !double.IsNaN(val);
        }
        
        private double VAL(double value, double ret = 0)
        {
            if (IsNumber(value)) return value;
            else return ret;
        }
        
        private double DIVZ(double dividend, double divisor)
        {
            if ((divisor == 0) || (double.IsNaN(divisor)) || (double.IsNaN(dividend)))
                return double.NaN;
            else
                return dividend / divisor;
        }
    
        private double GetPIPs(double v0, double pipsize)
        {
            double ret = double.NaN;
    
            if (IsNumber(v0))
                ret = DIVZ(v0, pipsize);
    
            return ret;
        }
        
        private double GetPIPs(double v0, double v1, double pipsize)
        {
            double ret = double.NaN;
    
            if (IsNumber(v0) && IsNumber(v1) && IsNumber(pipsize))
                ret = DIVZ(v0 - v1, pipsize);
    
            return ret;
        }
        
        private bool IsDatetime(DateTime dt)   
        {
            bool ok = false;
            DateTime temp;
            
            if (dt != null)
            {
                if (DateTime.TryParse(dt.ToString(), out temp))
                    ok = true;
            }
            
            return ok;
        }
        
        //=========================================================================
        //                              SCREEN
        //=========================================================================
        
        private void ShowValues(int indexlength, int arraylength, double maxvalue, int index)
        {
            if (IsNumber(indexlength) && IsNumber(arraylength) && IsNumber(maxvalue) && IsNumber(index))
            {
                int firstindex = GetFirstIndex(indexlength, index);
                RemoveAllObjects(arraylength);
                
                for (int i = 0; i < arraylength; i++)
                {
                    if (IsNumber(values[i].Price))
                    {
                        if (values[i].BidFilled)
                        {
                            double bidvalue = SplitBidAsk ? -values[i].Bid : values[i].Bid;
                            int width =  (int)VAL((DIVZ((bidvalue * indexlength), maxvalue))); 
                            DrawRectangle(GetObjectName(BID, i), firstindex, GetMargin(values[i].Price, Scale, true), firstindex + width, GetMargin(values[i].Price, Scale, false), ColorGradient ? GetGradientColor(values[i].Bid, maxvalue, BidColor) : GetOpacity(BidColor));
                        }
                        
                        if (values[i].AskFilled)
                        {
                            int width =  (int)VAL((DIVZ((values[i].Ask * indexlength), maxvalue))); 
                            DrawRectangle(GetObjectName(ASK, i), firstindex, GetMargin(values[i].Price, Scale, true), firstindex + width, GetMargin(values[i].Price, Scale, false), ColorGradient ? GetGradientColor(values[i].Ask, maxvalue, AskColor) : GetOpacity(AskColor));
                        }
                    }
                }
            }
        }
        
        private double GetMargin(double price, double gridpips, bool up)
        {
            double ret = -1;
            
            if (IsNumber(price) && IsNumber(gridpips) && IsNumber(Symbol.PipSize))
            {
                double pricepips = GetPIPs(price, Symbol.PipSize);
                if (IsNumber(pricepips))
                {
                    double shiftedpricepips = up == true ? pricepips + (gridpips/2) : pricepips - (gridpips/2);
                    ret = shiftedpricepips * Symbol.PipSize;
                }
            }
            
            return ret;
        }
        
        private Color GetGradientColor(double val, double maxval, Color RectangleColor)
        {
            Color ret = RectangleColor;
            
            if (IsNumber(val) && IsNumber(maxval) && IsNumber(ColorOpacity))
            {
                int transparency = (int)((Math.Abs(val) * 100) / maxval) + ColorOpacity - 155;
                ret = Color.FromArgb(transparency, RectangleColor);
            }
            
            return ret;
        }
        
        private Color GetOpacity(Color RectangleColor)
        {
            Color ret = RectangleColor;
            
            if (IsNumber(ColorOpacity))
            {
                ret = Color.FromArgb(ColorOpacity, RectangleColor);
            }
            
            return ret;
        }
        
        private int GetFirstIndex(int indexlength, int index)
        {
            return index - (indexlength) - Shift;
        }
        
        private void DrawRectangle(string name, int x1, double y1, int x2, double y2, Color color)
        {
            if (IsNumber(x1) && IsNumber(y1) && IsNumber(x2) && IsNumber(y2) && (name != null))
            {
                ChartRectangle rectangle = Chart.DrawRectangle(name, x1, y1, x2, y2, color);
                rectangle.IsFilled = true;
            }
        }
        
        private string GetObjectName(string type, int arrayindex)
        {
            return type + "-" + this.InstanceId + "-" + arrayindex;
        }
        
        private void RemoveAllObjects(int arraylength)
        {
            for (int i = 0; i < arraylength; i++)
            {
                RemoveObject(GetObjectName(BID, i));
                RemoveObject(GetObjectName(ASK, i));
            }
        }
        
        private void RemoveObject(string name)
        {
            ChartObject obj = Chart.FindObject(name);
            if (obj != null)
                Chart.RemoveObject(name);
        }
    }
}


Abstract's avatar
Abstract

Joined on 08.08.2021

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: DOMStudioV4.algo
  • Rating: 0
  • Installs: 966
Comments
Log in to add a comment.
No comments found.