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: 1211
- 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.