HOW QuickHedgeOrchestrator WORKS - Detailed AnalysisΒΆ
π― Document PurposeΒΆ
Show WHAT the orchestrator consists of and HOW EXACTLY it works at the code, methods and data level. Special attention is paid to the hedge trigger logic and the mathematics of loss locking.
π¦ What the orchestrator is made ofΒΆ
1. Class structure (lines 13-27)ΒΆ
public class QuickHedgeOrchestrator
{
// β SINGLE DEPENDENCY
// ββββββββββββββββββββββββββββββββββββββββββ
private readonly MT5Service _service;
// β 6 CONFIGURABLE PARAMETERS
// ββββββββββββββββββββββββββββββββββββββββββ
public string Symbol { get; set; } = "EURUSD";
public double RiskAmount { get; set; } = 30.0;
public int StopLossPoints { get; set; } = 25;
public int TakeProfitPoints { get; set; } = 40;
public bool OpenBuyFirst { get; set; } = true;
public int HedgeTriggerPoints { get; set; } = 15; // β KEY parameter!
public QuickHedgeOrchestrator(MT5Service service)
{
_service = service;
}
}
Dependency visualizationΒΆ
β QuickHedgeOrchestrator
β
β β private readonly MT5Service _service
β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββ
β
βΌ
β MT5Service
β
β β private MT5Account _account
β ββββββββββββββββ¬ββββββββββββββββ
βββββββββββββββββββΌββββββββββββββββββββ
β
βΌ
β MT5Account
β
β β gRPC Client
β ββββββββββββββββββββ
ββββββββββββββββββββββββββ
β
βΌ
[MT5 Terminal]
π How ExecuteAsync() works - step by stepΒΆ
Phase 1: Initialization (lines 35-40)ΒΆ
public async Task<double> ExecuteAsync(CancellationToken ct = default)
{
var initialBalance = await _service.GetBalanceAsync();
Console.WriteLine($" Starting balance: ${initialBalance:F2}");
Console.WriteLine($" Symbol: {Symbol}");
Console.WriteLine($" Initial direction: {(OpenBuyFirst ? "BUY" : "SELL")}");
Console.WriteLine($" Risk: ${RiskAmount:F2}");
Console.WriteLine($" Hedge trigger: {HedgeTriggerPoints} pts adverse");
}
Phase 2: Opening primary position (lines 44-77)ΒΆ
Console.WriteLine($" Opening initial {(OpenBuyFirst ? "BUY" : "SELL")} position...");
OrderSendData initialOrder;
if (OpenBuyFirst)
{
// β PRIMARY POSITION with risk sizing
// β Has SL and TP for protection
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
initialOrder = await _service.BuyMarketByRisk(
symbol: Symbol, // "EURUSD"
stopPoints: StopLossPoints, // 25 points
riskMoney: RiskAmount, // $30
tpPoints: TakeProfitPoints, // 40 points
comment: "Hedge-Primary"
);
}
else
{
initialOrder = await _service.SellMarketByRisk(
symbol: Symbol,
stopPoints: StopLossPoints,
riskMoney: RiskAmount,
tpPoints: TakeProfitPoints,
comment: "Hedge-Primary"
);
}
if (initialOrder.ReturnedCode != 10009)
{
Console.WriteLine($" β Initial order failed: {initialOrder.Comment}");
return 0;
}
Console.WriteLine($" β Initial position: #{initialOrder.Order}");
Console.WriteLine($" Entry price: {initialOrder.Price:F5}");
Console.WriteLine($" Volume: {initialOrder.Volume:F2} lots");
// β SAVE CRITICALLY IMPORTANT DATA
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
var entryPrice = initialOrder.Price; // For movement calculation
var point = await _service.GetPointAsync(Symbol); // Point size
Critically important:
- Uses BuyMarketByRisk / SellMarketByRisk with SL/TP
- Saves
entryPricefor monitoring movement - Saves
initialOrder.Volumefor hedging the same volume
Phase 3: Monitoring for hedge trigger (lines 82-133)ΒΆ
This is the most important part of the orchestrator - the logic for determining adverse movement and placing the hedge.
Console.WriteLine($" Monitoring price for hedge trigger...");
bool hedgePlaced = false;
ulong? hedgeTicket = null;
var monitorStart = DateTime.UtcNow;
var maxMonitorTime = TimeSpan.FromMinutes(5);
// β MONITORING LOOP (every 2 sec, max 5 minutes)
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
while (DateTime.UtcNow - monitorStart < maxMonitorTime && !ct.IsCancellationRequested)
{
await Task.Delay(2000, ct); // Every 2 seconds
// β STEP 1: Get current price
// ββββββββββββββββββββββββββββββββββββββββββββββββββ
var tick = await _service.SymbolInfoTickAsync(Symbol);
// For BUY position watch Bid (closing price)
// For SELL position watch Ask (closing price)
var currentPrice = OpenBuyFirst ? tick.Bid : tick.Ask;
// β STEP 2: Calculate movement in points
// ββββββββββββββββββββββββββββββββββββββββββββββββββ
var priceMovementPoints = Math.Abs((currentPrice - entryPrice) / point);
// β STEP 3: Check movement direction
// ββββββββββββββββββββββββββββββββββββββββββββββββββ
// For BUY: adverse = price BELOW entry (currentPrice < entryPrice)
// For SELL: adverse = price ABOVE entry (currentPrice > entryPrice)
var isAdverse = OpenBuyFirst ? (currentPrice < entryPrice) : (currentPrice > entryPrice);
// β STEP 4: KEY CHECK - Hedge trigger?
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if (isAdverse && priceMovementPoints >= HedgeTriggerPoints)
{
Console.WriteLine($"\n β οΈ Price moved {priceMovementPoints:F1} pts against us!");
Console.WriteLine($" Opening hedge {(OpenBuyFirst ? "SELL" : "BUY")} position...");
// β OPENING HEDGE POSITION
// β IMPORTANT: WITHOUT SL/TP, same Volume!
// ββββββββββββββββββββββββββββββββββββββββββββββ
OrderSendData hedgeOrder;
if (OpenBuyFirst)
{
// Primary was BUY β hedge SELL
hedgeOrder = await _service.SellMarketAsync(
symbol: Symbol,
volume: initialOrder.Volume, // β SAME VOLUME!
comment: "Hedge-Protection"
// WITHOUT sl and tp parameters!
);
}
else
{
// Primary was SELL β hedge BUY
hedgeOrder = await _service.BuyMarketAsync(
symbol: Symbol,
volume: initialOrder.Volume, // β SAME VOLUME!
comment: "Hedge-Protection"
);
}
if (hedgeOrder.ReturnedCode == 10009)
{
hedgeTicket = hedgeOrder.Order;
hedgePlaced = true;
Console.WriteLine($" β Hedge placed: #{hedgeOrder.Order}\n");
break; // Exit monitoring
}
else
{
Console.WriteLine($" β Hedge failed: {hedgeOrder.Comment}\n");
}
}
}
if (!hedgePlaced)
{
Console.WriteLine(" β No hedge needed - price moved favorably\n");
}
Key logic:
-
Movement calculation:
-
Hedge trigger:
-
Hedge without SL/TP:
-
Hedge position does NOT have SL and TP
- Hedge purpose is to freeze the loss, not to make profit
- Same Volume as primary position β full hedging
Phase 4: Holding and closing (lines 140-146)ΒΆ
// β Hold positions for 30 seconds
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Console.WriteLine(" β³ Holding positions for 30 seconds...\n");
await Task.Delay(30000, ct);
// β Close ALL positions simultaneously
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Console.WriteLine(" Closing all positions...");
await _service.CloseAll(Symbol);
Console.WriteLine(" β All closed");
What happens:
- If hedge placed β closes BOTH positions (primary + hedge)
- If hedge NOT placed β closes only primary position
SummaryΒΆ
QuickHedgeOrchestrator is made of:
- 1 dependency:
MT5Service _service - 6 parameters: Symbol, RiskAmount, StopLossPoints, TakeProfitPoints, OpenBuyFirst, HedgeTriggerPoints
-
Key logic:
-
Primary position: with SL/TP, risk sizing
- Hedge: SAME Volume, WITHOUT SL/TP
- Monitoring every 2 seconds
- Trigger:
isAdverse && priceMovementPoints >= HedgeTriggerPoints
Works through:
- Opening primary position with BuyMarketByRisk
- Monitoring adverse movement (SymbolInfoTickAsync)
- Calculating movement in points (GetPointAsync)
- Placing opposite position on trigger
- Closing both positions simultaneously (CloseAll)
Returns:
double profit- difference between final and initial balance
Key insight:
Hedge is NOT designed to make profit. Its purpose is to FREEZE the loss at the HedgeTriggerPoints level. After placing the hedge, the total P/L is frozen regardless of further price movement!
Mathematical guarantee: