Warning! This section will be deprecated on February 1st 2025. Please move all your Indicators to the cTrader Store catalogue.
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
Joined on 08.08.2021
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: DOMStudioV4.algo
- Rating: 0
- Installs: 1270
- Modified: 06/07/2023 16:02
Note that publishing copyrighted material is strictly prohibited. If you believe there is copyrighted material in this section, please use the Copyright Infringement Notification form to submit a claim.
Comments
Log in to add a comment.
No comments found.