Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class InstitutionalRiskDeskPro : Indicator
{
// =========================================================================
// 1. INPUTS & CONFIGURATION
// =========================================================================
[Parameter("Risk Per Trade (%)", Group = "Risk Management", DefaultValue = 1.0, MinValue = 0.1, Step = 0.1)]
public double RiskPercent { get; set; }
[Parameter("Max Stop Cap (Pips)", Group = "Risk Management", DefaultValue = 15.0)]
public double MaxStopPips { get; set; }
[Parameter("Include Spread in Risk", Group = "Risk Management", DefaultValue = true)]
public bool IncludeSpread { get; set; }
[Parameter("ATR Period", Group = "Volatility Engine", DefaultValue = 14)]
public int AtrPeriod { get; set; }
[Parameter("ATR Multiplier", Group = "Volatility Engine", DefaultValue = 1.5)]
public double AtrMult { get; set; }
[Parameter("Enforce Session", Group = "Session Constraints", DefaultValue = true)]
public bool UseSession { get; set; }
[Parameter("Start Hour (UTC)", Group = "Session Constraints", DefaultValue = 8)]
public int StartHour { get; set; }
[Parameter("End Hour (UTC)", Group = "Session Constraints", DefaultValue = 12)]
public int EndHour { get; set; }
// =========================================================================
// 2. OUTPUT BUFFERS & STATE
// =========================================================================
[Output("Long Valid", LineColor = "#00BFA5", Thickness = 2)]
public IndicatorDataSeries LongValid { get; set; }
[Output("Long Invalid", LineColor = "#FF5252", Thickness = 2)]
public IndicatorDataSeries LongInvalid { get; set; }
[Output("Short Valid", LineColor = "#00BFA5", Thickness = 2)]
public IndicatorDataSeries ShortValid { get; set; }
[Output("Short Invalid", LineColor = "#FF5252", Thickness = 2)]
public IndicatorDataSeries ShortInvalid { get; set; }
private AverageTrueRange _atr;
// UI Object References for Tick Updates
private TextBlock _statusText, _sizeText, _stopText, _spreadText, _sessionText;
private Border _uiPanel;
// =========================================================================
// 3. INITIALIZATION & UI CONSTRUCTION
// =========================================================================
protected override void Initialize()
{
_atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Simple);
ConstructUI();
}
private void ConstructUI()
{
var grid = new Grid(6, 2)
{
BackgroundColor = Color.FromArgb(220, 25, 25, 25),
ShowGridLines = true,
Margin = new Thickness(10)
};
// Helpers for clean UI generation
TextBlock AddHeader(string text, int row, int col)
{
var tb = new TextBlock { Text = text, ForegroundColor = Color.Gray, Margin = new Thickness(5), FontWeight = FontWeight.Bold };
grid.AddChild(tb, row, col);
return tb;
}
TextBlock AddValue(int row, int col)
{
var tb = new TextBlock { Text = "--", ForegroundColor = Color.White, Margin = new Thickness(5, 5, 10, 5), HorizontalAlignment = HorizontalAlignment.Right };
grid.AddChild(tb, row, col);
return tb;
}
// Headers
AddHeader("INSTITUTIONAL RISK DESK", 0, 0);
var titleRight = AddHeader(SymbolName, 0, 1);
titleRight.HorizontalAlignment = HorizontalAlignment.Right;
titleRight.ForegroundColor = Color.White;
// Rows
AddHeader("Session Status:", 1, 0);
_sessionText = AddValue(1, 1);
AddHeader("Live Spread:", 2, 0);
_spreadText = AddValue(2, 1);
AddHeader("Stop Dist (Pips):", 3, 0);
_stopText = AddValue(3, 1);
AddHeader("Suggested Volume:", 4, 0);
_sizeText = AddValue(4, 1);
AddHeader("VERDICT:", 5, 0);
_statusText = AddValue(5, 1);
_statusText.FontWeight = FontWeight.ExtraBold;
_uiPanel = new Border
{
BorderColor = Color.DimGray,
BorderThickness = new Thickness(1),
CornerRadius = 3,
Child = grid,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = new Thickness(0, 0, 20, 40)
};
Chart.AddControl(_uiPanel);
}
// =========================================================================
// 4. HISTORICAL BUFFER CALCULATIONS
// =========================================================================
public override void Calculate(int index)
{
if (index < AtrPeriod) return;
double atrValue = _atr.Result[index];
double stopDist = atrValue * AtrMult;
// Historical calculation does not include live spread to keep lines stable
double stopPips = stopDist / Symbol.PipSize;
bool withinCap = stopPips <= MaxStopPips;
DateTime barTime = Bars.OpenTimes[index];
bool inSession = !UseSession || (barTime.Hour >= StartHour && barTime.Hour < EndHour);
bool isValid = withinCap && inSession;
double closePrice = Bars.ClosePrices[index];
if (isValid)
{
LongValid[index] = closePrice - stopDist;
LongInvalid[index] = double.NaN;
ShortValid[index] = closePrice + stopDist;
ShortInvalid[index] = double.NaN;
}
else
{
LongValid[index] = double.NaN;
LongInvalid[index] = closePrice - stopDist;
ShortValid[index] = double.NaN;
ShortInvalid[index] = closePrice + stopDist;
}
}
// =========================================================================
// 5. LIVE TICK TELEMETRY & EXECUTION LOGIC
// =========================================================================
protected override void OnTick()
{
if (double.IsNaN(_atr.Result.LastValue)) return;
// 1. Gather Live Volatility & Spread
double liveSpreadPips = Symbol.Spread / Symbol.PipSize;
double atrDist = _atr.Result.LastValue * AtrMult;
double atrPips = atrDist / Symbol.PipSize;
// Scalpers must account for spread in their stop distance
double totalRiskPips = IncludeSpread ? atrPips + liveSpreadPips : atrPips;
// 2. Budget & Session Validation
bool withinCap = totalRiskPips <= MaxStopPips;
DateTime now = Server.Time;
bool inSession = !UseSession || (now.Hour >= StartHour && now.Hour < EndHour);
bool isValid = withinCap && inSession;
// 3. Exact Position Sizing Math
double riskAmount = Account.Balance * (RiskPercent / 100.0);
double exactVolume = 0;
if (totalRiskPips > 0 && Symbol.PipValue > 0)
{
exactVolume = riskAmount / (totalRiskPips * Symbol.PipValue);
}
double normalizedVolume = Symbol.NormalizeVolumeInUnits(exactVolume, RoundingMode.Down);
if (normalizedVolume < Symbol.VolumeInUnitsMin || !isValid)
{
normalizedVolume = 0;
}
// 4. Update UI Components Efficiently
_sessionText.Text = inSession ? "ACTIVE" : "CLOSED";
_sessionText.ForegroundColor = inSession ? Color.FromHex("#00BFA5") : Color.Gray;
_spreadText.Text = $"{Math.Round(liveSpreadPips, 1)} pips";
_stopText.Text = $"{Math.Round(totalRiskPips, 1)}";
_stopText.ForegroundColor = withinCap ? Color.White : Color.FromHex("#FF5252");
_sizeText.Text = isValid ? $"{normalizedVolume} Units" : "--";
if (!inSession)
{
_statusText.Text = "OUT OF SESSION";
_statusText.ForegroundColor = Color.Gray;
_uiPanel.BorderColor = Color.Gray;
}
else if (!withinCap)
{
_statusText.Text = "REJECT: OVER CAP";
_statusText.ForegroundColor = Color.FromHex("#FF5252");
_uiPanel.BorderColor = Color.FromHex("#FF5252");
}
else
{
_statusText.Text = "EXECUTE: IN BUDGET";
_statusText.ForegroundColor = Color.FromHex("#00BFA5");
_uiPanel.BorderColor = Color.FromHex("#00BFA5");
}
}
}
}