Advertisement IC Markets

Refining Entries, From 1H Zone to 1M Trigger

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

Architectural Notes for Implementation

Tick-Level Stop Resolution: Because OnTick() handles the trade management portion using SYMBOL_BID and SYMBOL_ASK, you avoid the classic MT4/MT5 backtesting pitfall where stops get slipped or ignored due to bar-open pricing. If a liquidity sweep spikes 21 pips deep into your 20-pip stop, this engine will log the -1R hit immediately mid-bar.

Separation of Concerns: The signal logic is strictly bound to the last_bar_time evaluation block. This saves CPU cycles during optimization, ensuring the EA only hunts for your setups (like an engulfing candle or a fast MA cross) once per bar, while managing the open risk on every single tick.

Spread Accuracy: The engine naturally accounts for the spread by executing Long entries at the ask and Short entries at the bid, and managing targets using the respective inverse quotes. Just ensure you use a fixed or accurate historical spread in the MT5 tester when testing those tight 20-pip thresholds.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

This cAlgo C# implementation leverages cTrader's OnTick and OnBar event handlers to cleanly separate tick-level trade management from your signal generation logic.

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class GhostExecutionsEngine : 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 Refined", DefaultValue = 12)]
        public int InpWindow { get; set; }

        // --- STATE VARIABLES ---
        private int _wideDir = 0; 
        private double _wideEp, _wideSl, _wideTp;

        private int _refDir = 0;
        private double _refEp, _refSl, _refTp;

        private bool _searchingLtf = false;
        private int _searchDir = 0;
        private int _barsSinceHtf = 0;

        // --- METRICS ---
        private double _wideRTotal = 0;
        private int _wideWins = 0, _wideLosses = 0;

        private double _refRTotal = 0;
        private int _refWins = 0, _refLosses = 0, _refMissed = 0;

        protected override void OnTick()
        {
            // 1. VIRTUAL TRADE MANAGEMENT (Tick-level precision for sweeps)
            if (_wideDir == 1)
            {
                if (Symbol.Bid >= _wideTp) { _wideRTotal += InpWideRR; _wideWins++; _wideDir = 0; }
                else if (Symbol.Bid <= _wideSl) { _wideRTotal -= 1.0; _wideLosses++; _wideDir = 0; }
            }
            else if (_wideDir == -1)
            {
                if (Symbol.Ask <= _wideTp) { _wideRTotal += InpWideRR; _wideWins++; _wideDir = 0; }
                else if (Symbol.Ask >= _wideSl) { _wideRTotal -= 1.0; _wideLosses++; _wideDir = 0; }
            }

            if (_refDir == 1)
            {
                if (Symbol.Bid >= _refTp) { _refRTotal += InpRefRR; _refWins++; _refDir = 0; }
                else if (Symbol.Bid <= _refSl) { _refRTotal -= 1.0; _refLosses++; _refDir = 0; }
            }
            else if (_refDir == -1)
            {
                if (Symbol.Ask <= _refTp) { _refRTotal += InpRefRR; _refWins++; _refDir = 0; }
                else if (Symbol.Ask >= _refSl) { _refRTotal -= 1.0; _refLosses++; _refDir = 0; }
            }

            UpdateDashboard();
        }

        protected override void OnBar()
        {
            if (_searchingLtf) _barsSinceHtf++;

            // --- 2. YOUR CUSTOM LOGIC PLACEHOLDERS ---
            // Evaluate raw price action logic or structural conditions here
            bool htfLongCond = false;  
            bool htfShortCond = false; 
            
            bool ltfLongCond = false;  
            bool ltfShortCond = false;

            // --- A. Evaluate HTF Trigger ---
            if (_wideDir == 0 && (htfLongCond || htfShortCond))
            {
                _wideDir = htfLongCond ? 1 : -1;
                _wideEp = htfLongCond ? Symbol.Ask : Symbol.Bid;

                double slDist = InpWideSL * Symbol.PipSize;
                double tpDist = slDist * InpWideRR;

                _wideSl = (_wideDir == 1) ? _wideEp - slDist : _wideEp + slDist;
                _wideTp = (_wideDir == 1) ? _wideEp + tpDist : _wideEp - tpDist;

                _searchingLtf = true;
                _searchDir = _wideDir;
                _barsSinceHtf = 0;
            }

            // --- B. Evaluate LTF Trigger ---
            if (_searchingLtf && _refDir == 0)
            {
                if (_barsSinceHtf > InpWindow)
                {
                    _searchingLtf = false;
                    _refMissed++;
                }
                else
                {
                    bool validLtfTrigger = (_searchDir == 1 && ltfLongCond) || (_searchDir == -1 && ltfShortCond);
                    if (validLtfTrigger)
                    {
                        _refDir = _searchDir;
                        _refEp = (_refDir == 1) ? Symbol.Ask : Symbol.Bid;

                        double slDist = InpRefSL * Symbol.PipSize;
                        double tpDist = slDist * InpRefRR;

                        _refSl = (_refDir == 1) ? _refEp - slDist : _refEp + slDist;
                        _refTp = (_refDir == 1) ? _refEp + tpDist : _refEp - tpDist;

                        _searchingLtf = false;
                    }
                }
            }
        }

        private void UpdateDashboard()
        {
            int wideTrades = _wideWins + _wideLosses;
            double wideWr = wideTrades > 0 ? ((double)_wideWins / wideTrades) * 100.0 : 0.0;

            int refTrades = _refWins + _refLosses;
            double refWr = refTrades > 0 ? ((double)_refWins / refTrades) * 100.0 : 0.0;

            string dash = string.Format(
                "--- GHOST EXECUTIONS ENGINE ---\n\n" +
                "1H STRUCTURAL (Wide)\n" +
                "Total Return: {0:F2} R\n" +
                "Win Rate: {1:F1}% ({2} trades)\n\n" +
                "LTF REFINED (Tight)\n" +
                "Total Return: {3:F2} R\n" +
                "Win Rate: {4:F1}% ({5} trades)\n" +
                "Missed Moves: {6}",
                _wideRTotal, wideWr, wideTrades,
                _refRTotal, refWr, refTrades, _refMissed
            );

            // Renders natively on the cTrader chart UI
            Chart.DrawStaticText("dashboard", dash, VerticalAlignment.Top, HorizontalAlignment.Left, Color.White);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

cTrader Implementation Advantages

Native Pip Handling: Unlike MT4/MT5 where you have to manually adjust for 3/5 digit brokers, cTrader's Symbol.PipSize abstracts that away entirely. 20.0 * Symbol.PipSize yields the precise decimal offset instantly.

Performance Optimization: Because Chart.DrawStaticText anchors to the UI layer, calling UpdateDashboard() on every tick is highly performant in cTrader, avoiding the UI stutter you sometimes get with MT5 Comment() updates during fast backtests.

Tick Data Fidelity: When running this in cTrader's backtester, select "Tick Data from Server". cTrader's historical spread and tick data is highly robust, giving you an exact read on whether those 20-pip stops are naturally surviving the structural wicks or getting clipped by spread widening before the real move happens.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

Here is an enterprise-grade implementation for cTrader. It upgrades the script from a basic state machine into an object-oriented architecture with a native, WPF-style UI dashboard, isolated trade state management, and advanced risk metrics (Profit Factor and Peak Drawdown).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

Ctrader Pro level

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}";
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: Refining Entries, From 1H Zone to 1M Trigger

Post by PTScalper »

Architectural Upgrades

Object-Oriented Encapsulation: The global state soup is gone. The VirtualTrade class now handles its own state logic, tick evaluation, PnL math, and performance metrics tracking. If you want to add a third strategy (e.g., a trailing stop variant), you simply instantiate a new VirtualTrade object.

Native WPF-Style Dashboard: Replaced the legacy text overlay with cTrader's native cAlgo.API.UI components (Grid, Border, TextBlock). It draws a modern, semi-transparent overlay that handles dynamic color-coding based on live R-multiples and keeps the chart clean.

Advanced Statistical Tracking: Standard expectancy math masks drawdown. The VirtualTrade class now continuously tracks Peak Drawdown in R-multiples and Profit Factor. A strategy might yield more total R, but if it generates a -15R drawdown to get there, the cognitive friction will likely cause a discretionary trader to abandon it.

Logging: Integrated standard Print() statements tied to the SearchState so you can visually verify in the cTrader Automate log exactly when the window opens, when it expires, and when the ghost entries are fired.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply