Page 2 of 2
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:56 pm
by PTScalper
Because MetaTrader 4 operates on a strictly synchronous, single-threaded execution model, true asynchronous execution (like the cTrader ClosePositionAsync or MT5's async API) is physically impossible in native MQL4. MT4 processes trade requests in a single queue; if you send a second request while the first is processing, you get Error 146 (Trade Context Busy).
To make an MT4 script "Pro" during a flash crash, the architecture must pivot from concurrency to Trade Context Management, Quote Freshness, and Aggressive Retry Loops. During high volatility, spreads widen and prices skip, causing brokers to throw Error 138 (Requote) or Error 136 (Off Quotes).
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:57 pm
by PTScalper
Here is the enterprise-grade MQL4 implementation designed to punch through requotes and busy signals:
Code: Select all
//+------------------------------------------------------------------+
//| EmergencyFlatPro_MT4.mq4 |
//+------------------------------------------------------------------+
#property copyright "Generated Script"
#property version "1.00"
#property description "Pro Emergency Flat for MT4 - Manages Trade Context & Requotes"
#property script_show_inputs
input string TargetAsset = "CHF"; // Asset to target (case-sensitive, usually UPPERCASE)
input int MaxRetries = 5; // Number of attempts if broker rejects
input int MaxSlippagePoints = 50; // Max slippage in points (e.g., 50 = 5 pips)
input bool PlaySoundAlert = true; // Play alert.wav on completion
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
if(TargetAsset == "")
{
Print("â Target Asset cannot be empty. Exiting.");
return;
}
int closedCount = 0;
int deletedCount = 0;
int total = OrdersTotal();
if(total == 0)
{
Print("âšī¸ INFO: No orders to process. Standing by.");
return;
}
Print("đ¨ EMERGENCY PROTOCOL INITIATED: Flattening ", TargetAsset, " exposure...");
// Loop backwards: critical because closing an order shifts the pool index
for(int i = total - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
continue;
string symbol = OrderSymbol();
// Check if the target asset is in the symbol string (e.g., EURCHF, USDCHF.m)
if(StringFind(symbol, TargetAsset) == -1)
continue;
int type = OrderType();
int ticket = OrderTicket();
double lots = OrderLots();
bool success = false;
int attempts = 0;
// Retry loop to handle requotes, slippage, and busy contexts
while(!success && attempts < MaxRetries)
{
// 1. Thread Safety: Wait if the terminal trade thread is locked by an EA
while(IsTradeContextBusy())
{
Sleep(50);
}
// 2. Quote Freshness: Refresh rates for the terminal, but specifically
// pull the exact MarketInfo Bid/Ask for the target symbol.
RefreshRates();
if(type == OP_BUY || type == OP_SELL)
{
double closePrice = 0;
if(type == OP_BUY) closePrice = MarketInfo(symbol, MODE_BID);
if(type == OP_SELL) closePrice = MarketInfo(symbol, MODE_ASK);
// Normalize price to prevent Error 130 (Invalid Price)
int digits = (int)MarketInfo(symbol, MODE_DIGITS);
closePrice = NormalizeDouble(closePrice, digits);
success = OrderClose(ticket, lots, closePrice, MaxSlippagePoints, clrRed);
if(success)
{
Print("â
SUCCESS: Closed Position ", symbol, " | Ticket: ", ticket);
closedCount++;
}
else
{
int err = GetLastError();
Print("â ī¸ FAILED to close ", symbol, " | Ticket: ", ticket, " | Error: ", err, " | Attempt: ", attempts + 1);
Sleep(200); // Pause before hammering the broker again
}
}
// Pending Orders
else if(type == OP_BUYLIMIT || type == OP_SELLLIMIT || type == OP_BUYSTOP || type == OP_SELLSTOP)
{
success = OrderDelete(ticket, clrRed);
if(success)
{
Print("â
SUCCESS: Canceled Order ", symbol, " | Ticket: ", ticket);
deletedCount++;
}
else
{
int err = GetLastError();
Print("â ī¸ FAILED to cancel ", symbol, " | Ticket: ", ticket, " | Error: ", err, " | Attempt: ", attempts + 1);
Sleep(200);
}
}
attempts++;
}
if(!success)
{
Print("â FATAL: Broker refused to resolve ticket ", ticket, " after ", MaxRetries, " attempts.");
}
}
Print("đ EMERGENCY PROTOCOL COMPLETE. Positions closed: ", closedCount, " | Orders deleted: ", deletedCount);
if(PlaySoundAlert)
{
PlaySound("alert.wav");
}
}
//+------------------------------------------------------------------+
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:57 pm
by PTScalper
MT4 Specific Safeguards Used:
IsTradeContextBusy(): Prevents Error 146. If another EA on your platform is mid-trade, the script pauses dynamically rather than failing outright.
MarketInfo(symbol, MODE_BID): When you execute a script on a specific chart (e.g., EURUSD), the standard Bid and Ask variables only reflect EURUSD. If the script is trying to close USDCHF, it must use MarketInfo() to request the specific bid/ask of the target symbol from the broker's data feed, averting Error 129 (Invalid Price).
NormalizeDouble(): MT4 is notorious for throwing Error 130 if you pass an unrounded double precision number (e.g., 0.88456000000001). This clamps the request strictly to the broker's accepted digit length.