Page 1 of 2
Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 5:38 pm
by FXS Prop Desk
The Swiss franc combines safe-haven status with a central bank that has surprised markets several times. That mix makes CHF pairs behave differently from other majors.
Key points:
- SNB monetary policy assessments are quarterly, so each one carries more weight than a monthly meeting would.
- The SNB has used FX intervention as a policy tool and reports its interventions with a delay.
- EURCHF often reacts to European risk sentiment more than to Swiss data.
- The historic removal of the EURCHF floor in 2015 shows how far CHF can move in minutes.
Practical considerations:
- Keep CHF exposure small around SNB decisions.
- Check whether your broker has negative balance protection and how it handled past shocks.
- Watch for comments from SNB officials on the franc's valuation.
Do you trade CHF pairs, and how do you manage the risk of an SNB surprise?
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:49 pm
by PTScalper
FXS Prop Desk wrote: Wed Oct 07, 2026 5:38 pm
The Swiss franc combines safe-haven status with a central bank that has surprised markets several times. That mix makes CHF pairs behave differently from other majors.
Key points:
- SNB monetary policy assessments are quarterly, so each one carries more weight than a monthly meeting would.
- The SNB has used FX intervention as a policy tool and reports its interventions with a delay.
- EURCHF often reacts to European risk sentiment more than to Swiss data.
- The historic removal of the EURCHF floor in 2015 shows how far CHF can move in minutes.
Practical considerations:
- Keep CHF exposure small around SNB decisions.
- Check whether your broker has negative balance protection and how it handled past shocks.
- Watch for comments from SNB officials on the franc's valuation.
Do you trade CHF pairs, and how do you manage the risk of an SNB surprise?
Hi FXS Prop,
i remember this situation on the market.
SNB setup 1.2000 peg and protect it i think 3-4 times.
Last time it fall down very very ugly and it was falling knife.
Thats why i prefer to use broker like Ic Markets, because im protected with negative account protection for such situations.
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:52 pm
by PTScalper
Assigning this script to a custom hotkey in MetaTrader 5 allows for instantaneous execution if unexpected SNB headlines cross the wire or spreads abruptly widen in the order book.
To structurally manage this risk:
Pre-Event Exposure Flattening: The most reliable defense is holding zero exposure. Because SNB monetary policy assessments are quarterly events scheduled on specific Thursdays (such as September 24 or December 10, 2026), any automated or manual strategy should flag these dates and flatten all CHF positions well before the 09:30 CEST announcements.
Gap-Adjusted Position Sizing: Risk should be calculated based on gap distance rather than the physical stop-loss distance. If a setup requires a 15-pip stop, calculating the lot size as if the market could gap 150 pips against the position ensures that a black swan event only results in a routine portfolio drawdown.
Negative Balance Protection Assessment: A severe liquidity gap can instantly push an account into a negative balance. Trading CHF pairs around intervention zones should strictly be done with brokers that guarantee negative balance protection, legally shifting the catastrophic gap liability away from the trader and onto the broker.
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:52 pm
by PTScalper
MQL5 version 1.00
Code: Select all
//+------------------------------------------------------------------+
//| Emergency_CHF_Flat.mq5 |
//+------------------------------------------------------------------+
#property copyright "Generated Script"
#property version "1.00"
#property description "Panic script to close all CHF positions and delete pending CHF orders"
#property script_show_inputs
#include <Trade\Trade.mqh>
input int MaxSlippage = 50; // Maximum allowed slippage in points
void OnStart()
{
CTrade trade;
trade.SetDeviationInPoints(MaxSlippage);
int closedCount = 0;
int deletedCount = 0;
// 1. Close all open positions containing "CHF"
// We loop backward so closing a position doesn't shift the remaining indices
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
string symbol = PositionGetString(POSITION_SYMBOL);
if(StringFind(symbol, "CHF") >= 0)
{
if(trade.PositionClose(ticket))
{
Print("Closed CHF Position: ", symbol, " | Ticket: ", ticket);
closedCount++;
}
else
{
Print("Failed to close position: ", symbol, " | Error: ", GetLastError());
}
}
}
}
// 2. Delete all pending orders containing "CHF"
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0)
{
string symbol = OrderGetString(ORDER_SYMBOL);
if(StringFind(symbol, "CHF") >= 0)
{
if(trade.OrderDelete(ticket))
{
Print("Deleted CHF Pending Order: ", symbol, " | Ticket: ", ticket);
deletedCount++;
}
else
{
Print("Failed to delete order: ", symbol, " | Error: ", GetLastError());
}
}
}
}
Print("Emergency flat complete. Positions closed: ", closedCount, " | Orders deleted: ", deletedCount);
}
//+------------------------------------------------------------------+
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:53 pm
by PTScalper
Here is the C# equivalent for cTrader using the cAlgo API.
Unlike MetaTrader 5, cTrader does not have a dedicated "Scripts" category. Instead, you build this as a cBot. By placing the Stop() command at the very end of the OnStart() method, the cBot will execute its logic instantly and then turn itself off, perfectly mimicking the behavior of an MT5 script.
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class EmergencyCHFFlat : Robot
{
protected override void OnStart()
{
int closedCount = 0;
int canceledCount = 0;
Print("Executing Emergency CHF Flat...");
// 1. Find and close all open positions containing "CHF"
// We use .ToUpper() to account for broker suffixes (e.g., eurchf)
// .ToArray() creates a snapshot so we don't get collection modification errors during iteration
var chfPositions = Positions.Where(p => p.SymbolName.ToUpper().Contains("CHF")).ToArray();
foreach (var position in chfPositions)
{
TradeResult result = ClosePosition(position);
if (result.IsSuccessful)
{
Print("Closed CHF Position: {0} | ID: {1}", position.SymbolName, position.Id);
closedCount++;
}
else
{
Print("Failed to close position: {0} | Error: {1}", position.SymbolName, result.Error);
}
}
// 2. Find and cancel all pending orders containing "CHF"
var chfOrders = PendingOrders.Where(o => o.SymbolName.ToUpper().Contains("CHF")).ToArray();
foreach (var order in chfOrders)
{
TradeResult result = CancelPendingOrder(order);
if (result.IsSuccessful)
{
Print("Canceled CHF Pending Order: {0} | ID: {1}", order.SymbolName, order.Id);
canceledCount++;
}
else
{
Print("Failed to cancel order: {0} | Error: {1}", order.SymbolName, result.Error);
}
}
Print("Emergency flat complete. Positions closed: {0} | Orders canceled: {1}", closedCount, canceledCount);
// Stop the cBot immediately so it acts exactly like a one-time execution script
Stop();
}
}
}
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:53 pm
by PTScalper
Key Differences from the MT5 Version:
LINQ Snapshots: Instead of looping backward through an index like in MQL5, C# allows us to use LINQ Where(...).ToArray() to filter the collections directly. This takes a static snapshot of the collections so deleting items won't break the loop logic.
Slippage Parameter: In cTrader's API, the ClosePosition() method simply submits a market order to close the trade using the available VWAP (Volume Weighted Average Price) liquidity pool. As a result, the MaxSlippage input was removed, as cTrader will automatically execute the closure at the best available market prices.
Execution: To use this in an emergency, build the cBot and attach it to any active chart. The moment you press the Start button on the cBot instance, it will sweep the account, close the CHF exposures, print the audit to the log, and shut down.
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:54 pm
by PTScalper
To elevate this from a basic script to an enterprise-grade emergency tool, we need to address the reality of a flash crash: liquidity vacuums cause rejections, and sequential execution is too slow.
If you are closing 5 CHF positions in a fast-moving market, waiting for the first round-trip to the server before sending the second request can cost you dozens of pips.
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:55 pm
by PTScalper
This "Pro" version introduces three major architectural upgrades suited for your C# background:
Asynchronous Concurrency: Uses ClosePositionAsync and CancelPendingOrderAsync to blast all requests to the broker simultaneously without blocking the main thread.
Recursive Retry Pattern: If the broker rejects the closure (common during extreme slippage or "No Liquidity" events), the callback automatically retries the operation up to a defined limit.
Thread-Safe State Management: Uses Interlocked.Decrement to track pending asynchronous operations and only shuts down the cBot when all callbacks have resolved.
Parameterization: Abstracts "CHF" into a parameter so you can reuse the tool for NFP (USD) or BoE (GBP) surprises.
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:55 pm
by PTScalper
The Pro Implementation
Code: Select all
using System;
using System.Linq;
using System.Threading;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class EmergencyFlatPro : Robot
{
[Parameter("Target Asset (e.g. CHF, USD)", DefaultValue = "CHF", Group = "Filter")]
public string TargetAsset { get; set; }
[Parameter("Max Retries on Reject", DefaultValue = 3, MinValue = 0, Group = "Execution")]
public int MaxRetries { get; set; }
[Parameter("Play Sound on Completion", DefaultValue = true, Group = "Notifications")]
public bool PlaySoundAlert { get; set; }
// Tracks active async operations to prevent premature cBot termination
private int _pendingOperations;
protected override void OnStart()
{
if (string.IsNullOrWhiteSpace(TargetAsset))
{
Print("â ERROR: Target asset cannot be empty.");
Stop();
return;
}
string filter = TargetAsset.ToUpper();
// Take immediate snapshots of the collections
var targetPositions = Positions.Where(p => p.SymbolName.ToUpper().Contains(filter)).ToArray();
var targetOrders = PendingOrders.Where(o => o.SymbolName.ToUpper().Contains(filter)).ToArray();
if (targetPositions.Length == 0 && targetOrders.Length == 0)
{
Print("âšī¸ INFO: No open positions or pending orders found for {0}. Standing by.", filter);
Stop();
return;
}
Print("đ¨ EMERGENCY PROTOCOL INITIATED: Flattening {0} exposure concurrently...", filter);
_pendingOperations = targetPositions.Length + targetOrders.Length;
// Fire all requests simultaneously. Do NOT await them.
foreach (var position in targetPositions)
{
AttemptClosePosition(position, 0);
}
foreach (var order in targetOrders)
{
AttemptCancelOrder(order, 0);
}
}
private void AttemptClosePosition(Position position, int attemptCount)
{
ClosePositionAsync(position, result =>
{
if (result.IsSuccessful)
{
Print("â
SUCCESS: Closed Position {0} (ID: {1})", position.SymbolName, position.Id);
CheckCompletion();
}
else
{
if (attemptCount < MaxRetries)
{
Print("â ī¸ WARNING: FAILED to close {0} (ID: {1}) [{2}]. Retrying {3}/{4}...",
position.SymbolName, position.Id, result.Error, attemptCount + 1, MaxRetries);
AttemptClosePosition(position, attemptCount + 1);
}
else
{
Print("â FATAL: Failed to close {0} (ID: {1}) after {2} attempts. Error: {3}",
position.SymbolName, position.Id, MaxRetries, result.Error);
CheckCompletion();
}
}
});
}
private void AttemptCancelOrder(PendingOrder order, int attemptCount)
{
CancelPendingOrderAsync(order, result =>
{
if (result.IsSuccessful)
{
Print("â
SUCCESS: Canceled Order {0} (ID: {1})", order.SymbolName, order.Id);
CheckCompletion();
}
else
{
if (attemptCount < MaxRetries)
{
Print("â ī¸ WARNING: FAILED to cancel order {0} (ID: {1}) [{2}]. Retrying {3}/{4}...",
order.SymbolName, order.Id, result.Error, attemptCount + 1, MaxRetries);
AttemptCancelOrder(order, attemptCount + 1);
}
else
{
Print("â FATAL: Failed to cancel order {0} (ID: {1}) after {2} attempts. Error: {3}",
order.SymbolName, order.Id, MaxRetries, result.Error);
CheckCompletion();
}
}
});
}
private void CheckCompletion()
{
// Thread-safe decrement just in case the broker implementation
// fires callbacks from a different thread pool worker
int remaining = Interlocked.Decrement(ref _pendingOperations);
if (remaining <= 0)
{
Print("đ EMERGENCY PROTOCOL COMPLETE. All requested operations have resolved.");
if (PlaySoundAlert)
{
// Triggers the standard platform alert sound
Notifications.PlaySound(SoundType.Alert);
}
// Only turn off the cBot once all callbacks are finished
Stop();
}
}
}
}
Re: Swiss Franc Moves and SNB Policy
Posted: Wed Oct 07, 2026 6:55 pm
by PTScalper
Why this architecture matters for manual scalpers:
When trading raw price action on the 15-minute chart, your setups rely on localized liquidity. A sudden SNB intervention immediately pulls market makers out of the order book.
If you use a standard foreach loop with a synchronous ClosePosition(), cTrader waits up to 250ms+ (depending on ping and broker processing) per order. Closing 4 orders takes a full second.
By using ClosePositionAsync, the C# script pushes all FIX API commands to the broker in less than a millisecond, shifting the execution bottleneck from your local machine to the broker's matching engine, ensuring you capture the absolute best available VWAP before the book fully empties.