IC Markets

The "News Nuke" - Close All Positions Safely During Extreme Volatility

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Hi traders/scalpers :-)

We’ve all been there. A high-impact news event like CPI or NFP drops, the spread blows up, and price action goes completely erratic. If you need to bail out of the market instantly, manually clicking "Close" on multiple positions is a death sentence. By the time you get to your third trade, the price has moved 30 pips and your broker is spamming you with requotes.

To solve this, I wrote a lightweight, aggressive MQL4 script designed specifically to execute closes under heavy market stress.

Here is what makes this different from the standard generic "close all" scripts floating around:

Multi-Symbol Awareness: It uses MarketInfo() instead of standard Bid/Ask variables. This means you can drop this on any chart, and it will fetch the correct execution prices to close positions across all traded pairs.

Reverse Iteration: It loops through the order pool backward. If you loop forward and close an order, the terminal's indices shift, causing the script to skip trades. This prevents that.

Hardcore Retry Logic: During news spikes, brokers love throwing ERR_REQUOTE (138) or ERR_PRICE_CHANGED (139). This script intercepts execution failures and rapid-fires the close request again up to your defined limit.

Pending Order Wipe: Includes a toggle to instantly delete pending Limit/Stop orders so a widening spread doesn't drag you back into the market.

The MT4 Code
Create a new Script in MetaEditor, name it News_Panic_Close.mq4, and paste the following:

Code: Select all

//+------------------------------------------------------------------+
//|                                             News_Panic_Close.mq4 |
//|                       Closes all open positions with retry logic |
//+------------------------------------------------------------------+
#property strict
#property show_inputs

//--- Input Parameters
input int    MaxRetries   = 5;      // Number of retries on requotes/errors
input int    Slippage     = 50;     // Allowed slippage in points (higher for news)
input bool   ClosePending = true;   // Delete pending limit/stop orders as well?

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int total = OrdersTotal();
   
   // Loop backwards to prevent index shifting when an order is removed
   for(int i = total - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         bool result = false;
         int retries = 0;
         
         // Aggressive retry loop for news volatility 
         while(!result && retries < MaxRetries)
           {
            RefreshRates(); // Force update of local environment data
            
            int type = OrderType();
            double closePrice = 0.0;
            string symbol = OrderSymbol();
            
            // Calculate accurate close prices for multi-symbol scenarios
            if(type == OP_BUY)
              {
               closePrice = MarketInfo(symbol, MODE_BID);
               result = OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrRed);
              }
            else if(type == OP_SELL)
              {
               closePrice = MarketInfo(symbol, MODE_ASK);
               result = OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrBlue);
              }
            else if (ClosePending && (type == OP_BUYLIMIT || type == OP_SELLLIMIT || type == OP_BUYSTOP || type == OP_SELLSTOP))
              {
               result = OrderDelete(OrderTicket(), clrOrange);
              }
            else
              {
                 break; // Break loop if it's an unrecognized order type or pending is toggled off
              }
              
            // Handle broker execution errors
            if(!result)
              {
               int err = GetLastError();
               Print("Error closing order #", OrderTicket(), " on ", symbol, ". Error: ", err, ". Retrying...");
               Sleep(150); // Give the server a tiny breather before hammering it again
               retries++;
              }
            else
              {
               Print("Order #", OrderTicket(), " on ", symbol, " successfully closed/deleted.");
              }
           }
           
         if (!result)
           {
             Print("CRITICAL: Failed to close order #", OrderTicket(), " after ", MaxRetries, " retries.");
           }
        }
     }
     
   Print("News Panic Close execution finished.");
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Pro-Tip for Execution
Do not rely on dragging and dropping this from the Navigator window when seconds matter.

Right-click the script in your MT4 Navigator.

Select Set hotkey.

Bind it to something you won't hit by accident (e.g., Ctrl + Shift + C).

When the market loses its mind, just hit the hotkey and let the script fight the broker for you.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Here is the upgraded version.

To make this truly robust for high-volume execution, the script needs fail-safes that prevent it from getting stuck on fatal broker errors, and it needs proper filtering so it doesn’t accidentally wipe out positions it shouldn’t touch.

Key Improvements Added:
Auto-Adjusting Slippage (4/5 Digits): Hardcoding points is dangerous because 50 points on a 4-digit broker is massive, but on a 5-digit broker, it's tight. The script now calculates this automatically per symbol.

Targeted Filtering: Added inputs for MagicNumber and OnlyCurrentSymbol. This ensures you don't accidentally close positions managed by other EAs running concurrently.

Smart Error Handling: Instead of blindly retrying on any error, it reads the server code. It will only rapid-fire retry on broker execution friction (Requotes 138, Broker Busy 137, Price Changed 135, etc.). If it hits a fatal error (like an invalid ticket), it immediately skips to the next order instead of wasting time in a dead loop.

Pre-Flight Safety Checks:

Added IsConnected() and IsTradeAllowed() checks before execution to prevent the terminal from freezing if the connection to the broker has dropped.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

The Upgraded Code (v2.0)

Code: Select all

//+------------------------------------------------------------------+
//|                                    News_Panic_Close_Advanced.mq4 |
//|        Closes open positions with smart retry and filtering      |
//+------------------------------------------------------------------+
#property strict
#property show_inputs

//--- Input Parameters
input bool   CloseOnlyCurrentSymbol = false; // True = this chart only, False = all symbols
input int    FilterMagicNumber      = 0;     // 0 = close all, > 0 = specific EA trades only
input int    MaxRetries             = 5;     // Number of retries on requotes/busy errors
input int    SlippagePips           = 5;     // Allowed slippage in PIPS (auto-adjusts for 5-digit)
input bool   ClosePending           = true;  // Delete pending orders as well?
input bool   PlayAlert              = true;  // Trigger pop-up/sound when finished

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   // Safety check before attempting loop
   if(!IsConnected() || !IsTradeAllowed())
     {
      Print("CRITICAL: Terminal disconnected or trading context is busy!");
      if(PlayAlert) Alert("Panic Close Failed: Terminal disconnected!");
      return;
     }

   int total = OrdersTotal();
   int successCount = 0;
   
   for(int i = total - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         string sym = OrderSymbol();
         
         // --- Filtering ---
         if(CloseOnlyCurrentSymbol && sym != Symbol()) continue;
         if(FilterMagicNumber > 0 && OrderMagicNumber() != FilterMagicNumber) continue;
         
         int type = OrderType();
         
         // --- Auto-adjust slippage for 4 vs 5 digit brokers ---
         int digits = (int)MarketInfo(sym, MODE_DIGITS);
         int adjustedSlippage = SlippagePips;
         if(digits == 3 || digits == 5) adjustedSlippage = SlippagePips * 10;
         
         bool result = false;
         int retries = 0;
         
         // --- Execution Loop ---
         while(!result && retries < MaxRetries)
           {
            RefreshRates(); // Update local environment
            double closePrice = 0.0;
            
            if(type == OP_BUY)
              {
               closePrice = MarketInfo(sym, MODE_BID);
               result = OrderClose(OrderTicket(), OrderLots(), closePrice, adjustedSlippage, clrRed);
              }
            else if(type == OP_SELL)
              {
               closePrice = MarketInfo(sym, MODE_ASK);
               result = OrderClose(OrderTicket(), OrderLots(), closePrice, adjustedSlippage, clrBlue);
              }
            else if(ClosePending && type >= OP_BUYLIMIT && type <= OP_SELLSTOP)
              {
               result = OrderDelete(OrderTicket(), clrOrange);
              }
            else
              {
               break; // Unrecognized type, break execution loop
              }
              
            // --- Error Handling ---
            if(!result)
              {
               int err = GetLastError();
               
               // 135: Price changed, 136: Off quotes, 137: Broker busy, 138: Requote, 146: Trade context busy
               if(err == 135 || err == 136 || err == 137 || err == 138 || err == 146) 
                 {
                  Print("Retryable error on #", OrderTicket(), " [Err: ", err, "]. Retrying...");
                  Sleep(100); 
                  retries++;
                 }
               else 
                 {
                  Print("Fatal error on #", OrderTicket(), " [Err: ", err, "]. Skipping to next order.");
                  break; // Break loop on fatal errors (e.g., invalid ticket) so we don't hold up other trades
                 }
              }
            else
              {
               successCount++;
               Print("Successfully closed/deleted order #", OrderTicket());
              }
           }
        }
     }
     
   Print("News Panic Close execution finished.");
   if(PlayAlert) Alert("Panic Close Complete. Processed: ", successCount, " positions/orders.");
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

To take this from a solid retail script to an institutional-grade tool suited for high-volume scalping and professional algorithmic deployment, we need to address the realities of extreme market mechanics. When you are pushing serious volume during a news event, brokers don't just give you requotes—they throttle your lot sizes and widen spreads to toxic levels.

Here is the v3.0 architecture. This version introduces advanced volume management and spread-protection logic, making it a perfect high-value resource to share with a dedicated scalping community.

Enterprise-Grade Additions

1.) Large Lot Chunking (Volume Slicing): If you are closing a position that exceeds the broker’s MaxLot limit (e.g., trying to dump a 150-lot position when the broker caps at 50), standard scripts fail completely. This version detects the maximum permitted lot size and recursively slices the order into compliant chunks until the entire position is liquidated.

2.) Max Spread Protection Lock: During a CPI release, spreads can briefly widen to 30+ pips. If you market-close into that vacuum, you lock in catastrophic slippage. The MaxSpreadPoints filter prevents the script from executing if the current spread exceeds your predefined safety limit.

3.) Trade Context Mutex Simulation: MT4 is single-threaded for trade execution. If another EA is tying up the trade thread, order requests will bounce. The script now actively waits for the IsTradeContextBusy() flag to clear before firing off requests, drastically reducing ERR_TRADE_CONTEXT_BUSY (146) errors.

4.) Tick Value Normalization: Automatically rounds lot sizes to the broker's exact LotStep to prevent ERR_INVALID_STOPS or invalid lot size errors during partial closes.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

The Code (v3.0 - High-Volume Edition)

Code: Select all

//+------------------------------------------------------------------+
//|                                  News_Panic_Close_Enterprise.mq4 |
//|           High-volume position liquidation with spread logic     |
//+------------------------------------------------------------------+
#property strict
#property show_inputs

//--- Input Parameters
input string   Filter_Settings        = "--- Execution Filters ---";
input bool     CloseOnlyCurrentSymbol = false; 
input int      FilterMagicNumber      = 0;     

input string   Risk_Settings          = "--- Risk & Routing ---";
input int      MaxSpreadPoints        = 150;   // Abort close if spread exceeds this (points)
input int      SlippagePips           = 5;     
input int      MaxRetries             = 10;    
input bool     ClosePending           = true;  

//+------------------------------------------------------------------+
void OnStart()
  {
   if(!IsConnected() || !IsTradeAllowed())
     {
      Print("CRITICAL: Terminal disconnected or trading disabled!");
      return;
     }

   int total = OrdersTotal();
   int processedCount = 0;
   
   for(int i = total - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         string sym = OrderSymbol();
         
         if(CloseOnlyCurrentSymbol && sym != Symbol()) continue;
         if(FilterMagicNumber > 0 && OrderMagicNumber() != FilterMagicNumber) continue;
         
         // --- Spread Protection Check ---
         int currentSpread = (int)MarketInfo(sym, MODE_SPREAD);
         if(currentSpread > MaxSpreadPoints)
           {
            Print("WARNING: Spread on ", sym, " is ", currentSpread, " pts. Exceeds max limit. Skipping to protect capital.");
            continue;
           }

         int digits = (int)MarketInfo(sym, MODE_DIGITS);
         int adjSlippage = (digits == 3 || digits == 5) ? SlippagePips * 10 : SlippagePips;
         
         int type = OrderType();
         
         // --- Pending Order Wipe ---
         if(ClosePending && type >= OP_BUYLIMIT && type <= OP_SELLSTOP)
           {
            ExecuteDelete(OrderTicket());
            continue;
           }

         // --- High-Volume Lot Slicing Logic ---
         double lotsRemaining = OrderLots();
         double maxBrokerLot = MarketInfo(sym, MODE_MAXLOT);
         double lotStep = MarketInfo(sym, MODE_LOTSTEP);
         
         while(lotsRemaining > 0.0001) // Account for floating point inaccuracies
           {
            double lotsToClose = MathMin(lotsRemaining, maxBrokerLot);
            // Normalize lot size to broker requirements
            lotsToClose = MathRound(lotsToClose / lotStep) * lotStep;
            
            if(lotsToClose < MarketInfo(sym, MODE_MINLOT)) break; 

            bool result = ExecuteClose(OrderTicket(), lotsToClose, sym, type, adjSlippage);
            
            if(result) 
              {
               lotsRemaining -= lotsToClose;
               processedCount++;
              }
            else 
              {
               break; // Fatal error hit, break lot slicing loop
              }
           }
        }
     }
   Print("Enterprise Panic Close Complete. Executed actions: ", processedCount);
  }

//+------------------------------------------------------------------+
//| Order Close wrapper with Trade Context waiting and Retry Logic   |
//+------------------------------------------------------------------+
bool ExecuteClose(int ticket, double volume, string sym, int type, int slippage)
  {
   bool result = false;
   int retries = 0;
   
   while(!result && retries < MaxRetries)
     {
      // Wait for trade context to free up
      while(IsTradeContextBusy()) Sleep(50);
      
      RefreshRates();
      double closePrice = (type == OP_BUY) ? MarketInfo(sym, MODE_BID) : MarketInfo(sym, MODE_ASK);
      color clr = (type == OP_BUY) ? clrRed : clrBlue;
      
      result = OrderClose(ticket, volume, closePrice, slippage, clr);
      
      if(!result)
        {
         int err = GetLastError();
         if(err == 135 || err == 136 || err == 137 || err == 138 || err == 146) 
           {
            Print("Retryable close error #", ticket, " [Err: ", err, "]. Retrying...");
            Sleep(100); 
            retries++;
           }
         else 
           {
            Print("Fatal close error #", ticket, " [Err: ", err, "]. Aborting this order.");
            return false;
           }
        }
     }
   return result;
  }

//+------------------------------------------------------------------+
//| Order Delete wrapper with Trade Context waiting and Retry Logic  |
//+------------------------------------------------------------------+
bool ExecuteDelete(int ticket)
  {
   bool result = false;
   int retries = 0;
   
   while(!result && retries < MaxRetries)
     {
      while(IsTradeContextBusy()) Sleep(50);
      result = OrderDelete(ticket, clrOrange);
      
      if(!result)
        {
         int err = GetLastError();
         if(err == 137 || err == 146) // Usually just context busy for deletions
           {
            Sleep(100); 
            retries++;
           }
         else return false;
        }
     }
   return result;
  }
//+------------------------------------------------------------------+
Deployment Strategy

If this is running on an optimized server setup—like a low-latency VPS physically located near the broker's data center—the Sleep(50) commands inside the trade context loops will process incredibly fast.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Porting this over to MetaTrader 5 requires a structural shift. MT4 treats everything (open trades and pending limit/stop trades) as "Orders." MT5 splits this into two completely distinct categories: Positions (active trades in the market) and Orders (pending requests).

Because of this, the V1 script for MT5 requires two separate backward-iterating loops. We can also leverage the built-in CTrade library, which handles the heavy lifting of OrderSend execution and formatting for us.

Here is the direct V1 port for MT5. Create a new script in MetaEditor 5, name it News_Panic_Close_v1.mq5, and drop this in:

Code: Select all

//+------------------------------------------------------------------+
//|                                         News_Panic_Close_v1.mq5  |
//|                 MT5 Port - Closes all open positions and orders  |
//+------------------------------------------------------------------+
#property strict
#property script_show_inputs

#include <Trade\Trade.mqh>

//--- Input Parameters
input int    MaxRetries   = 5;      // Number of retries on requotes/errors
input ulong  Slippage     = 50;     // Allowed slippage in points
input bool   ClosePending = true;   // Delete pending limit/stop orders as well?

CTrade trade;

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   // Set the allowed slippage for the CTrade class
   trade.SetDeviationInPoints(Slippage);
   trade.LogLevel(LOG_LEVEL_ERRORS); // Only print real errors to the journal
   
   // --- 1. CLOSE ACTIVE POSITIONS ---
   int totalPositions = PositionsTotal();
   
   // Loop backwards to prevent index shifting
   for(int i = totalPositions - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
        {
         bool result = false;
         int retries = 0;
         string symbol = PositionGetString(POSITION_SYMBOL);
         
         // Retry loop for active positions
         while(!result && retries < MaxRetries)
           {
            result = trade.PositionClose(ticket);
            
            if(!result)
              {
               uint err = trade.ResultRetcode();
               Print("Error closing position #", ticket, " on ", symbol, ". Retcode: ", err, ". Retrying...");
               Sleep(150);
               retries++;
              }
            else
              {
               Print("Position #", ticket, " on ", symbol, " successfully closed.");
              }
           }
           
         if(!result)
           {
            Print("CRITICAL: Failed to close position #", ticket, " after ", MaxRetries, " retries.");
           }
        }
     }
     
   // --- 2. DELETE PENDING ORDERS ---
   if(ClosePending)
     {
      int totalOrders = OrdersTotal();
      
      for(int i = totalOrders - 1; i >= 0; i--)
        {
         ulong ticket = OrderGetTicket(i);
         if(ticket > 0)
           {
            bool result = false;
            int retries = 0;
            string symbol = OrderGetString(ORDER_SYMBOL);
            
            // Retry loop for pending orders
            while(!result && retries < MaxRetries)
              {
               result = trade.OrderDelete(ticket);
               
               if(!result)
                 {
                  uint err = trade.ResultRetcode();
                  Print("Error deleting pending order #", ticket, " on ", symbol, ". Retcode: ", err, ". Retrying...");
                  Sleep(150);
                  retries++;
                 }
               else
                 {
                  Print("Pending order #", ticket, " on ", symbol, " successfully deleted.");
                 }
              }
              
            if(!result)
              {
               Print("CRITICAL: Failed to delete order #", ticket, " after ", MaxRetries, " retries.");
              }
           }
        }
     }
     
   Print("News Panic Close MT5 execution finished.");
  }
//+------------------------------------------------------------------+
Key Differences to Note for MT5

CTrade Library: By using #include <Trade\Trade.mqh>, we don't have to manually pull Ask or Bid prices using SymbolInfoDouble(). The PositionClose() method automatically requests the correct market price.

Return Codes: MT4 uses GetLastError(). MT5 server return codes are captured via trade.ResultRetcode(). For example, 10004 means Requote, and 10006 means Request Rejected.

Hedging vs. Netting: This script works perfectly on both Hedging and Netting MT5 accounts. If you are on a Netting account, closing a position simply sends an opposite market order to flatten the net volume.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Here is the V3 Enterprise edition for MetaTrader 5.

MT5 handles execution asynchronously and supports ultra-low latency order routing, but during extreme news volatility, brokers frequently reject bulk liquidations if the volume exceeds symbol constraints or if spreads blow out.

This version incorporates institutional lot-slicing, dynamic spread-protection filters, retry logic tuned for MT5 return codes (TRADE_RETCODE_*), and independent loops for both active positions and pending orders.

Enterprise Architectural Features

1.) Recursive Partial Position Slicing (PositionClosePartial): Detects SYMBOL_VOLUME_MAX and SYMBOL_VOLUME_STEP. If an open position exceeds the maximum permitted lot size for a single execution ticket, it iteratively slices and closes the volume in broker-compliant chunks until the position is flat.

2.) Spread Threshold Guard: Queries SYMBOL_SPREAD in real time before attempting execution on any symbol. If the spread exceeds MaxSpreadPoints, the script aborts the close request for that specific instrument to avoid absorbing toxic market liquidity.

3.) MT5 Execution Return Code Interception: Rather than relying on generic sleep cycles, it inspects trade.ResultRetcode(). It instantly retries on transient network/broker delays (REQUOTE, PRICE_CHANGED, PRICE_OFF, CONNECTION_ERROR, SERVER_BUSY) while immediately aborting on fatal rejections (INVALID_VOLUME, TRADE_DISABLED, MARKET_CLOSED).

4.) Multi-Symbol & Magic Number Isolation: Cleanly filters target symbols and specific EA magic numbers, allowing you to liquidate specific algorithms or wipe all open exposure across the terminal.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

The Code (News_Panic_Close_Enterprise.mq5)

Code: Select all

//+------------------------------------------------------------------+
//|                               News_Panic_Close_Enterprise.mq5    |
//|               MT5 High-Volume Liquidation with Lot Slicing       |
//+------------------------------------------------------------------+
#property strict
#property script_show_inputs

#include <Trade\Trade.mqh>

//--- Execution Filters
input string   Filter_Settings        = "--- Execution Filters ---";
input bool     CloseOnlyCurrentSymbol = false; // Close only the chart's symbol?
input ulong    FilterMagicNumber      = 0;     // 0 = Close all, > 0 = Filter by Magic Number

//--- Risk & Execution Routing
input string   Risk_Settings          = "--- Risk & Routing ---";
input int      MaxSpreadPoints        = 150;   // Maximum allowed spread in points
input int      SlippagePips           = 5;     // Allowed slippage in PIPS
input int      MaxRetries             = 10;    // Max retry attempts per position slice
input bool     ClosePending           = true;  // Delete pending limit/stop orders as well?

CTrade trade;

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   // Pre-flight checks
   if(!TerminalInfoInteger(TERMINAL_CONNECTED) || !MQLInfoInteger(MQL_TRADE_ALLOWED))
     {
      Print("CRITICAL: Terminal disconnected or automated trading disabled in MT5!");
      return;
     }

   trade.LogLevel(LOG_LEVEL_ERRORS);
   int processedActions = 0;

   // =================================================================
   // 1. ACTIVE POSITIONS LIQUIDATION (WITH LOT SLICING)
   // =================================================================
   int totalPositions = PositionsTotal();

   for(int i = totalPositions - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;

      string symbol = PositionGetString(POSITION_SYMBOL);
      ulong  magic  = PositionGetInteger(POSITION_MAGIC);

      // Filtering logic
      if(CloseOnlyCurrentSymbol && symbol != _Symbol) continue;
      if(FilterMagicNumber > 0 && magic != FilterMagicNumber) continue;

      // Real-time Spread Check
      long currentSpread = SymbolInfoInteger(symbol, SYMBOL_SPREAD);
      if(currentSpread > MaxSpreadPoints)
        {
         PrintFormat("SPREAD GUARD: %s spread is %d pts (Max: %d). Skipping #%I64u to protect equity.",
                     symbol, currentSpread, MaxSpreadPoints, ticket);
         continue;
        }

      // Symbol volume constraints
      double maxLot   = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
      double minLot   = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
      double lotStep  = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
      int    digits   = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
      ulong  slippage = (digits == 3 || digits == 5) ? (ulong)(SlippagePips * 10) : (ulong)SlippagePips;

      trade.SetDeviationInPoints(slippage);

      double remainingVolume = PositionGetDouble(POSITION_VOLUME);

      // Volume slicing execution loop
      while(remainingVolume > (minLot / 2.0))
        {
         double volumeToClose = MathMin(remainingVolume, maxLot);
         volumeToClose = MathRound(volumeToClose / lotStep) * lotStep;

         if(volumeToClose < minLot) break;

         bool sliceSuccess = ExecutePositionClose(ticket, volumeToClose, symbol);

         if(sliceSuccess)
           {
            remainingVolume -= volumeToClose;
            processedActions++;
           }
         else
           {
            // Fatal broker rejection encountered; skip to avoid thread hang
            break;
           }
        }
     }

   // =================================================================
   // 2. PENDING ORDERS PURGE
   // =================================================================
   if(ClosePending)
     {
      int totalOrders = OrdersTotal();

      for(int i = totalOrders - 1; i >= 0; i--)
        {
         ulong ticket = OrderGetTicket(i);
         if(ticket == 0) continue;

         string symbol = OrderGetString(ORDER_SYMBOL);
         ulong  magic  = OrderGetInteger(ORDER_MAGIC);

         if(CloseOnlyCurrentSymbol && symbol != _Symbol) continue;
         if(FilterMagicNumber > 0 && magic != FilterMagicNumber) continue;

         if(ExecuteOrderDelete(ticket, symbol))
           {
            processedActions++;
           }
        }
     }

   PrintFormat("Enterprise MT5 Panic Close complete. Total successful operations: %d", processedActions);
  }

//+------------------------------------------------------------------+
//| Position Close Slicer with Return Code Handling                  |
//+------------------------------------------------------------------+
bool ExecutePositionClose(ulong ticket, double volume, string symbol)
  {
   bool result = false;
   int retries = 0;

   while(!result && retries < MaxRetries)
     {
      // Partial close handles both full lots and sliced volume
      result = trade.PositionClosePartial(ticket, volume);

      if(result)
        {
         PrintFormat("SUCCESS: Closed %.2f lots on #%I64u (%s)", volume, ticket, symbol);
         return true;
        }

      uint retcode = trade.ResultRetcode();

      // Inspect MT5 Server Return Codes
      switch(retcode)
        {
         case TRADE_RETCODE_REQUOTE:
         case TRADE_RETCODE_PRICE_CHANGED:
         case TRADE_RETCODE_PRICE_OFF:
         case TRADE_RETCODE_CONNECTION:
         case TRADE_RETCODE_TIMEOUT:
         case TRADE_RETCODE_SERVER_DISABLES_AT:
            PrintFormat("RETRYABLE ERROR: %s on #%I64u. Code: %u. Retrying (%d/%d)...",
                        symbol, ticket, retcode, retries + 1, MaxRetries);
            Sleep(80);
            retries++;
            break;

         default:
            PrintFormat("FATAL ERROR: %s on #%I64u. Code: %u (%s). Aborting order.",
                        symbol, ticket, retcode, trade.ResultRetcodeDescription());
            return false;
        }
     }

   return false;
  }

//+------------------------------------------------------------------+
//| Pending Order Deletion Wrapper                                   |
//+------------------------------------------------------------------+
bool ExecuteOrderDelete(ulong ticket, string symbol)
  {
   bool result = false;
   int retries = 0;

   while(!result && retries < MaxRetries)
     {
      result = trade.OrderDelete(ticket);

      if(result)
        {
         PrintFormat("SUCCESS: Deleted pending order #%I64u (%s)", ticket, symbol);
         return true;
        }

      uint retcode = trade.ResultRetcode();

      if(retcode == TRADE_RETCODE_REQUOTE || retcode == TRADE_RETCODE_CONNECTION || retcode == TRADE_RETCODE_TIMEOUT)
        {
         Sleep(80);
         retries++;
        }
      else
        {
         PrintFormat("FATAL: Failed to delete order #%I64u. Code: %u. Skipping.", ticket, retcode);
         return false;
        }
     }

   return false;
  }
//+------------------------------------------------------------------+
Operational Notes for MT5 Deployment

Deviation Setting: In MT5, slippage is set as deviation in points directly inside the MqlTradeRequest structure (which trade.SetDeviationInPoints() populates). On 5-digit brokers, the script automatically multiplies your SlippagePips input by 10.

Asynchronous vs. Synchronous Execution: This script uses synchronous CTrade calls to confirm each slice fills before proceeding to the next. For ultra-low latency VPS execution, the Sleep(80) interval provides enough time for the server order book to clear without stalling subsequent trade closures.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: The "News Nuke" - Close All Positions Safely During Extreme Volatility

Post by PTScalper »

Porting this architecture to cTrader requires adapting to its Direct Market Access (DMA) and STP/ECN mechanics. When you are executing high-volume scalping strategies—especially on historically volatile markets like forex and silver—cTrader handles execution very differently than MetaTrader.

Here are the critical architectural shifts for the cTrader port:

1.) Script Simulation: cTrader does not have a standalone "Script" folder anymore. To run a script, we build a cBot that executes its logic inside OnStart() and immediately calls Stop(). This ensures it fires exactly once and then shuts down.

2.) VWAP Execution & Slippage: Unlike MT5, cTrader's ClosePosition() method does not accept a slippage parameter. Market closures are executed aggressively against the order book at Volume-Weighted Average Price (VWAP). This makes the MaxSpreadPips guard absolutely vital—it prevents you from dumping massive volume into an empty order book during a news spike.

3.) Labels over Magic Numbers: cTrader groups algorithmic trades using string-based Labels rather than integer Magic Numbers. The filtering logic has been updated to reflect this.

The C# cTrader Code (V3 Enterprise)
Create a new cBot in cTrader Automate, name it News_Panic_Close_Enterprise, and paste the following C# code:

Code: Select all

using System;
using System.Linq;
using System.Threading;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NewsPanicCloseEnterprise : Robot
    {
        [Parameter("Close Only Current Symbol", Group = "Execution Filters", DefaultValue = false)]
        public bool CloseOnlyCurrentSymbol { get; set; }

        [Parameter("Filter Label", Group = "Execution Filters", DefaultValue = "")]
        public string FilterLabel { get; set; }

        [Parameter("Max Spread (Pips)", Group = "Risk & Routing", DefaultValue = 15.0)]
        public double MaxSpreadPips { get; set; }

        [Parameter("Max Retries", Group = "Risk & Routing", DefaultValue = 10)]
        public int MaxRetries { get; set; }

        [Parameter("Close Pending Orders", Group = "Risk & Routing", DefaultValue = true)]
        public bool ClosePending { get; set; }

        protected override void OnStart()
        {
            Print("Starting Enterprise Panic Close...");
            int processedActions = 0;

            // =================================================================
            // 1. ACTIVE POSITIONS LIQUIDATION (WITH VOLUME SLICING)
            // =================================================================
            var positionsToClose = Positions.Where(p => MatchFilters(p.SymbolName, p.Label)).ToArray();

            foreach (var pos in positionsToClose)
            {
                var sym = Symbols.GetSymbol(pos.SymbolName);
                
                // Real-time Spread Guard
                double currentSpreadPips = sym.Spread / sym.PipSize;
                if (currentSpreadPips > MaxSpreadPips)
                {
                    Print("SPREAD GUARD: {0} spread is {1:F1} pips (Max: {2:F1}). Skipping Position ID {3}.", 
                          sym.Name, currentSpreadPips, MaxSpreadPips, pos.Id);
                    continue;
                }

                double remainingVolume = pos.VolumeInUnits;
                
                // Volume slicing execution loop
                while (remainingVolume >= sym.VolumeInUnitsMin)
                {
                    double volumeToClose = Math.Min(remainingVolume, sym.VolumeInUnitsMax);
                    volumeToClose = sym.NormalizeVolumeInUnits(volumeToClose, RoundingMode.Down);
                    
                    if (volumeToClose < sym.VolumeInUnitsMin) break;

                    bool success = ExecutePositionClose(pos, volumeToClose, sym);
                    
                    if (success)
                    {
                        remainingVolume -= volumeToClose;
                        processedActions++;
                        
                        // Exit slice loop if remainder is negligible
                        if (remainingVolume < sym.VolumeInUnitsMin) break;
                    }
                    else
                    {
                        break; // Fatal error or max retries hit, break loop to protect thread
                    }
                }
            }

            // =================================================================
            // 2. PENDING ORDERS PURGE
            // =================================================================
            if (ClosePending)
            {
                var ordersToCancel = PendingOrders.Where(o => MatchFilters(o.SymbolName, o.Label)).ToArray();
                
                foreach (var order in ordersToCancel)
                {
                    if (ExecuteOrderCancel(order))
                    {
                        processedActions++;
                    }
                }
            }

            Print("Enterprise cTrader Panic Close complete. Processed actions: {0}", processedActions);
            
            // Terminate cBot immediately to act as a one-shot script execution
            Stop(); 
        }

        // --- Helper Methods ---

        private bool MatchFilters(string symbolName, string label)
        {
            if (CloseOnlyCurrentSymbol && symbolName != SymbolName)
                return false;

            if (!string.IsNullOrEmpty(FilterLabel) && label != FilterLabel)
                return false;

            return true;
        }

        private bool ExecutePositionClose(Position pos, double volume, Symbol sym)
        {
            int retries = 0;
            
            while (retries < MaxRetries)
            {
                TradeResult result = ClosePosition(pos, volume);

                if (result.IsSuccessful)
                {
                    Print("SUCCESS: Closed {0} units on Position ID {1} ({2})", volume, pos.Id, sym.Name);
                    return true;
                }
                
                // Intercept cTrader Error Codes
                switch (result.Error)
                {
                    case ErrorCode.Disconnected:
                    case ErrorCode.TechnicalError:
                    case ErrorCode.InternalError:
                    case ErrorCode.Timeout:
                        Print("RETRYABLE ERROR: {0} on Position {1}. Code: {2}. Retrying ({3}/{4})...",
                            sym.Name, pos.Id, result.Error, retries + 1, MaxRetries);
                        Thread.Sleep(80); 
                        retries++;
                        break;
                    default:
                        Print("FATAL ERROR: {0} on Position {1}. Code: {2}. Aborting execution.",
                            sym.Name, pos.Id, result.Error);
                        return false;
                }
            }
            return false;
        }

        private bool ExecuteOrderCancel(PendingOrder order)
        {
            int retries = 0;
            
            while (retries < MaxRetries)
            {
                TradeResult result = CancelPendingOrder(order);

                if (result.IsSuccessful)
                {
                    Print("SUCCESS: Deleted pending order ID {0} ({1})", order.Id, order.SymbolName);
                    return true;
                }
                
                switch (result.Error)
                {
                    case ErrorCode.Disconnected:
                    case ErrorCode.TechnicalError:
                    case ErrorCode.InternalError:
                    case ErrorCode.Timeout:
                        Thread.Sleep(80);
                        retries++;
                        break;
                    default:
                        Print("FATAL: Failed to delete order {0}. Code: {1}. Skipping.", order.Id, result.Error);
                        return false;
                }
            }
            return false;
        }
    }
}
cTrader Deployment Tip

To use this with split-second precision, you don't need to manually attach it to a chart every time. In cTrader, you can map any installed cBot to a custom hotkey.

1.) Go to Settings (bottom left gear icon).

2.) Select Hotkeys.

3.) Scroll down to the Automate section, find News_Panic_Close_Enterprise, and bind it to a dedicated keystroke.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply