Description
VFX Margin Visualizer allows you to visualize your actual margin level on the on-chart scale together with 100% and Stop-Out level lines and highlighted areas.
You can also simulate effect of a custom leverage set via the Leverage parameter or see the impact of a increased/reduced account balance.
Version history:
v1.0 - initial release
v1.1 - minor enhancements
v1.2 - fixed error causing deletion of all chart objects
v1.3 - minor visual enhancements
v1.4 - balance change input added
// Margin Visualizer v1.4 by VFX
// https://ctrader.com/algos/indicators/show/2629
// Version history:
// v1.0 - initial release
// v1.1 - minor enhancements
// v1.2 - fixed error causing deletion of all chart objects
// v1.3 - minor visual enhancements
// v1.4 - balance change input added
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using System.Linq;
namespace cAlgo.Indicators
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class MarginVisualizer : Indicator
{
[Parameter("Leverage (0=real)", Group = "General", DefaultValue = 0, MinValue = 0, MaxValue = 1000, Step = 50)]
public int parLeverage { get; set; }
[Parameter("Balance +/- (0=real)", Group = "General", DefaultValue = 0, Step = 100)]
public int parBalanceChange { get; set; }
[Parameter("Stop-Out Level", Group = "General", DefaultValue = 50, MaxValue = 100, MinValue = 10, Step = 5)]
public int parStopOutLevel { get; set; }
[Parameter("Show Warning Line (100%)", Group = "General", DefaultValue = false)]
public bool parShowML100 { get; set; }
[Parameter("Show Warning Area", Group = "General", DefaultValue = true)]
public bool parShowWarningArea { get; set; }
[Parameter("Show Stop-Out Line", Group = "General", DefaultValue = true)]
public bool parShowMLStop { get; set; }
[Parameter("Show Stop-Out Area", Group = "General", DefaultValue = true)]
public bool parShowStopArea { get; set; }
[Parameter("Averaging Factor", Group = "General", DefaultValue = 0.5, MinValue = 0, MaxValue = 0.99)]
public double parAvgFactor { get; set; }
[Parameter("Show Scale", Group = "Scale", DefaultValue = true)]
public bool parShowScale { get; set; }
[Parameter("Show Stop-Out Label", Group = "Scale", DefaultValue = false)]
public bool parShowMLStopLabel { get; set; }
[Parameter("Scale Alignment", Group = "Scale", DefaultValue = AlignmentType.Right)]
public AlignmentType parAlignment { get; set; }
[Parameter("Scale Indent", Group = "Scale", DefaultValue = 0, MinValue = 0, MaxValue = 500, Step = 5)]
public int parHindent { get; set; }
[Parameter("Scale Step Factor", Group = "Scale", DefaultValue = 10, MinValue = 1, MaxValue = 100, Step = 1)]
public int parScaleStepFactor { get; set; }
[Parameter("Scale Length Factor", Group = "Scale", DefaultValue = 2, MinValue = 1, MaxValue = 10, Step = 1)]
public int parScaleLengthFactor { get; set; }
[Parameter("Show Leverage Needed", Group = "Scale", DefaultValue = true)]
public bool parShowLevNeeded { get; set; }
[Parameter("Line Thickness", Group = "Format", DefaultValue = 1, MinValue = 1)]
public int parThickness { get; set; }
[Parameter("Scale Color", Group = "Format", DefaultValue = "DimGray")]
public string parScaleColor { get; set; }
[Parameter("Warning Color", Group = "Format", DefaultValue = "Pink")]
public string parWarningColor { get; set; }
[Parameter("Stop-out Color", Group = "Format", DefaultValue = "DarkRed")]
public string parStopOutColor { get; set; }
[Parameter("Areas Opacity", Group = "Format", DefaultValue = 40, MinValue = 10, MaxValue = 100, Step = 10)]
public int parAreasOpacity { get; set; }
[Parameter("Warning Area Gradient Steps", Group = "Format", DefaultValue = 20, MinValue = 0, MaxValue = 50, Step = 5)]
public int parGradientSteps { get; set; }
private Functions Fn = new Functions();
// Global variables
private double avgPrice, avgEquity, mlStopPrice, ml100Price;
private double currNetProfit, currentSymbolVolume;
private Color clr100, clrStop;
protected override void Initialize()
{
clr100 = Color.FromArgb(128, Color.FromName(parWarningColor));
clrStop = Color.FromArgb(128, Color.FromName(parStopOutColor));
avgPrice = Symbol.Bid;
avgEquity = Account.Equity;
Positions.Opened += OnPositionsOpened;
Positions.Closed += OnPositionsClosed;
Positions.Modified += OnPositionsModified;
Chart.ScrollChanged += OnChartScrollChanged;
Chart.SizeChanged += OnChartSizeChanged;
Chart.ZoomChanged += OnChartZoomChanged;
UpdatePositionsData();
DrawMarginLevels();
Timer.Start(1);
}
protected override void OnTimer()
{
DrawMarginLevels();
}
void UpdatePositionsData()
{
currentSymbolVolume = 0;
var positions = Positions.Where(p => p.SymbolName == Symbol.Name).ToList();
if (positions.Count > 0)
{
currentSymbolVolume = positions.Sum(t => t.VolumeInUnits * (t.TradeType == TradeType.Buy ? 1 : -1));
currNetProfit = positions.Sum(t => t.NetProfit);
}
}
void DrawMarginLevels()
{
double accCurrRate = Symbol.PipSize / Symbol.PipValue;
if (currentSymbolVolume != 0)
{
double marginUsed = Account.Margin * (parLeverage == 0 ? 1 : Math.Min(Account.PreciseLeverage, Symbol.DynamicLeverage[0].Leverage) / parLeverage);
avgPrice = parAvgFactor * avgPrice + (1.0 - parAvgFactor) * (currentSymbolVolume > 0 ? Symbol.Bid : Symbol.Ask);
avgEquity = parAvgFactor * avgEquity + (1.0 - parAvgFactor) * Account.Equity;
ml100Price = avgPrice - (avgEquity - 1.0 * marginUsed) / currentSymbolVolume * accCurrRate;
mlStopPrice = avgPrice - (avgEquity - parStopOutLevel / 100.0 * marginUsed) / currentSymbolVolume * accCurrRate;
if (parShowML100)
Chart.DrawHorizontalLine("ML100", ml100Price, clr100, parThickness, LineStyle.Solid);
if (parShowMLStop)
Chart.DrawHorizontalLine("MLStop", mlStopPrice, clrStop, parThickness, LineStyle.Solid);
if (parShowWarningArea)
{
double priceStep = (ml100Price - mlStopPrice) / (parGradientSteps + 1);
for (int i = 0; i <= parGradientSteps; i++)
{
ChartRectangle warningArea = Chart.DrawRectangle("ML100Area" + i, Bars.OpenTimes[0], mlStopPrice + i * priceStep, Bars.OpenTimes[Chart.LastVisibleBarIndex].AddYears(10), mlStopPrice + (i + 1) * priceStep, Color.FromArgb((int)((double)parAreasOpacity * (1.0 - 0.9 * i / (double)(parGradientSteps + 1))), parWarningColor));
warningArea.IsFilled = true;
warningArea.Thickness = 0;
warningArea.ZIndex = -1000;
}
}
if (parShowStopArea)
{
ChartRectangle stopArea = Chart.DrawRectangle("MLStopArea", Bars.OpenTimes[0], mlStopPrice, Bars.OpenTimes[Chart.LastVisibleBarIndex].AddYears(10), avgPrice - avgEquity / currentSymbolVolume * accCurrRate, Color.FromArgb(parAreasOpacity, clrStop));
stopArea.IsFilled = true;
stopArea.Thickness = 0;
stopArea.ZIndex = -1000;
}
int positionIndex = Chart.FirstVisibleBarIndex + (parAlignment == AlignmentType.Right ? Chart.MaxVisibleBars - parHindent - 1 : parHindent + 1);
if (parShowScale)
{
double scale100Size = avgPrice / (parLeverage == 0 ? Math.Min(Account.PreciseLeverage, Symbol.DynamicLeverage[0].Leverage) : parLeverage);
// int scaleStep = Fn.ToSignificantStep((Chart.TopY - Chart.BottomY) / parScaleStepFactor / scale100Size * 100);
int scaleStep = Fn.ToSignificantStep((Chart.TopY - Chart.BottomY) / parScaleStepFactor / scale100Size * 100);
int scaleMax = 100 + scaleStep * parScaleStepFactor * parScaleLengthFactor;
for (int scaleLevel = 100; scaleLevel <= scaleMax; scaleLevel += scaleStep)
DrawScaleStep(positionIndex, scaleLevel, marginUsed);
// for (int scaleLevel = 100 - scaleStep; scaleLevel > parStopOutLevel + scaleStep; scaleLevel -= scaleStep)
// DrawScaleStep(positionIndex, scaleLevel, marginUsed);
DrawScaleStep(positionIndex, 100, marginUsed, parShowML100);
if (parShowMLStopLabel)
DrawScaleStep(positionIndex, parStopOutLevel, marginUsed, parShowMLStop || parShowStopArea || parShowWarningArea);
}
string stepSignRight = parAlignment == AlignmentType.Right ? new string((char)160, 4) : string.Empty;
string stepSignLeft = parAlignment == AlignmentType.Left ? string.Empty : new string((char)160, 4);
if (parShowLevNeeded || parLeverage > 0)
{
ChartText text = Chart.DrawText("MLUSED", (parLeverage > 0 ? stepSignLeft + "TEST Leverage: 1:" + parLeverage.ToString() : "Leverage Needed: 1:" + Fn.ToRoundedString((double)(Math.Min(Account.PreciseLeverage, Symbol.DynamicLeverage[0].Leverage) * 100.0 / Account.MarginLevel), 1)) + stepSignRight, positionIndex, mlStopPrice, parLeverage > 0 ? Color.Red : parScaleColor);
text.HorizontalAlignment = parAlignment == AlignmentType.Right ? HorizontalAlignment.Left : HorizontalAlignment.Right;
text.VerticalAlignment = VerticalAlignment.Bottom;
}
if (parBalanceChange != 0)
{
ChartText text = Chart.DrawText("BALANCEBIAS", stepSignLeft + "Acc.Balance " + (parBalanceChange > 0 ? "+" : "") + parBalanceChange.ToString() + stepSignRight, positionIndex, mlStopPrice, Color.Red);
text.HorizontalAlignment = parAlignment == AlignmentType.Right ? HorizontalAlignment.Left : HorizontalAlignment.Right;
text.VerticalAlignment = VerticalAlignment.Top;
}
}
}
void DrawScaleStep(int positionIndex, int scaleLevel, double marginUsed, bool isShifted = false)
{
string stepSignRight = parAlignment == AlignmentType.Right ? (isShifted ? new string((char)160, 4) : " —") : string.Empty;
string stepSignLeft = parAlignment == AlignmentType.Left ? (isShifted ? new string((char)160, 4) : "— ") : string.Empty;
double accCurrRate = Symbol.PipSize / Symbol.PipValue;
double stepPrice = avgPrice - (avgEquity - (double)scaleLevel * marginUsed / 100.0) / currentSymbolVolume * accCurrRate;
ChartText text = Chart.DrawText("MLScale" + scaleLevel.ToString("F0"), stepSignLeft + Fn.ToRoundedString(scaleLevel, 0) + "%" + stepSignRight, positionIndex, stepPrice, parScaleColor);
text.HorizontalAlignment = parAlignment == AlignmentType.Right ? HorizontalAlignment.Left : HorizontalAlignment.Right;
text.VerticalAlignment = isShifted ? VerticalAlignment.Top : VerticalAlignment.Center;
}
void OnChartZoomChanged(ChartZoomEventArgs obj)
{
RemoveObjects("ML");
DrawMarginLevels();
}
void OnChartSizeChanged(ChartSizeEventArgs obj)
{
RemoveObjects("ML");
DrawMarginLevels();
}
void OnChartScrollChanged(ChartScrollEventArgs obj)
{
RemoveObjects("ML");
DrawMarginLevels();
}
void OnPositionsOpened(PositionOpenedEventArgs obj)
{
UpdatePositionsData();
RemoveObjects("ML");
DrawMarginLevels();
}
void OnPositionsClosed(PositionClosedEventArgs obj)
{
UpdatePositionsData();
RemoveObjects("ML");
DrawMarginLevels();
}
void OnPositionsModified(PositionModifiedEventArgs obj)
{
UpdatePositionsData();
RemoveObjects("ML");
DrawMarginLevels();
}
private void RemoveObjects(string startsWith)
{
var cobjects = Chart.Objects.Where(o => o.Name.StartsWith(startsWith)).ToList();
foreach (var cobj in cobjects)
Chart.RemoveObject(cobj.Name);
}
public override void Calculate(int index)
{
}
public enum AlignmentType
{
Left,
Right
}
public class Functions
{
public int ToSignificantStep(double num)
{
double step = Math.Abs(num);
if (num == 0)
return 1;
int numorder = (int)Math.Pow(10, (int)Math.Floor(Math.Log10(step)));
step /= numorder;
int sigstep = 10;
if (step < 1.5)
sigstep = 1;
else if (step < 3)
sigstep = 2;
else if (step < 7)
sigstep = 5;
return sigstep * numorder * Math.Sign(num);
}
public string ToRoundedString(double num, int type, bool fix = false)
{
if (fix && (Math.Abs(num) < 1.0))
return num.ToString("#,##0." + new string('0', type));
else
{
if (Math.Abs(num) < 1E-05)
num = 0;
int shift = 0;
if ((type != 0) && (num != 0))
shift = (int)Math.Floor(Math.Log10(Math.Abs(num))) + ((Math.Abs(num) < 1) ? 1 : 0);
if (type - shift > 0)
return num.ToString("#,##0." + new string('0', type - shift));
else
return num.ToString("#,##0");
}
}
}
}
}
vitofx
Joined on 08.03.2021
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: VFX_Margin_Visualizer_v1.4.algo
- Rating: 5
- Installs: 1527
- Modified: 13/10/2021 09:54
Comments
Не работает на Индексах!)
I would suggest inserting the possibility of carrying out a simulation through input of a virtual order( both market and pending). would be useful for doing analysis. i.e. thank you for this amazing indicator.
Thank you very much !
I really needed this !
You saved me a lot of time with this great indicator.
Works perfectly thanks!
Hi Astroke - thank you! Download it again, it should be fixed now
Hi, wow nice work!
First issues i can't set 100% for Stop out lvl and my broker apply this setting. In any case very good idea.
Regards.
Hello VitoFX.
Such a cool indicator. Well done Mate. Very usefull.
I wanted to ask though if you have tested it on V4.5.1 ?
Do you think there is a chance you might consider adding in " what if scenarios " ?
Thanks again for this. Nice one.