Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.API.UI;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class GhostExecutionsPro : Robot
{
[Parameter("Wide SL (pips)", Group = "HTF Structural", DefaultValue = 50.0)]
public double InpWideSL { get; set; }
[Parameter("Wide Target (R)", Group = "HTF Structural", DefaultValue = 2.0)]
public double InpWideRR { get; set; }
[Parameter("Refined SL (pips)", Group = "LTF Refined", DefaultValue = 20.0)]
public double InpRefSL { get; set; }
[Parameter("Refined Target (R)", Group = "LTF Refined", DefaultValue = 5.0)]
public double InpRefRR { get; set; }
[Parameter("LTF Search Window (bars)", Group = "LTF Engine", DefaultValue = 12)]
public int InpWindow { get; set; }
// --- Core Engine States ---
private VirtualTrade _wideTrade;
private VirtualTrade _refTrade;
private SearchState _searchState;
// --- UI Controls ---
private TextBlock _wideStatsUi;
private TextBlock _refStatsUi;
private TextBlock _wideRTotalUi;
private TextBlock _refRTotalUi;
protected override void OnStart()
{
_wideTrade = new VirtualTrade("1H Wide");
_refTrade = new VirtualTrade("LTF Refined");
_searchState = new SearchState();
InitializeDashboard();
}
protected override void OnTick()
{
// Tick-level execution to resolve intra-bar liquidity sweeps accurately
if (_wideTrade.IsActive)
_wideTrade.EvaluateTick(Symbol.Bid, Symbol.Ask, InpWideRR);
if (_refTrade.IsActive)
_refTrade.EvaluateTick(Symbol.Bid, Symbol.Ask, InpRefRR);
UpdateDashboard();
}
protected override void OnBar()
{
if (_searchState.IsSearching)
_searchState.BarsElapsed++;
// --- SIGNAL GENERATION (Replace with your actual price action / indicator logic) ---
bool isHtfLong = false; // e.g., price hits daily/1H demand
bool isHtfShort = false;
bool isLtfLong = false; // e.g., 5m micro-structure shift
bool isLtfShort = false;
// 1. Evaluate HTF Trigger
if (!_wideTrade.IsActive && (isHtfLong || isHtfShort))
{
var dir = isHtfLong ? TradeType.Buy : TradeType.Sell;
double entry = isHtfLong ? Symbol.Ask : Symbol.Bid;
_wideTrade.Open(dir, entry, InpWideSL * Symbol.PipSize, InpWideRR);
_searchState.StartSearch(dir);
Print($"[HTF] Activated {dir} at {entry}. Hunting LTF signal for next {InpWindow} bars.");
}
// 2. Evaluate LTF Trigger
if (_searchState.IsSearching && !_refTrade.IsActive)
{
if (_searchState.BarsElapsed > InpWindow)
{
_searchState.Reset();
_refTrade.MissedTrades++;
Print($"[LTF] Search window expired. Missed trade logged.");
}
else
{
bool validLtf = (_searchState.Direction == TradeType.Buy && isLtfLong) ||
(_searchState.Direction == TradeType.Sell && isLtfShort);
if (validLtf)
{
double entry = (_searchState.Direction == TradeType.Buy) ? Symbol.Ask : Symbol.Bid;
_refTrade.Open(_searchState.Direction, entry, InpRefSL * Symbol.PipSize, InpRefRR);
_searchState.Reset();
Print($"[LTF] Activated {_refTrade.Direction} at {entry}.");
}
}
}
}
// ==============================================================================
// UI & DASHBOARD ARCHITECTURE
// ==============================================================================
private void InitializeDashboard()
{
var grid = new Grid(4, 3)
{
BackgroundColor = Color.FromArgb(220, 30, 30, 30),
ShowGridLines = true,
Margin = new Thickness(10)
};
// Headers
grid.AddChild(CreateHeader("Strategy Model"), 0, 0);
grid.AddChild(CreateHeader("Expectancy & Metrics"), 0, 1);
grid.AddChild(CreateHeader("Total Return (R)"), 0, 2);
// Wide Row
grid.AddChild(CreateCell("1H Structural Zone\n(Wide SL)", Color.DodgerBlue), 1, 0);
_wideStatsUi = CreateCell("-", Color.White);
_wideRTotalUi = CreateCell("0.00 R", Color.Gray);
grid.AddChild(_wideStatsUi, 1, 1);
grid.AddChild(_wideRTotalUi, 1, 2);
// Refined Row
grid.AddChild(CreateCell("LTF Execution\n(Tight SL)", Color.DarkOrange), 2, 0);
_refStatsUi = CreateCell("-", Color.White);
_refRTotalUi = CreateCell("0.00 R", Color.Gray);
grid.AddChild(_refStatsUi, 2, 1);
grid.AddChild(_refRTotalUi, 2, 2);
var border = new Border
{
BorderColor = Color.DimGray,
BorderThickness = new Thickness(1),
CornerRadius = 5,
Child = grid,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Margin = new Thickness(20)
};
Chart.AddControl(border);
}
private TextBlock CreateHeader(string text) => new TextBlock
{
Text = text, Foreground = Color.DarkGray, Margin = new Thickness(10, 5, 10, 5), FontWeight = FontWeight.Bold
};
private TextBlock CreateCell(string text, Color color) => new TextBlock
{
Text = text, Foreground = color, Margin = new Thickness(10, 10, 10, 10), VerticalAlignment = VerticalAlignment.Center
};
private void UpdateDashboard()
{
_wideStatsUi.Text = _wideTrade.GetMetricsString();
_wideRTotalUi.Text = $"{_wideTrade.TotalR:F2} R";
_wideRTotalUi.Foreground = GetReturnColor(_wideTrade.TotalR);
_refStatsUi.Text = _refTrade.GetMetricsString();
_refRTotalUi.Text = $"{_refTrade.TotalR:F2} R";
_refRTotalUi.Foreground = GetReturnColor(_refTrade.TotalR);
}
private Color GetReturnColor(double r) => r > 0 ? Color.LimeGreen : r < 0 ? Color.Tomato : Color.Gray;
}
// ==============================================================================
// DOMAIN MODELS
// ==============================================================================
public class SearchState
{
public bool IsSearching { get; private set; }
public TradeType Direction { get; private set; }
public int BarsElapsed { get; set; }
public void StartSearch(TradeType dir)
{
IsSearching = true;
Direction = dir;
BarsElapsed = 0;
}
public void Reset()
{
IsSearching = false;
BarsElapsed = 0;
}
}
public class VirtualTrade
{
public string Name { get; }
public bool IsActive { get; private set; }
public TradeType Direction { get; private set; }
public double Entry { get; private set; }
public double SL { get; private set; }
public double TP { get; private set; }
public double TotalR { get; private set; }
public int Wins { get; private set; }
public int Losses { get; private set; }
public int MissedTrades { get; set; }
private double _peakR = 0;
public double MaxDrawdownR { get; private set; }
public double ProfitFactor => Losses == 0 ? (Wins > 0 ? double.PositiveInfinity : 0) : (Wins * _targetR) / Losses;
private double _targetR;
public VirtualTrade(string name)
{
Name = name;
}
public void Open(TradeType dir, double entry, double slDistance, double rr)
{
IsActive = true;
Direction = dir;
Entry = entry;
_targetR = rr;
SL = (dir == TradeType.Buy) ? entry - slDistance : entry + slDistance;
double tpDistance = slDistance * rr;
TP = (dir == TradeType.Buy) ? entry + tpDistance : entry - tpDistance;
}
public void EvaluateTick(double bid, double ask, double rr)
{
bool hitTp = (Direction == TradeType.Buy && bid >= TP) || (Direction == TradeType.Sell && ask <= TP);
bool hitSl = (Direction == TradeType.Buy && bid <= SL) || (Direction == TradeType.Sell && ask >= SL);
if (hitTp) CloseTrade(rr, true);
else if (hitSl) CloseTrade(-1.0, false);
}
private void CloseTrade(double rYield, bool isWin)
{
IsActive = false;
TotalR += rYield;
if (isWin) Wins++; else Losses++;
// Track Drawdown in R-Multiples
if (TotalR > _peakR) _peakR = TotalR;
double currentDd = _peakR - TotalR;
if (currentDd > MaxDrawdownR) MaxDrawdownR = currentDd;
}
public string GetMetricsString()
{
int total = Wins + Losses;
double wr = total > 0 ? ((double)Wins / total) * 100 : 0;
return $"WR: {wr:F1}% ({Wins}W / {Losses}L)\nMissed: {MissedTrades} | DD: -{MaxDrawdownR:F2}R\nPF: {ProfitFactor:F2}";
}
}
}