forked from Werewolfvn/SimpleHedging
276 lines
10 KiB
MQL5
276 lines
10 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| MartingaleEA_XAUUSD.mq5 |
|
|
//| Copyright 2025, Your Name |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, Your Name"
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
|
|
#include <Trade/Trade.mqh> // Include Trade library
|
|
|
|
//--- Input parameters
|
|
input double InitialVolume = 0.01; // Initial Volume (x) for XAUUSD
|
|
input double VolumeMultiplier = 1.5; // Volume Multiplier
|
|
input double TakeProfitPips = 50; // Take Profit (pips) for XAUUSD
|
|
input double PriceStepPips = 20; // Price Step for new orders (pips)
|
|
input double MaxSpreadPips = 5; // Maximum allowable spread (pips)
|
|
|
|
//--- Global variables
|
|
double PipValue; // Value of 1 pip in points
|
|
CTrade trade; // Trade object for order operations
|
|
double initialBuyTP = 0; // Store TP of initial Buy order
|
|
double initialBuyPrice = 0; // Store price of initial Buy order
|
|
double initialSellPrice =0;
|
|
double initialSellTP = 0;
|
|
int priceDigits; // Digits for price normalization
|
|
ulong lastDealTicket = 0; // Track last processed deal ticket
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Calculate pip value based on symbol's point
|
|
PipValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10; // 1 pip = 10 points for XAUUSD
|
|
priceDigits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); // Get symbol digits for normalization
|
|
trade.SetExpertMagicNumber(123456); // Set unique magic number
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Normalize volume to comply with broker's requirements |
|
|
//+------------------------------------------------------------------+
|
|
double NormalizeVolume(double volume)
|
|
{
|
|
double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
// Normalize volume to the nearest step
|
|
double normalized = MathRound(volume / volumeStep) * volumeStep;
|
|
|
|
// Ensure volume is within min/max limits
|
|
normalized = MathMax(minVolume, MathMin(maxVolume, normalized));
|
|
|
|
// Round to correct precision
|
|
int digits = (int)MathCeil(-MathLog10(volumeStep));
|
|
return NormalizeDouble(normalized, digits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Normalize price to comply with broker's tick size |
|
|
//+------------------------------------------------------------------+
|
|
double NormalizePrice(double price)
|
|
{
|
|
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
return NormalizeDouble(price, priceDigits);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if a pending order already exists at a specific price |
|
|
//+------------------------------------------------------------------+
|
|
bool PendingOrderExists(double price, ENUM_ORDER_TYPE orderType)
|
|
{
|
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
if(OrderSelect(ticket))
|
|
{
|
|
if(OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_TYPE) == orderType)
|
|
{
|
|
if(MathAbs(OrderGetDouble(ORDER_PRICE_OPEN) - price) < PipValue)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Cancel all pending Sell Stop orders |
|
|
//+------------------------------------------------------------------+
|
|
void CancelPendingStops()
|
|
{
|
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
if(OrderSelect(ticket))
|
|
{
|
|
if(OrderGetString(ORDER_SYMBOL) == _Symbol)
|
|
{
|
|
trade.OrderDelete(ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// Check spread
|
|
double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) / 10; // Convert to pips
|
|
if(spread > MaxSpreadPips)
|
|
return; // Skip if spread is too high
|
|
|
|
// Check if there are no open positions
|
|
if(PositionsTotal() == 0)
|
|
{
|
|
// Cancel any remaining Sell Stop orders
|
|
CancelPendingStops();
|
|
|
|
// Open initial Buy order if no pending orders
|
|
if(OrdersTotal() == 0)
|
|
{
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double tp = NormalizePrice(ask + TakeProfitPips * PipValue);
|
|
double normalizedVolume = NormalizeVolume(InitialVolume);
|
|
// Place Sell Stop order immediately
|
|
double sellStopPrice = NormalizePrice(bid - PriceStepPips * PipValue);
|
|
double sellTP = NormalizePrice(sellStopPrice - TakeProfitPips * PipValue);
|
|
double sellVolume = NormalizeVolume(InitialVolume * VolumeMultiplier);
|
|
if(trade.Buy(normalizedVolume, _Symbol, ask, sellTP, tp, "Initial Buy"))
|
|
{
|
|
initialBuyTP = tp; // Store normalized TP of initial Buy order
|
|
initialBuyPrice = ask; // Store initial Buy price
|
|
|
|
trade.SellStop(sellVolume, sellStopPrice, _Symbol, tp, sellTP, 0, 0, "Sell Stop Hedge");
|
|
initialSellPrice = sellStopPrice;
|
|
initialSellTP = sellTP;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Process open positions and pending orders
|
|
ManagePositions();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert trade event function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTrade()
|
|
{
|
|
// Select history for the current symbol
|
|
HistorySelect(0, TimeCurrent());
|
|
|
|
// Loop through deals to find the latest one
|
|
for(int i = HistoryDealsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong dealTicket = HistoryDealGetTicket(i);
|
|
if(dealTicket > lastDealTicket && HistoryDealSelect(dealTicket))
|
|
{
|
|
if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol)
|
|
{
|
|
// Check if deal was closed by TP
|
|
if(HistoryDealGetInteger(dealTicket, DEAL_REASON) == DEAL_REASON_TP)
|
|
{
|
|
// Close all remaining positions
|
|
CloseAllPositions();
|
|
// Cancel all pending orders
|
|
CancelPendingStops();
|
|
// Update last processed deal ticket
|
|
lastDealTicket = dealTicket;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close all open positions |
|
|
//+------------------------------------------------------------------+
|
|
void CloseAllPositions()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket))
|
|
{
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
|
|
{
|
|
trade.PositionClose(ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manage open positions and pending orders |
|
|
//+------------------------------------------------------------------+
|
|
void ManagePositions()
|
|
{
|
|
double lastVolume = InitialVolume;
|
|
int buyCount = 0;
|
|
int sellCount = 0;
|
|
bool sellStopTriggered = false;
|
|
|
|
// Loop through all open positions
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket))
|
|
{
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
|
|
{
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
buyCount++;
|
|
if(buyCount == 1) // First Buy order
|
|
{
|
|
initialBuyPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
initialBuyTP = PositionGetDouble(POSITION_TP);
|
|
}
|
|
lastVolume = MathMax(lastVolume, PositionGetDouble(POSITION_VOLUME));
|
|
}
|
|
else // Sell position
|
|
{
|
|
sellCount++;
|
|
lastVolume = MathMax(lastVolume, PositionGetDouble(POSITION_VOLUME));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
// Place Buy Stop order if Sell Stop was triggered and no Buy Stop exists at initialBuyPrice
|
|
if(initialBuyPrice > 0 && !PendingOrderExists(initialBuyPrice, ORDER_TYPE_BUY_STOP) && buyCount == sellCount)
|
|
{
|
|
double newVolume = NormalizeVolume(lastVolume * VolumeMultiplier);
|
|
trade.BuyStop(newVolume, initialBuyPrice, _Symbol, initialSellTP, initialBuyTP, 0, 0, "Additional Buy Stop");
|
|
}
|
|
|
|
// Place new Sell Stop orders after a Buy Stop is triggered
|
|
if(buyCount > 1 && !PendingOrderExists(initialSellPrice, ORDER_TYPE_SELL_STOP) && (buyCount - sellCount) == 1)
|
|
{
|
|
double sellVolume = NormalizeVolume(lastVolume * VolumeMultiplier);
|
|
trade.SellStop(sellVolume, initialSellPrice, _Symbol, initialBuyTP, initialSellTP, 0, 0, "Sell Stop Hedge");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get first Buy order ticket |
|
|
//+------------------------------------------------------------------+
|
|
ulong GetFirstBuyTicket()
|
|
{
|
|
for(int i = 0; i < PositionsTotal(); i++)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket))
|
|
{
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
return ticket;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|