NewsStraddleOrchestrator - News Straddle¶
Description¶
NewsStraddleOrchestrator is an orchestrator for news trading using the straddle method. Before important news release, it places two pending Stop orders: Buy Stop above and Sell Stop below the current price. When the news comes out and the price moves sharply in either direction, the corresponding order is triggered and the opposite is cancelled.
Operating Principle: "News Trading" - catch volatility from news without predicting direction. Straddle allows catching the movement regardless of whether the price goes up or down after the news (NFP, CPI, central bank decisions, etc.).
File: Examples\Orchestrators\NewsStraddleOrchestrator.cs
Architecture¶
NEWS STRADDLE ORCHESTRATOR
↓
MT5Service Instance
↓
┌─────┼─────┐
↓ ↓ ↓
BuyStop SellStop CloseBy
Points Points Ticket
(above) (below) (cancel)
Dependencies¶
- MT5Service: Service layer for MT5
- MT5Sugar Extension Methods:
BuyStopPoints,SellStopPoints,CloseByTicket,CloseAll - mt5_term_api: gRPC types (
OrderSendData,OpenedOrdersTicketsData)
Configuration Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
Symbol |
string | "EURUSD" |
Trading instrument |
StraddleDistancePoints |
int | 15 |
Distance of orders from current price (in points) |
Volume |
double | 0.02 |
Volume of each order in lots |
StopLossPoints |
int | 20 |
Stop-loss in points |
TakeProfitPoints |
int | 40 |
Take-profit in points |
SecondsBeforeNews |
int | 60 |
Waiting time before news (sec) |
MaxWaitAfterNewsSeconds |
int | 180 |
Maximum waiting time for breakout after placing orders |
Configuration Example¶
var newsStraddle = new NewsStraddleOrchestrator(service)
{
Symbol = "EURUSD",
StraddleDistancePoints = 20, // Orders at distance of 20 points
Volume = 0.01, // Conservative volume
StopLossPoints = 25, // SL = 25 points
TakeProfitPoints = 60, // TP = 60 points (R:R = 1:2.4)
SecondsBeforeNews = 120, // Wait 2 minutes before news
MaxWaitAfterNewsSeconds = 300 // Wait for breakout 5 minutes
};
How to Run¶
You can run this orchestrator using any of the following commands:
Algorithm¶
Flowchart¶
START
↓
Get initial balance
↓
Task.Delay(SecondsBeforeNews × 1000) // Countdown
↓
"NEWS EVENT IMMINENT!"
↓
SymbolInfoTickAsync() → current price
↓
BuyStopPoints(+StraddleDistancePoints)
→ If error → return 0
↓
SellStopPoints(-StraddleDistancePoints)
→ If error → cancel BuyStop, return 0
↓
"STRADDLE ACTIVE"
↓
MONITORING (every second, max MaxWaitAfterNewsSeconds)
→ OpenedOrdersTicketsAsync()
→ Check: BuyStop still pending?
→ Check: SellStop still pending?
→ CONDITIONS:
If BuyStop executed, SellStop pending
→ UPWARD breakout → cancel SellStop
If SellStop executed, BuyStop pending
→ DOWNWARD breakout → cancel BuyStop
If both executed
→ BOTH (extreme volatility!)
Timeout → cancel both
↓
Task.Delay(60000) // Hold position 60 sec
↓
CloseAll(Symbol)
↓
Return profit
END
Step-by-Step Description¶
1. Initialization and countdown (lines 36-47)¶
var initialBalance = await _service.GetBalanceAsync();
Console.WriteLine($" Starting balance: ${initialBalance:F2}");
Console.WriteLine($" Symbol: {Symbol}");
Console.WriteLine($" Straddle distance: {StraddleDistancePoints} pts");
// │ COUNTDOWN TO NEWS
// │ Simulating waiting before event
// └─────────────────────────────────────────────────────────
Console.WriteLine($" ⏲ Waiting {SecondsBeforeNews}s before news event...\n");
await Task.Delay(SecondsBeforeNews * 1000, ct);
What happens:
- Waiting before news (default 60 seconds)
- In real trading: run orchestrator 1-2 minutes before exact news time
- Example: NFP releases at 13:30 UTC → run orchestrator at 13:29:00
2. Getting current price before news (lines 49-51)¶
var tick = await _service.SymbolInfoTickAsync(Symbol);
Console.WriteLine($" 📰 NEWS EVENT IMMINENT!");
Console.WriteLine($" Current: Bid={tick.Bid:F5}, Ask={tick.Ask:F5}\n");
Critically important:
- Price is obtained IMMEDIATELY before placing orders
- Ensures straddle symmetry relative to current price
3. Placing Buy Stop (upper straddle order) (lines 53-70)¶
Console.WriteLine(" Placing BUY STOP (upper straddle)...");
var buyStopResult = await _service.BuyStopPoints(
symbol: Symbol, // "EURUSD"
volume: Volume, // 0.02
priceOffsetPoints: StraddleDistancePoints, // +15 (POSITIVE!)
slPoints: StopLossPoints, // 20
tpPoints: TakeProfitPoints, // 40
comment: "News-Buy"
);
if (buyStopResult.ReturnedCode != 10009)
{
Console.WriteLine($" ✗ BUY STOP failed: {buyStopResult.Comment}\n");
return 0;
}
Console.WriteLine($" ✓ BUY STOP: #{buyStopResult.Order}\n");
Calculation example:
Current Ask price: 1.10000
StraddleDistancePoints: 15
point: 0.00001
BuyStop price = 1.10000 + (15 × 0.00001) = 1.10015
When news comes out and price shoots UP to 1.10015 → order executes
4. Placing Sell Stop (lower straddle order) (lines 72-91)¶
Console.WriteLine(" Placing SELL STOP (lower straddle)...");
var sellStopResult = await _service.SellStopPoints(
symbol: Symbol,
volume: Volume,
priceOffsetPoints: -StraddleDistancePoints, // -15 (NEGATIVE!)
slPoints: StopLossPoints,
tpPoints: TakeProfitPoints,
comment: "News-Sell"
);
if (sellStopResult.ReturnedCode != 10009)
{
Console.WriteLine($" ✗ SELL STOP failed: {sellStopResult.Comment}");
Console.WriteLine(" Canceling BUY STOP...");
// │ IMPORTANT: Cancel first order!
// └─────────────────────────────────────────────────
await _service.CloseByTicket(buyStopResult.Order);
return 0;
}
Console.WriteLine($" ✓ SELL STOP: #{sellStopResult.Order}\n");
Console.WriteLine(" ✅ STRADDLE ACTIVE - Waiting for news spike!\n");
Calculation example:
Current Bid price: 1.10000
StraddleDistancePoints: 15 (but use -15)
point: 0.00001
SellStop price = 1.10000 + (-15 × 0.00001) = 1.09985
When news comes out and price shoots DOWN to 1.09985 → order executes
Result:
│ STRADDLE PLACED:
│
│ 1.10015 BUY STOP (upward breakout)
│ SL: 1.09995, TP: 1.10055
│
│ 1.10000 CURRENT PRICE
│
│ 1.09985 SELL STOP (downward breakout)
│ SL: 1.10005, TP: 1.09945
└─────────────────────────────────────────────────────
5. Monitoring for breakout after news (lines 94-136)¶
var monitorStart = DateTime.UtcNow;
var timeout = TimeSpan.FromSeconds(MaxWaitAfterNewsSeconds);
ulong? executedOrder = null;
ulong? pendingOrder = null;
string direction = "";
// │ MONITORING LOOP (every 1 sec, max 3 minutes)
// └─────────────────────────────────────────────────────────
while (DateTime.UtcNow - monitorStart < timeout && !ct.IsCancellationRequested)
{
await Task.Delay(1000, ct); // Every second (faster than PendingBreakout!)
var tickets = await _service.OpenedOrdersTicketsAsync();
bool buyStillPending = false;
bool sellStillPending = false;
foreach (var ticket in tickets.OpenedOrdersTickets)
{
if (ticket == (long)buyStopResult.Order) buyStillPending = true;
if (ticket == (long)sellStopResult.Order) sellStillPending = true;
}
// ═══════════════════════════════════════════════════════
// SCENARIO 1: UPWARD BREAKOUT
// ═══════════════════════════════════════════════════════
if (!buyStillPending && sellStillPending)
{
executedOrder = buyStopResult.Order;
pendingOrder = sellStopResult.Order;
direction = "UPWARD";
break;
}
// ═══════════════════════════════════════════════════════
// SCENARIO 2: DOWNWARD BREAKOUT
// ═══════════════════════════════════════════════════════
else if (buyStillPending && !sellStillPending)
{
executedOrder = sellStopResult.Order;
pendingOrder = buyStopResult.Order;
direction = "DOWNWARD";
break;
}
// ═══════════════════════════════════════════════════════
// SCENARIO 3: BOTH TRIGGERED (rare case!)
// ═══════════════════════════════════════════════════════
else if (!buyStillPending && !sellStillPending)
{
Console.WriteLine(" ⚡ BOTH ORDERS TRIGGERED - Extreme volatility!");
direction = "BOTH";
break;
}
// SCENARIO 4: Both still pending → continue waiting
}
Key differences from PendingBreakoutOrchestrator:
- Check every second (instead of 3 seconds) → news requires fast reaction
- Additional scenario "BOTH" for extreme volatility
- Short timeout (3 minutes) → news acts quickly
6. Breakout handling (lines 138-161)¶
// ═══════════════════════════════════════════════════════
// SCENARIO A: ONE ORDER TRIGGERED (normal breakout)
// ═══════════════════════════════════════════════════════
if (executedOrder.HasValue && pendingOrder.HasValue)
{
Console.WriteLine($" 🚀 {direction} BREAKOUT DETECTED!");
Console.WriteLine($" Position opened: #{executedOrder.Value}");
Console.WriteLine($" Canceling opposite order #{pendingOrder.Value}...");
// Cancel opposite order
await _service.CloseByTicket(pendingOrder.Value);
Console.WriteLine(" ✓ Opposite order canceled\n");
// │ Hold position for 60 seconds
// │ SL or TP may trigger during this time
// └─────────────────────────────────────────────────
Console.WriteLine(" ⏳ Holding position for 60 seconds...");
await Task.Delay(60000, ct);
}
// ═══════════════════════════════════════════════════════
// SCENARIO B: BOTH ORDERS TRIGGERED (extreme volatility)
// ═══════════════════════════════════════════════════════
else if (direction == "BOTH")
{
// Price jerked in both directions very fast!
// TWO positions opened (BUY and SELL simultaneously)
Console.WriteLine(" ⏳ Holding both positions for 30 seconds...");
await Task.Delay(30000, ct);
}
// ═══════════════════════════════════════════════════════
// SCENARIO C: TIMEOUT (breakout didn't happen)
// ═══════════════════════════════════════════════════════
else
{
Console.WriteLine($" ⏱ No breakout after {MaxWaitAfterNewsSeconds}s");
Console.WriteLine(" Canceling both pending orders...");
// Cancel both orders
await _service.CloseByTicket(buyStopResult.Order);
await _service.CloseByTicket(sellStopResult.Order);
}
7. Final close (lines 163-176)¶
// │ Close all remaining positions
// └─────────────────────────────────────────────────────────
Console.WriteLine("\n Closing all remaining positions...");
await _service.CloseAll(Symbol);
Console.WriteLine(" ✓ All closed");
var finalBalance = await _service.GetBalanceAsync();
var profit = finalBalance - initialBalance;
Console.WriteLine($"\n Final balance: ${finalBalance:F2}");
Console.WriteLine($" Profit/Loss: ${profit:F2}");
Console.WriteLine($" Direction: {(string.IsNullOrEmpty(direction) ? "None" : direction)}");
return profit;
Strategy Visualization¶
Scenario 1: UPWARD Breakout (typical NFP scenario)¶
T=0 START
SecondsBeforeNews = 60
│
├─► Task.Delay(60000) → waiting for news
│
T=60s ├─► "NEWS EVENT IMMINENT!"
│ Current price: Bid=1.10000, Ask=1.10002
│
├─► BuyStopPoints(+15) → order @ 1.10017
├─► SellStopPoints(-15) → order @ 1.09985
│
│ STRADDLE PLACED:
│
│ │ BUY STOP @ 1.10017
│ │ CURRENT @ 1.10000
│ │ SELL STOP @ 1.09985
│ └──────────────────────────────────
│
T=61s ├─► NEWS RELEASED! (NFP better than forecast)
│ Price shoots UP:
│ 1.10000 → 1.10010 → 1.10018...
│
T=62s ├─► BUY STOP TRIGGERED @ 1.10017!
│ Opened BUY position 0.02 lots
│ SL: 1.09997, TP: 1.10057
│
├─► Monitoring detected:
│ buyStillPending = false (disappeared from list)
│ sellStillPending = true
│
│ → "UPWARD BREAKOUT DETECTED!"
│ → Cancel SELL STOP @ 1.09985
│
│ STATE:
│
│ │ Position: BUY 0.02 @ 1.10017
│ │ SL: 1.09997, TP: 1.10057
│ └──────────────────────────────────
│
│ Price continues to rise:
│ 1.10018 → 1.10035 → 1.10050 → 1.10057...
│
T=90s ├─► TP HIT @ 1.10057!
│ Profit: +40 pts × 0.02 lots = +$8.00
│
T=120s ├─► Task.Delay(60000) finished
│
├─► CloseAll("EURUSD")
│ └─► Position already closed (TP hit earlier)
│
└─► Result: Profit +$8.00
TOTAL: Caught movement from news WITHOUT predicting direction!
Scenario 2: DOWNWARD Breakout¶
T=60s STRADDLE PLACED:
BUY STOP @ 1.10017
SELL STOP @ 1.09985
T=61s NEWS: NFP worse than forecast
Price shoots down:
1.10000 → 1.09995 → 1.09985 → 1.09980...
T=62s SELL STOP TRIGGERED @ 1.09985!
Opened SELL position 0.02 lots
SL: 1.10005, TP: 1.09945
→ "DOWNWARD BREAKOUT DETECTED!"
→ Cancel BUY STOP @ 1.10017
Price continues to fall:
1.09980 → 1.09960 → 1.09945...
T=85s TP HIT @ 1.09945!
Profit: +40 pts × 0.02 lots = +$8.00
Result: Profit +$8.00
Scenario 3: Extreme volatility (BOTH orders triggered)¶
T=60s STRADDLE PLACED:
BUY STOP @ 1.10017
SELL STOP @ 1.09985
T=61s NEWS: Unexpected data!
Price sharply DOWN:
1.10000 → 1.09985 (SELL STOP triggered!)
T=62s Then sharply UP (correction):
1.09985 → 1.10000 → 1.10017 (BUY STOP triggered!)
│ ⚡ BOTH ORDERS TRIGGERED!
│
│ Position 1: SELL 0.02 @ 1.09985
│ Position 2: BUY 0.02 @ 1.10017
│
│ This is HEDGE (mutually neutralize)!
└──────────────────────────────────────────
T=92s Task.Delay(30000) finished
CloseAll("EURUSD")
├─► Closed SELL @ current price 1.10010
│ P/L: (1.09985 - 1.10010) × 0.02 = -$5
│
└─► Closed BUY @ current price 1.10010
P/L: (1.10010 - 1.10017) × 0.02 = -$1.40
Total: -$5 + (-$1.40) = -$6.40
Result: Loss -$6.40 (spreads and commissions on whipsaw)
Scenario 4: No breakout (weak news)¶
T=60s STRADDLE PLACED:
BUY STOP @ 1.10017
SELL STOP @ 1.09985
T=61s NEWS: Data within forecast range
Price moves in narrow range:
1.10000 → 1.10005 → 1.09998 → 1.10003...
T=180s NO ORDERS TRIGGERED!
(Price didn't reach either 1.10017 or 1.09985)
→ "No breakout after 180s"
→ Cancel BUY STOP
→ Cancel SELL STOP
Result: Profit $0.00 (orders not executed)
When to Use News Straddle¶
✅ Best News Events¶
- Non-Farm Payrolls (NFP):
- USA, first Friday of month, 13:30 UTC
- Highest volatility
-
Recommended parameters:
-
Central Bank Decisions:
- Fed (FOMC), ECB, BoE
- Huge movements on rate decisions
-
Recommended parameters:
-
CPI (Inflation Data):
- USA, mid-month, 13:30 UTC
- Strong movements, especially on surprises
-
Recommended parameters:
-
GDP:
- Quarterly data
- Medium volatility
- Recommended parameters:
❌ Bad News Events¶
-
Secondary data: Consumer Confidence, Housing Starts.
-
Central bank speeches: Unpredictable volatility timing.
-
Corporate earnings: More for stocks, not forex.
-
Geopolitical events: Unpredictable, may cause whipsaw.
Risk Management¶
Choosing StraddleDistancePoints¶
Too close (5-10 pts):
✗ False triggers from noise before news
✗ Both orders may trigger (whipsaw)
Optimal (15-25 pts):
✓ Protection from noise
✓ Catches real breakouts
✓ Rarely both orders trigger
Too far (30+ pts):
✗ Will miss weak news
✗ Late execution (less movement before TP)
Recommendation:
StraddleDistancePoints = ATR(14) × 0.3-0.5
For EURUSD with ATR=12: use 15-20 pts
R:R Calculation¶
Default:
- StraddleDistancePoints = 15
- StopLossPoints = 20
- TakeProfitPoints = 40
Risk/Reward:
- Risk: 20 pts
- Reward: 40 pts
- R:R = 1:2 ✓
Why this is good:
- Even with 40% win rate → profitable
- News often gives strong directional movements
- TP reachable within 1-3 minutes after news
Maximum Risk¶
Worst Case: Both orders triggered (whipsaw)
Position 1: SELL 0.02 @ 1.09985, SL 1.10005
Position 2: BUY 0.02 @ 1.10017, SL 1.09997
If both SL trigger:
SELL SL: -20 pts × 0.02 = -$4.00
BUY SL: -20 pts × 0.02 = -$4.00
────────────────────────────────
Maximum loss: -$8.00
Probability: ~5-10% (rarely both SL trigger simultaneously)
Recommendations¶
- Position volume:
- Conservative: 0.01 lots (risk $2-4)
- Moderate: 0.02 lots (risk $4-8)
-
Aggressive: 0.05 lots (risk $10-20)
-
Placement timing:
- Run orchestrator 1-2 minutes before exact news time
- Too early → risk of triggering from noise
-
Too late → miss start of movement
-
Calendar check:
-
Spread:
- Spread widens before news!
- EURUSD: usually 0.5-1 point, during news can be 3-5 points
- Account for this in
StraddleDistancePoints
Usage Examples¶
Example 1: NFP Trading¶
// Non-Farm Payrolls - first Friday of month, 13:30 UTC
// Run at 13:29:00
var nfpStraddle = new NewsStraddleOrchestrator(service)
{
Symbol = "EURUSD",
StraddleDistancePoints = 20, // Wide (strong volatility)
Volume = 0.01, // Conservative
StopLossPoints = 30, // Wide SL
TakeProfitPoints = 70, // Ambitious TP (R:R = 1:2.3)
SecondsBeforeNews = 60, // Wait 1 minute
MaxWaitAfterNewsSeconds = 240 // 4 minutes for breakout
};
var profit = await nfpStraddle.ExecuteAsync();
Console.WriteLine($"NFP Trade: ${profit:F2}");
Example 2: CPI Trading¶
// CPI - mid-month, 13:30 UTC
// Run at 13:29:30
var cpiStraddle = new NewsStraddleOrchestrator(service)
{
Symbol = "EURUSD",
StraddleDistancePoints = 18,
Volume = 0.02,
StopLossPoints = 25,
TakeProfitPoints = 60, // R:R = 1:2.4
SecondsBeforeNews = 30, // Short wait
MaxWaitAfterNewsSeconds = 180
};
var profit = await cpiStraddle.ExecuteAsync();
Example 3: FOMC Decision¶
// FOMC - 8 times per year, ~19:00 UTC
// Run at 18:58:00
var fomcStraddle = new NewsStraddleOrchestrator(service)
{
Symbol = "EURUSD",
StraddleDistancePoints = 25, // VERY wide
Volume = 0.01,
StopLossPoints = 40, // Protection from whipsaw
TakeProfitPoints = 100, // R:R = 1:2.5
SecondsBeforeNews = 120, // 2 minutes before
MaxWaitAfterNewsSeconds = 300 // 5 minutes
};
var profit = await fomcStraddle.ExecuteAsync();
Optimization¶
Possible Improvements¶
-
Adaptive distance based on ATR:
-
Asymmetric straddle (if there's bias):
-
Partial close on profit:
-
Cancel orders if spread too wide:
Related Orchestrators¶
-
PendingBreakoutOrchestrator: General breakout (not just news).
-
QuickHedgeOrchestrator: Protection through hedging.
-
SimpleScalpingOrchestrator: Market entry after breakout.
Console Output¶
Output example (upward breakout)¶
+============================================================+
| NEWS STRADDLE ORCHESTRATOR |
+============================================================+
Starting balance: $10000.00
Symbol: EURUSD
Straddle distance: 15 pts
Volume: 0.02 lots
SL: 20 pts | TP: 40 pts
⏲ Waiting 60s before news event...
📰 NEWS EVENT IMMINENT!
Current: Bid=1.10000, Ask=1.10002
Placing BUY STOP (upper straddle)...
✓ BUY STOP: #123456789
Placing SELL STOP (lower straddle)...
✓ SELL STOP: #123456790
✅ STRADDLE ACTIVE - Waiting for news spike!
🚀 UPWARD BREAKOUT DETECTED!
Position opened: #123456789
Canceling opposite order #123456790...
✓ Opposite order canceled
⏳ Holding position for 60 seconds...
Closing all remaining positions...
✓ All closed
Final balance: $10008.00
Profit/Loss: $8.00
Direction: UPWARD
+============================================================+
Summary¶
NewsStraddleOrchestrator is a powerful strategy for news trading:
✅ Pros:
- No need to predict direction - catch movement in any direction
- Excellent R:R ratio (1:2 and better)
- Automation of news trading
- Works on most volatile events
- OCO mechanism (cancel opposite order)
❌ Cons:
- Whipsaw risk (both orders trigger)
- Wide spreads during news
- Requires precise timing (run 1-2 min before news)
- Not all news gives strong movements
- Stress from high volatility
Recommendation: Use ONLY for HIGH IMPACT news (NFP, CPI, FOMC). Always check economic calendar. Start with small lots (0.01). Test on demo account before real trading. Avoid trading all news indiscriminately - choose 2-3 most important per month.