Problem Description
Solution
Here is a complete, fully functional MQL4 code for a Simple Hedging Expert Advisor (EA).
Strategy Logic: "The Straddle Hedge"
This is one of the most common and effective simple hedging strategies.
- Entry: The EA places two pending orders: a Buy Stop above the current price and a Sell Stop below the current price.
- Execution:
- If the market trends, one order triggers and hits the Take Profit.
- If the market reverses before hitting TP, the opposite pending order triggers. You are now "Hedged" (Long and Short simultaneously).
- Exit: The EA monitors the Total Net Profit of all trades with its Magic Number. Once the total profit (in money) reaches your target, it closes all open positions and deletes pending orders to start fresh.
MQL4 Code
//+------------------------------------------------------------------+
//| SimpleHedgeEA.mq4 |
//| Copyright 2023, Quantitative Assistant |
//| https://mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Assistant"
#property link "https://mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input double LotSize = 0.1; // Fixed Lot Size
input int DistancePips = 20; // Distance from price to place pending orders
input int TakeProfitPips = 40; // Take Profit in Pips (0 = No TP)
input int StopLossPips = 0; // Stop Loss in Pips (0 = No SL, recommended for hedging)
input double ProfitTarget = 10.0; // Global Profit Target in Currency (USD/EUR) to close all
input int MagicNumber = 123456; // Unique Identifier for this EA
input int Slippage = 3; // Max Slippage allowed
//--- Global Variables
double pipMultiplier;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust pips for 3 or 5 digit brokers (Jpy pairs vs others)
if(Digits == 3 || Digits == 5) pipMultiplier = 10;
else pipMultiplier = 1;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Optional: Clean up comments or objects
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Check Global Profit
// If the total profit of our hedge basket exceeds the target, close everything.
if(GetTotalProfit() >= ProfitTarget)
{
CloseAllOrders();
DeleteAllPending();
return; // Wait for next tick
}
// 2. Count current orders
int totalOrders = 0;
int buyStops = 0;
int sellStops = 0;
int activeTrades = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalOrders++;
if(OrderType() == OP_BUYSTOP) buyStops++;
if(OrderType() == OP_SELLSTOP) sellStops++;
if(OrderType() == OP_BUY || OrderType() == OP_SELL) activeTrades++;
}
}
}
// 3. Logic: If no orders exist, place the Straddle (Hedge Trap)
if(totalOrders == 0)
{
double buyPrice = Ask + (DistancePips * Point * pipMultiplier);
double sellPrice = Bid - (DistancePips * Point * pipMultiplier);
// Calculate TP/SL prices
double buyTP = (TakeProfitPips > 0) ? buyPrice + (TakeProfitPips * Point * pipMultiplier) : 0;
double buySL = (StopLossPips > 0) ? buyPrice - (StopLossPips * Point * pipMultiplier) : 0;
double sellTP = (TakeProfitPips > 0) ? sellPrice - (TakeProfitPips * Point * pipMultiplier) : 0;
double sellSL = (StopLossPips > 0) ? sellPrice + (StopLossPips * Point * pipMultiplier) : 0;
// Send Orders
int ticket1 = OrderSend(Symbol(), OP_BUYSTOP, LotSize, buyPrice, Slippage, buySL, buyTP, "Hedge Buy", MagicNumber, 0, clrBlue);
int ticket2 = OrderSend(Symbol(), OP_SELLSTOP, LotSize, sellPrice, Slippage, sellSL, sellTP, "Hedge Sell", MagicNumber, 0, clrRed);
if(ticket1 < 0 || ticket2 < 0) Print("Error placing orders: ", GetLastError());
}
// 4. Maintenance: If one side hit TP and closed, but the other pending order is still there,
// we usually want to delete the pending order to restart the cycle.
if(activeTrades == 0 && totalOrders > 0)
{
// If we have pending orders but no active trades, it usually means one trade hit TP
// and the other pending order is left over. Let's clean up to restart.
// Note: This logic assumes we want a fresh start after a win.
DeleteAllPending();
}
// Display Info
Comment("Simple Hedge EA Running\n",
"Total Profit: ", DoubleToString(GetTotalProfit(), 2), "\n",
"Target: ", DoubleToString(ProfitTarget, 2));
}
//+------------------------------------------------------------------+
//| Helper: Calculate Total Profit for this Magic Number |
//+------------------------------------------------------------------+
double GetTotalProfit()
{
double profit = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// Add Profit + Swap + Commission
profit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return profit;
}
//+------------------------------------------------------------------+
//| Helper: Close all Active Market Orders |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool res = false;
if(OrderType() == OP_BUY)
res = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrNONE);
else if(OrderType() == OP_SELL)
res = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrNONE);
if(!res) Print("Failed to close order: ", OrderTicket(), " Error: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Delete all Pending Orders |
//+------------------------------------------------------------------+
void DeleteAllPending()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() > OP_SELL) // OP_BUYLIMIT, OP_BUYSTOP, etc. are > 1
{
bool res = OrderDelete(OrderTicket(), clrNONE);
if(!res) Print("Failed to delete order: ", OrderTicket(), " Error: ", GetLastError());
}
}
}
}
}
How to Install and Run
- Open MetaTrader 4.
- Press F4 to open the MetaEditor.
- Click New -> Expert Advisor (template) -> Name it "SimpleHedge".
- Delete everything in the new file and paste the code above.
- Press F7 or click Compile. Ensure there are no errors.
- Go back to the MT4 Terminal, find "SimpleHedge" in the Navigator window, and drag it onto a chart.
Key Features Explained
-
Pip Adjustment (
pipMultiplier):
The code automatically detects if your broker uses 5-digit pricing (e.g., EURUSD 1.09123) or 3-digit JPY pricing and adjusts theDistancePipsaccordingly. -
Global Profit Target (
ProfitTarget):
This is crucial for hedging. Often, one trade will be positive and the hedge trade will be negative. The goal is for the volatility to push the winner far enough to cover the loser's drawdown plus a small profit. WhenGetTotalProfit()hits your target (e.g., $10), everything closes. -
The "Straddle" Logic:
- If there are no orders, it places a Buy Stop
DistancePipsabove and a Sell StopDistancePipsbelow. - This captures breakouts in either direction.
- If there are no orders, it places a Buy Stop
-
Maintenance Logic:
- If a trade triggers and hits its individual Take Profit, the EA detects that there are no active trades left but one pending order remains. It deletes that pending order to reset the grid and start fresh.
Important Warnings
- Broker Rules: Some brokers (specifically in the US under NFA rules) do not allow "Hedging" (holding a Buy and Sell on the same symbol simultaneously). This EA requires a broker that allows hedging.
- Spread Risk: If the
DistancePipsis too small (e.g., 5 pips) and the spread is high, both orders might trigger instantly, locking you in a loss immediately. KeepDistancePipsat least 2x or 3x the spread. - Drawdown: If the market ranges perfectly between your Buy and Sell orders without hitting the Profit Target, you will accumulate Swap fees, and equity will be tied up.
Q&A: Quantitative Trading & MQL4
Q: How can I modify this to increase the lot size if the first trade fails (Martingale)?
A: You would need to modify the OnTick logic. Instead of placing both orders at the start, you would place one. If that trade goes into negative pips, you would open an opposite trade with LotSize * Multiplier. However, Martingale significantly increases the risk of blowing the account.
Q: Why do we use OrderSelect loop backwards when closing orders?
A: When you close or delete an order, the index of the remaining orders in the pool changes. If you loop forward (i++), you will skip orders. Looping backward (i--) ensures every order is checked and processed correctly.
Q: Can this EA work on Renko charts?
A: Yes, technically. However, Renko charts on MT4 are usually offline charts. You would need to enable "Allow DLL imports" and ensure the EA is attached to the offline chart generated by your Renko indicator. The logic remains the same.