MQL5 Hedging EA: Hedging Account Strategy Code & Risk Guide
This educational hedging-account EA uses an MA crossover for an initial position and can request one opposite-direction leg after an adverse move. It demonstrates actual position-ticket selection, volume-step rounding, and repeated cluster-close requests. A hedge changes exposure; it does not guarantee a locked loss or break-even recovery. No reproducible performance report supports this example, and account permissions and broker execution rules still apply.
Strategy logic
Entry conditions
Compare completed price bars 2 and 1 with the corresponding completed MA values once per chart bar. An upward crossover opens a buy; a downward crossover opens a sell. If the initial position moves adversely by HedgeTriggerPoints broker points, the EA attempts one opposite-direction position on the chart or configured hedge symbol. Its requested volume is InitialLot times HedgeLotMultiple, rounded down to that symbol’s volume step. This is not correlation-, volatility-, or currency-exposure sizing. The tag must be exclusive to this instance.
Exit conditions
The cluster trigger sums floating POSITION_PROFIT and POSITION_SWAP in account currency, excluding commissions and realized outcomes. Reaching its target starts repeated close requests until no matching positions remain. Reaching the loss threshold, losing a leg externally, or failing to establish a leg also halts further entries until reinitialization. The source does not contain time expiry or early profit-based hedge removal. There are no broker-side SL/TP orders: terminal availability, chart ticks, stale cross-symbol quotes, and rejected closures can delay exit beyond the threshold. Restarting with existing tagged positions is rejected rather than silently forgetting them.
MQL5 Expert Advisor Code
// Educational two-leg hedging-account example, not a loss guarantee.
#property version "1.10"
input double InitialLot = 0.10;
input double HedgeLotMultiple = 1.50;
input int MAPeriod = 50;
input ENUM_MA_METHOD MAMethod = MODE_EMA;
input int HedgeTriggerPoints = 400;
input double TargetProfitMoney = 30.0;
input double MaxLossMoney = 150.0;
input long MagicNumber = 202401;
input string HedgeSymbol = "";
int maHandle = INVALID_HANDLE;
string hedgeSymbol;
ulong initialTicket = 0;
bool hedgeAttempted = false;
bool closing = false;
bool halted = false;
datetime lastBar = 0;
ENUM_POSITION_TYPE initialDirection = POSITION_TYPE_BUY;
bool ManagedSelected()
{
string symbol = PositionGetString(POSITION_SYMBOL);
return PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
(symbol == _Symbol || symbol == hedgeSymbol);
}
int ManagedCount()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
if(PositionGetTicket(i) > 0 && ManagedSelected()) count++;
return count;
}
int OnInit()
{
if(InitialLot <= 0 || HedgeLotMultiple <= 0 || MAPeriod <= 0 ||
HedgeTriggerPoints <= 0 || TargetProfitMoney <= 0 || MaxLossMoney <= 0 || MagicNumber <= 0)
return INIT_PARAMETERS_INCORRECT;
if(AccountInfoInteger(ACCOUNT_MARGIN_MODE) != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
return INIT_PARAMETERS_INCORRECT;
hedgeSymbol = HedgeSymbol == "" ? _Symbol : HedgeSymbol;
if(!SymbolSelect(hedgeSymbol, true)) return INIT_FAILED;
// The tag must be exclusive to this instance, including across symbols.
for(int i = PositionsTotal()-1; i >= 0; i--)
if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
return INIT_FAILED;
maHandle = iMA(_Symbol, _Period, MAPeriod, 0, MAMethod, PRICE_CLOSE);
return maHandle == INVALID_HANDLE ? INIT_FAILED : INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(maHandle != INVALID_HANDLE) IndicatorRelease(maHandle);
}
// Find an actual position ticket, never mistake a deal ID for a position ticket.
ulong OpenLeg(string symbol, ENUM_ORDER_TYPE direction, double requestedLots)
{
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double minimum = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maximum = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
if(step <= 0) return 0;
double lots = NormalizeDouble(MathFloor(MathMin(maximum, requestedLots) / step) * step, 8);
if(lots < minimum) return 0;
MqlTick tick;
if(!SymbolInfoTick(symbol, tick) || tick.ask <= 0 || tick.bid <= 0) return 0;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = symbol;
req.type = direction;
req.volume = lots;
req.price = direction == ORDER_TYPE_BUY ? tick.ask : tick.bid;
req.magic = (ulong)MagicNumber;
req.deviation = 10;
req.type_filling = ORDER_FILLING_FOK;
req.comment = "HedgingEA";
bool sent = OrderSend(req, res);
if(!sent || res.retcode != TRADE_RETCODE_DONE)
{
Print("Open not completed: ", res.retcode, " ", res.comment);
return 0;
}
ENUM_POSITION_TYPE expected = direction == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
PositionGetInteger(POSITION_TYPE) == expected) return ticket;
}
Print("Executed order has no immediately visible matching position; halt and reconcile");
return 0;
}
void CloseManaged()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !ManagedSelected()) continue;
string symbol = PositionGetString(POSITION_SYMBOL);
MqlTick tick;
if(!SymbolInfoTick(symbol, tick)) continue;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.position = ticket;
req.symbol = symbol;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.type = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
req.price = req.type == ORDER_TYPE_SELL ? tick.bid : tick.ask;
req.magic = (ulong)MagicNumber;
req.deviation = 10;
req.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
Print("Close not completed for ", ticket, ": ", res.retcode, " ", res.comment);
}
}
void OnTick()
{
// Management is independent of MA data availability.
if(closing || halted)
{
CloseManaged();
if(!halted && ManagedCount() == 0)
{
closing = false; initialTicket = 0; hedgeAttempted = false;
}
return;
}
if(initialTicket != 0)
{
double floating = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
if(PositionGetTicket(i) > 0 && ManagedSelected())
floating += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
bool missingLeg = !PositionSelectByTicket(initialTicket) ||
(hedgeAttempted && ManagedCount() < 2);
if(missingLeg || floating <= -MaxLossMoney || floating >= TargetProfitMoney)
{
closing = true;
halted = missingLeg || floating <= -MaxLossMoney;
CloseManaged();
return;
}
if(!hedgeAttempted && PositionSelectByTicket(initialTicket))
{
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick)) return;
double adverse = initialDirection == POSITION_TYPE_BUY ? entry - tick.bid : tick.ask - entry;
if(adverse / _Point >= HedgeTriggerPoints)
{
hedgeAttempted = true;
ENUM_ORDER_TYPE direction = initialDirection == POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
if(OpenLeg(hedgeSymbol, direction, InitialLot * HedgeLotMultiple) == 0)
{
halted = true; closing = true; CloseManaged();
}
}
}
return;
}
datetime bar = iTime(_Symbol, _Period, 0);
if(bar <= 0 || bar == lastBar) return;
double ma[2], prices[2];
if(CopyBuffer(maHandle, 0, 1, 2, ma) != 2 || CopyClose(_Symbol, _Period, 1, 2, prices) != 2) return;
lastBar = bar;
bool up = prices[0] <= ma[0] && prices[1] > ma[1];
bool down = prices[0] >= ma[0] && prices[1] < ma[1];
if(!up && !down) return;
initialDirection = up ? POSITION_TYPE_BUY : POSITION_TYPE_SELL;
initialTicket = OpenLeg(_Symbol, up ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, InitialLot);
if(initialTicket == 0) { halted = true; closing = true; CloseManaged(); }
}Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom Multi-pair hedging EA
Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.