Page 2 of 2

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:01 pm
by PTScalper
Since cTrader natively runs C# (.NET), you can build this with a much cleaner event-driven architecture than Pine Script. Instead of continuously polling states in a loop, cTrader’s OnTick() and Positions.Closed event handlers allow for immediate, millisecond-accurate execution when a risk threshold is breached.

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:02 pm
by PTScalper
Here is the complete cBot framework. It utilizes cTrader’s native UI elements to build the HUD directly on the chart and handles the daily rollovers, event subscriptions, and position sizing dynamically.

Code: Select all

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProDisciplineFramework : Robot
    {
        // ==============================================================================
        // 1. INSTITUTIONAL RISK PARAMETERS
        // ==============================================================================
        [Parameter("Max Daily Drawdown (%)", Group = "Risk Management", DefaultValue = 2.0, MinValue = 0.1, Step = 0.1)]
        public double MaxDailyDrawdownPct { get; set; }

        [Parameter("Max Consecutive Losses", Group = "Risk Management", DefaultValue = 3)]
        public int MaxConsecutiveLosses { get; set; }

        [Parameter("Post-Loss Cooldown (Mins)", Group = "Risk Management", DefaultValue = 10)]
        public int RevengeCooldownMins { get; set; }

        // ==============================================================================
        // 2. STRESS PROTOCOL & SIZING
        // ==============================================================================
        [Parameter("Engage Stress Protocol", Group = "Life Stress Protocol", DefaultValue = false)]
        public bool StressMode { get; set; }

        [Parameter("Stress Sizing Exposure (%)", Group = "Life Stress Protocol", DefaultValue = 25.0, MinValue = 1.0, Step = 1.0)]
        public double StressExposurePct { get; set; }

        [Parameter("Base Risk Per Trade (%)", Group = "Execution", DefaultValue = 1.0, MinValue = 0.1, Step = 0.1)]
        public double BaseRiskPct { get; set; }

        // ==============================================================================
        // STATE VARIABLES
        // ==============================================================================
        private double _startOfDayEquity;
        private DateTime _currentSessionDate;
        private bool _isDailyLockout;
        private int _consecutiveLosses;
        private DateTime _lastLossTime;
        
        // UI Elements
        private TextBlock _statusText;
        private TextBlock _ddText;
        private TextBlock _streakText;
        private TextBlock _stressText;

        protected override void OnStart()
        {
            // Initialize session tracking
            _currentSessionDate = Server.Time.Date;
            _startOfDayEquity = Account.Equity;
            
            // Subscribe to position events for consecutive loss & cooldown tracking
            Positions.Closed += OnPositionClosed;

            // Build HUD
            InitializeUI();
        }

        protected override void OnTick()
        {
            CheckSessionRollover();
            EnforceDailyDrawdown();
            UpdateUI();
        }

        // ==============================================================================
        // EVENT HANDLERS & RISK LOGIC
        // ==============================================================================
        private void OnPositionClosed(PositionClosedEventArgs args)
        {
            // Only track trades opened by this framework (or remove condition to track manual trades)
            if (args.Position.SymbolName != SymbolName) return;

            if (args.Position.NetProfit < 0)
            {
                _lastLossTime = Server.Time;
                _consecutiveLosses++;
            }
            else
            {
                _consecutiveLosses = 0; // Reset streak on a win
            }
        }

        private void CheckSessionRollover()
        {
            if (Server.Time.Date != _currentSessionDate)
            {
                _currentSessionDate = Server.Time.Date;
                _startOfDayEquity = Account.Equity;
                _isDailyLockout = false;
                _consecutiveLosses = 0;
                Print("New trading session started. Risk parameters reset.");
            }
        }

        private void EnforceDailyDrawdown()
        {
            if (_isDailyLockout) return;

            double currentDrawdownPct = ((_startOfDayEquity - Account.Equity) / _startOfDayEquity) * 100;

            if (currentDrawdownPct >= MaxDailyDrawdownPct)
            {
                _isDailyLockout = true;
                FlattenAllPositions();
                Print($"CIRCUIT BREAKER: Max Daily Drawdown ({MaxDailyDrawdownPct}%) breached. Flattening.");
            }
        }

        private void FlattenAllPositions()
        {
            foreach (var position in Positions.FindAll("DisciplineFramework", SymbolName))
            {
                ClosePosition(position);
            }
        }

        // ==============================================================================
        // EXECUTION GATES & SIZING
        // ==============================================================================
        private bool IsCooldownActive()
        {
            if (_lastLossTime == default) return false;
            return (Server.Time - _lastLossTime).TotalMinutes < RevengeCooldownMins;
        }

        private bool CanTrade()
        {
            if (_isDailyLockout) return false;
            if (_consecutiveLosses >= MaxConsecutiveLosses) return false;
            if (IsCooldownActive()) return false;
            return true;
        }

        private double GetCalculatedVolume()
        {
            // Calculate raw risk amount based on equity
            double riskAmount = Account.Equity * (BaseRiskPct / 100);
            
            // Apply Stress Protocol Haircut
            if (StressMode)
            {
                riskAmount *= (StressExposurePct / 100);
            }

            // Convert raw amount to standardized lot units based on standard leverage/SL math
            // (Placeholder conversion: 1 standard lot per $1000 at risk)
            double rawVolume = (riskAmount / 1000) * 100000; 
            
            return Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.Down);
        }

        // ==============================================================================
        // PLACEHOLDER STRATEGY LOGIC
        // ==============================================================================
        protected override void OnBar()
        {
            // Simple logic placeholder (e.g., inside bar or engulfing setup)
            bool goLong = Bars.ClosePrices.Last(1) > Bars.OpenPrices.Last(1) && Bars.ClosePrices.Last(2) < Bars.OpenPrices.Last(2);
            bool goShort = Bars.ClosePrices.Last(1) < Bars.OpenPrices.Last(1) && Bars.ClosePrices.Last(2) > Bars.OpenPrices.Last(2);

            if (CanTrade())
            {
                if (goLong && Positions.Count(p => p.TradeType == TradeType.Buy) == 0)
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, GetCalculatedVolume(), "DisciplineFramework", 15, 30);
                }
                else if (goShort && Positions.Count(p => p.TradeType == TradeType.Sell) == 0)
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, GetCalculatedVolume(), "DisciplineFramework", 15, 30);
                }
            }
        }

        // ==============================================================================
        // PRO UI DASHBOARD
        // ==============================================================================
        private void InitializeUI()
        {
            var grid = new Grid(2, 4)
            {
                BackgroundColor = Color.FromArgb(200, 18, 18, 18),
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                Margin = 10,
            };

            var styleText = new Style();
            styleText.Set(ControlProperty.ForegroundColor, Color.Gray);
            styleText.Set(ControlProperty.Margin, new Thickness(5));
            styleText.Set(ControlProperty.FontSize, 10);

            // Headers
            grid.AddChild(new TextBlock { Text = "SYSTEM STATUS", Style = styleText }, 0, 0);
            grid.AddChild(new TextBlock { Text = "DD %", Style = styleText }, 0, 1);
            grid.AddChild(new TextBlock { Text = "STREAK", Style = styleText }, 0, 2);
            grid.AddChild(new TextBlock { Text = "STRESS MODE", Style = styleText }, 0, 3);

            // Dynamic Texts
            _statusText = new TextBlock { Style = styleText, ForegroundColor = Color.Teal, FontWeight = FontWeight.Bold };
            _ddText = new TextBlock { Style = styleText };
            _streakText = new TextBlock { Style = styleText };
            _stressText = new TextBlock { Style = styleText, Text = StressMode ? "ENGAGED" : "STANDBY", ForegroundColor = StressMode ? Color.MediumPurple : Color.Gray };

            grid.AddChild(_statusText, 1, 0);
            grid.AddChild(_ddText, 1, 1);
            grid.AddChild(_streakText, 1, 2);
            grid.AddChild(_stressText, 1, 3);

            var border = new Border
            {
                BorderColor = Color.FromArgb(255, 40, 40, 40),
                BorderThickness = 1,
                Child = grid
            };

            Chart.AddControl(border);
        }

        private void UpdateUI()
        {
            double currentDrawdownPct = ((_startOfDayEquity - Account.Equity) / _startOfDayEquity) * 100;
            currentDrawdownPct = Math.Max(0, currentDrawdownPct);

            // Drawdown Update
            _ddText.Text = $"{currentDrawdownPct:F2}% / {MaxDailyDrawdownPct:F1}%";
            _ddText.ForegroundColor = currentDrawdownPct > (MaxDailyDrawdownPct * 0.8) ? Color.Orange : Color.Silver;

            // Streak Update
            _streakText.Text = $"{_consecutiveLosses} Loss";
            _streakText.ForegroundColor = _consecutiveLosses > 0 ? Color.Orange : Color.Silver;

            // Status Update
            if (_isDailyLockout)
            {
                _statusText.Text = "MAX DD LOCK";
                _statusText.ForegroundColor = Color.Red;
            }
            else if (_consecutiveLosses >= MaxConsecutiveLosses)
            {
                _statusText.Text = "STREAK LOCK";
                _statusText.ForegroundColor = Color.Red;
            }
            else if (IsCooldownActive())
            {
                int remainingMins = RevengeCooldownMins - (int)(Server.Time - _lastLossTime).TotalMinutes;
                _statusText.Text = $"COOLDOWN ({remainingMins}m)";
                _statusText.ForegroundColor = Color.Orange;
            }
            else
            {
                _statusText.Text = "ACTIVE";
                _statusText.ForegroundColor = Color.Teal;
            }
            
            // Allow live toggling of Stress Protocol via cBot parameters during runtime
            _stressText.Text = StressMode ? "ENGAGED" : "STANDBY";
            _stressText.ForegroundColor = StressMode ? Color.MediumPurple : Color.Gray;
        }
    }
}

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:02 pm
by PTScalper
Key Differences & Architectural Advantages

OnPositionClosed Event: Pine Script has to parse historical trade arrays on bar close to detect a loss. In cAlgo, Positions.Closed += OnPositionClosed hooks directly into the broker feed, triggering the cooldown counter the exact millisecond the stop loss is hit.

True Real-Time Flattening: Because OnTick() processes every individual price update, a trade that dips your equity below the MaxDailyDrawdownPct limit is closed instantly mid-candle via FlattenAllPositions(), completely eliminating slippage gaps caused by waiting for bar closures.

UI Render Loop: The InitializeUI() and UpdateUI() functions map directly to WPF-style StackPanels and TextBlocks (which cTrader is built on). It gives you a clean, tear-free overlay that won't obscure raw price action or clutter your 1-minute structure zones.

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:03 pm
by PTScalper
Because MetaTrader bridges the gap between C# object-oriented paradigms and legacy procedural execution, the implementation splits into two distinct architectures.

MT5 allows us to replicate the event-driven accuracy of the cTrader OnPositionClosed hook using the OnTradeTransaction handler. MT4 lacks this event model, forcing us to poll the history pool procedurally inside the OnTick() loop.

Here is the complete professional framework for both platforms.

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:03 pm
by PTScalper
1. The MT5 (MQL5) Implementation

This utilizes CTrade for execution and OnTradeTransaction for asynchronous, millisecond-accurate loss detection—identical to the C# event-driven model.

Code: Select all

//+------------------------------------------------------------------+
//|                                    ProDisciplineFramework_MT5.mq5|
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>

input string   grpRisk = "--- Risk Parameters ---";
input double   MaxDailyDrawdownPct = 2.0;     // Max Daily Drawdown (%)
input int      MaxConsecutiveLosses = 3;      // Max Consecutive Losses
input int      RevengeCooldownMins = 10;      // Post-Loss Cooldown (Mins)

input string   grpStress = "--- Stress Protocol ---";
input bool     StressMode = false;            // Engage Stress Protocol
input double   StressExposurePct = 25.0;      // Stress Sizing Exposure (%)
input double   BaseRiskPct = 1.0;             // Base Risk Per Trade (%)

// State variables
double         startOfDayEquity;
datetime       currentSessionDate;
bool           isDailyLockout;
int            consecutiveLosses;
datetime       lastLossTime;
CTrade         trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    currentSessionDate = GetCurrentDay();
    startOfDayEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    isDailyLockout = false;
    consecutiveLosses = 0;
    
    InitializeUI();
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    CheckSessionRollover();
    EnforceDailyDrawdown();
    UpdateUI();

    if(CanTrade())
    {
        // Placeholder PA setup (e.g., engulfing)
        double close1 = iClose(_Symbol, PERIOD_CURRENT, 1);
        double open1 = iOpen(_Symbol, PERIOD_CURRENT, 1);
        double close2 = iClose(_Symbol, PERIOD_CURRENT, 2);
        double open2 = iOpen(_Symbol, PERIOD_CURRENT, 2);
        
        bool goLong = close1 > open1 && close2 < open2;
        bool goShort = close1 < open1 && close2 > open2;
        
        if(goLong && !PositionSelect(_Symbol))
            trade.Buy(GetCalculatedVolume(), _Symbol);
        else if(goShort && !PositionSelect(_Symbol))
            trade.Sell(GetCalculatedVolume(), _Symbol);
    }
}

//+------------------------------------------------------------------+
//| Trade Transaction Event (Replaces MT4 History Polling)           |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
    // Catch deals exactly when they hit the history pool
    if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
    {
        HistorySelect(0, TimeCurrent());
        ulong ticket = trans.deal;
        
        if(HistoryDealSelect(ticket))
        {
            if(HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol)
            {
                double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_COMMISSION) + HistoryDealGetDouble(ticket, DEAL_SWAP);
                if(profit < 0)
                {
                    lastLossTime = TimeCurrent();
                    consecutiveLosses++;
                }
                else if(profit > 0)
                {
                    consecutiveLosses = 0; // Reset streak on win
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Core Risk Logic                                                  |
//+------------------------------------------------------------------+
void CheckSessionRollover()
{
    datetime today = GetCurrentDay();
    if(today != currentSessionDate)
    {
        currentSessionDate = today;
        startOfDayEquity = AccountInfoDouble(ACCOUNT_EQUITY);
        isDailyLockout = false;
        consecutiveLosses = 0;
        Print("New trading session started. Risk parameters reset.");
    }
}

void EnforceDailyDrawdown()
{
    if(isDailyLockout) return;
    
    double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    double drawdownPct = ((startOfDayEquity - currentEquity) / startOfDayEquity) * 100.0;
    
    if(drawdownPct >= MaxDailyDrawdownPct)
    {
        isDailyLockout = true;
        FlattenAllPositions();
        Print("CIRCUIT BREAKER: Max Daily Drawdown breached. Flattening.");
    }
}

void FlattenAllPositions()
{
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionGetString(POSITION_SYMBOL) == _Symbol)
        {
            trade.PositionClose(ticket);
        }
    }
}

bool CanTrade()
{
    if(isDailyLockout) return false;
    if(consecutiveLosses >= MaxConsecutiveLosses) return false;
    if(lastLossTime > 0 && (TimeCurrent() - lastLossTime) < (RevengeCooldownMins * 60)) return false;
    return true;
}

double GetCalculatedVolume()
{
    double equity = AccountInfoDouble(ACCOUNT_EQUITY);
    double riskAmount = equity * (BaseRiskPct / 100.0);
    
    if(StressMode) riskAmount *= (StressExposurePct / 100.0);
    
    // Convert risk amount to lots (standardizing against contract size)
    double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
    double lots = riskAmount / (100 * tickValue); // Example math for generic 100 pip SL
    
    double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    return MathMax(minLot, MathRound(lots / stepLot) * stepLot);
}

datetime GetCurrentDay()
{
    MqlDateTime dt;
    TimeCurrent(dt);
    dt.hour = 0; dt.min = 0; dt.sec = 0;
    return StructToTime(dt);
}

//+------------------------------------------------------------------+
//| UI Generation                                                    |
//+------------------------------------------------------------------+
void InitializeUI()
{
    ObjectCreate(0, "HUD_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_CORNER, CORNER_RIGHT_LOWER);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_YDISTANCE, 10);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_XSIZE, 200);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_YSIZE, 100);
    ObjectSetInteger(0, "HUD_BG", OBJPROP_BGCOLOR, clrBlack);
    
    CreateLabel("HUD_TITLE", "SYSTEM STATUS:", 20, 80, clrGray);
    CreateLabel("HUD_STATUS", "ACTIVE", 120, 80, clrTeal);
    CreateLabel("HUD_DD_TXT", "DD %:", 20, 60, clrGray);
    CreateLabel("HUD_DD_VAL", "0.00%", 120, 60, clrSilver);
    CreateLabel("HUD_STREAK_TXT", "STREAK:", 20, 40, clrGray);
    CreateLabel("HUD_STREAK_VAL", "0 Loss", 120, 40, clrSilver);
    CreateLabel("HUD_STRESS_TXT", "STRESS MODE:", 20, 20, clrGray);
    CreateLabel("HUD_STRESS_VAL", StressMode ? "ENGAGED" : "STANDBY", 120, 20, StressMode ? clrMediumPurple : clrGray);
}

void CreateLabel(string name, string text, int x, int y, color clr)
{
    ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
}

void UpdateUI()
{
    double equity = AccountInfoDouble(ACCOUNT_EQUITY);
    double ddPct = MathMax(0.0, ((startOfDayEquity - equity) / startOfDayEquity) * 100.0);
    
    ObjectSetString(0, "HUD_DD_VAL", OBJPROP_TEXT, DoubleToString(ddPct, 2) + "% / " + DoubleToString(MaxDailyDrawdownPct, 1) + "%");
    ObjectSetInteger(0, "HUD_DD_VAL", OBJPROP_COLOR, ddPct > (MaxDailyDrawdownPct * 0.8) ? clrOrange : clrSilver);
    
    ObjectSetString(0, "HUD_STREAK_VAL", OBJPROP_TEXT, IntegerToString(consecutiveLosses) + " Loss");
    ObjectSetInteger(0, "HUD_STREAK_VAL", OBJPROP_COLOR, consecutiveLosses > 0 ? clrOrange : clrSilver);
    
    if(isDailyLockout)
    {
        ObjectSetString(0, "HUD_STATUS", OBJPROP_TEXT, "MAX DD LOCK");
        ObjectSetInteger(0, "HUD_STATUS", OBJPROP_COLOR, clrRed);
    }
    else if(consecutiveLosses >= MaxConsecutiveLosses)
    {
        ObjectSetString(0, "HUD_STATUS", OBJPROP_TEXT, "STREAK LOCK");
        ObjectSetInteger(0, "HUD_STATUS", OBJPROP_COLOR, clrRed);
    }
    else if(lastLossTime > 0 && (TimeCurrent() - lastLossTime) < (RevengeCooldownMins * 60))
    {
        int remainingMins = RevengeCooldownMins - (int)((TimeCurrent() - lastLossTime) / 60);
        ObjectSetString(0, "HUD_STATUS", OBJPROP_TEXT, "COOLDOWN (" + IntegerToString(remainingMins) + "m)");
        ObjectSetInteger(0, "HUD_STATUS", OBJPROP_COLOR, clrOrange);
    }
    else
    {
        ObjectSetString(0, "HUD_STATUS", OBJPROP_TEXT, "ACTIVE");
        ObjectSetInteger(0, "HUD_STATUS", OBJPROP_COLOR, clrTeal);
    }
}

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:03 pm
by PTScalper
2. The MT4 (MQL4) Core Differences

Because MT4 operates on a legacy procedural history pool without transaction event hooks, the OnTradeTransaction function from MT5 must be replaced with an active polling mechanism inside OnTick().

To port the above script to MT4, you substitute the execution engine (OrderSend instead of CTrade) and add this history-tracking module to detect the closed trades:

Code: Select all

// Required MT4 global state tracking
int lastHistoryTotal = 0;

void OnTick()
{
    CheckSessionRollover();
    CheckClosedTradesMT4(); // MT4 Polling mechanism
    EnforceDailyDrawdown();
    UpdateUI();
    
    // ... execution logic using OrderSend()
}

//+------------------------------------------------------------------+
//| MT4 Procedural History Polling                                   |
//+------------------------------------------------------------------+
void CheckClosedTradesMT4()
{
    int currentHistory = OrdersHistoryTotal();
    
    // Only parse the loop if the history pool has increased
    if(currentHistory > lastHistoryTotal)
    {
        for(int i = currentHistory - 1; i >= lastHistoryTotal; i--)
        {
            if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
            {
                if(OrderSymbol() == Symbol() && OrderMagicNumber() == 12345) // Assume your EA magic number
                {
                    double profit = OrderProfit() + OrderSwap() + OrderCommission();
                    
                    if(profit < 0)
                    {
                        lastLossTime = TimeCurrent();
                        consecutiveLosses++;
                    }
                    else if(profit > 0)
                    {
                        consecutiveLosses = 0; // Reset streak
                    }
                }
            }
        }
        lastHistoryTotal = currentHistory; // Update state tracker
    }
}

//+------------------------------------------------------------------+
//| MT4 Flatten Positions Logic                                      |
//+------------------------------------------------------------------+
void FlattenAllPositions()
{
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol())
            {
                if(OrderType() == OP_BUY)
                    OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrRed);
                else if(OrderType() == OP_SELL)
                    OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrRed);
            }
        }
    }
}

Re: July psychology scorecard: tilt days and wins

Posted: Mon Oct 05, 2026 5:03 pm
by PTScalper
By explicitly mapping lastHistoryTotal, you avoid looping through thousands of historical trades on a 1-minute chart during every tick, maintaining execution speed while still catching the exact second your order rejection or stop-loss hits the ledger.