1.) Open cTrader and go to the Automate tab on the left menu.
2.) Under the Indicators list, click New (the + icon).
3.) Name it SystematicOmissionJournal.
4.) Replace all the default code with the C# code below.
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
public enum LogType
{
RuleBasedOmission,
ExecutionHesitation
}
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class SystematicOmissionJournal : Indicator
{
// ---------------------------------------------------------------------------------------------
// INSTITUTIONAL COLOR PALETTE
// ---------------------------------------------------------------------------------------------
private readonly Color colRBO = Color.FromHex("#089981"); // Teal
private readonly Color colEH = Color.FromHex("#F23645"); // Red
private readonly Color colText = Color.White;
private readonly Color colBg = Color.FromHex("#131722");
private readonly Color colBorder = Color.FromHex("#363A45");
private int _rboCount = 0;
private int _ehCount = 0;
// ---------------------------------------------------------------------------------------------
// INPUTS (String based Time for safe cross-version parsing)
// ---------------------------------------------------------------------------------------------
[Parameter("Log 1 Active", Group = "Event 1", DefaultValue = false)]
public bool Log1Active { get; set; }
[Parameter("Time (YYYY-MM-DD HH:MM)", Group = "Event 1", DefaultValue = "2026-09-24 10:00")]
public string Log1Time { get; set; }
[Parameter("Price", Group = "Event 1", DefaultValue = 0.0)]
public double Log1Price { get; set; }
[Parameter("Classification", Group = "Event 1", DefaultValue = LogType.RuleBasedOmission)]
public LogType Log1Type { get; set; }
[Parameter("Filter / Note", Group = "Event 1", DefaultValue = "Spread/Cost Veto")]
public string Log1Note { get; set; }
[Parameter("Log 2 Active", Group = "Event 2", DefaultValue = false)]
public bool Log2Active { get; set; }
[Parameter("Time (YYYY-MM-DD HH:MM)", Group = "Event 2", DefaultValue = "2026-09-24 10:00")]
public string Log2Time { get; set; }
[Parameter("Price", Group = "Event 2", DefaultValue = 0.0)]
public double Log2Price { get; set; }
[Parameter("Classification", Group = "Event 2", DefaultValue = LogType.RuleBasedOmission)]
public LogType Log2Type { get; set; }
[Parameter("Filter / Note", Group = "Event 2", DefaultValue = "Macro/News Proximity")]
public string Log2Note { get; set; }
[Parameter("Log 3 Active", Group = "Event 3", DefaultValue = false)]
public bool Log3Active { get; set; }
[Parameter("Time (YYYY-MM-DD HH:MM)", Group = "Event 3", DefaultValue = "2026-09-24 10:00")]
public string Log3Time { get; set; }
[Parameter("Price", Group = "Event 3", DefaultValue = 0.0)]
public double Log3Price { get; set; }
[Parameter("Classification", Group = "Event 3", DefaultValue = LogType.RuleBasedOmission)]
public LogType Log3Type { get; set; }
[Parameter("Filter / Note", Group = "Event 3", DefaultValue = "HTF Invalidation")]
public string Log3Note { get; set; }
[Parameter("Log 4 Active", Group = "Event 4", DefaultValue = false)]
public bool Log4Active { get; set; }
[Parameter("Time (YYYY-MM-DD HH:MM)", Group = "Event 4", DefaultValue = "2026-09-24 10:00")]
public string Log4Time { get; set; }
[Parameter("Price", Group = "Event 4", DefaultValue = 0.0)]
public double Log4Price { get; set; }
[Parameter("Classification", Group = "Event 4", DefaultValue = LogType.RuleBasedOmission)]
public LogType Log4Type { get; set; }
[Parameter("Filter / Note", Group = "Event 4", DefaultValue = "Correlated Exposure")]
public string Log4Note { get; set; }
[Parameter("Log 5 Active", Group = "Event 5", DefaultValue = false)]
public bool Log5Active { get; set; }
[Parameter("Time (YYYY-MM-DD HH:MM)", Group = "Event 5", DefaultValue = "2026-09-24 10:00")]
public string Log5Time { get; set; }
[Parameter("Price", Group = "Event 5", DefaultValue = 0.0)]
public double Log5Price { get; set; }
[Parameter("Classification", Group = "Event 5", DefaultValue = LogType.ExecutionHesitation)]
public LogType Log5Type { get; set; }
[Parameter("Filter / Note", Group = "Event 5", DefaultValue = "Psychological Fatigue")]
public string Log5Note { get; set; }
protected override void Initialize()
{
_rboCount = 0;
_ehCount = 0;
ProcessLog(1, Log1Active, Log1Time, Log1Price, Log1Type, Log1Note);
ProcessLog(2, Log2Active, Log2Time, Log2Price, Log2Type, Log2Note);
ProcessLog(3, Log3Active, Log3Time, Log3Price, Log3Type, Log3Note);
ProcessLog(4, Log4Active, Log4Time, Log4Price, Log4Type, Log4Note);
ProcessLog(5, Log5Active, Log5Time, Log5Price, Log5Type, Log5Note);
DrawInstitutionalHUD();
}
public override void Calculate(int index)
{
// Calculation not required for static journaling annotations
}
// ---------------------------------------------------------------------------------------------
// DRAWING LOGIC: TAGS AND RAYS
// ---------------------------------------------------------------------------------------------
private void ProcessLog(int index, bool active, string timeStr, double price, LogType type, string note)
{
if (!active || price == 0) return;
if (!DateTime.TryParse(timeStr, out DateTime parsedTime))
{
Print($"SOJ Error: Could not parse time for Event {index}. Please use YYYY-MM-DD HH:MM format.");
return;
}
if (type == LogType.RuleBasedOmission) _rboCount++;
else _ehCount++;
Color tagColor = (type == LogType.RuleBasedOmission) ? colRBO : colEH;
string tag = (type == LogType.RuleBasedOmission) ? "RBO" : "EH";
// Text annotation on chart
string labelText = $"{tag} - {note}";
Chart.DrawText($"SOJ_TXT_{index}", labelText, parsedTime, price, tagColor);
// Calculate forward projection for the dotted line (approx 30 bars)
DateTime endTime = parsedTime.AddMinutes(Chart.TimeFrame.ToTimeSpan().TotalMinutes * 30);
// Draw dotted omission risk level
var line = Chart.DrawTrendLine($"SOJ_LINE_{index}", parsedTime, price, endTime, price, Color.FromArgb(100, tagColor));
line.LineStyle = LineStyle.Dots;
line.Thickness = 1;
}
// ---------------------------------------------------------------------------------------------
// UI CONSTRUCTION: DOCKED PERFORMANCE HUD
// ---------------------------------------------------------------------------------------------
private void DrawInstitutionalHUD()
{
int totalEvents = _rboCount + _ehCount;
double compliance = totalEvents > 0 ? ((double)_rboCount / totalEvents) * 100 : 0;
Color compColor = compliance >= 80 ? colRBO : colEH;
var mainPanel = new StackPanel
{
Orientation = Orientation.Vertical,
BackgroundColor = colBg,
Margin = new Thickness(10)
};
// Header
mainPanel.AddChild(new TextBlock { Text = "SESSION METRICS", Foreground = Color.Gray, FontWeight = FontWeight.Bold, Margin = new Thickness(0, 0, 0, 10) });
// Row 1: RBO
var row1 = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 5) };
row1.AddChild(new TextBlock { Text = "Systematic Omissions (RBO): ", Foreground = colRBO, Width = 180 });
row1.AddChild(new TextBlock { Text = _rboCount.ToString(), Foreground = colText, FontWeight = FontWeight.Bold });
mainPanel.AddChild(row1);
// Row 2: EH
var row2 = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 5) };
row2.AddChild(new TextBlock { Text = "Execution Hesitation (EH): ", Foreground = colEH, Width = 180 });
row2.AddChild(new TextBlock { Text = _ehCount.ToString(), Foreground = colText, FontWeight = FontWeight.Bold });
mainPanel.AddChild(row2);
// Row 3: Compliance Rate
var row3 = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 5, 0, 0) };
row3.AddChild(new TextBlock { Text = "Filter Compliance Rate: ", Foreground = colText, Width = 180 });
row3.AddChild(new TextBlock { Text = $"{Math.Round(compliance, 1)}%", Foreground = compColor, FontWeight = FontWeight.Bold });
mainPanel.AddChild(row3);
// Wrap in Border for aesthetic styling
var border = new Border
{
BorderColor = colBorder,
BorderThickness = 1,
Child = mainPanel,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = new Thickness(0, 0, 20, 30) // Offset slightly from exact corner
};
Chart.AddControl(border);
}
}
}