//+------------------------------------------------------------------+
//| EA_XAU_Scalper_Martingale.mq4 |
//| Simple scalper for XAUUSD with optional martingale averaging |
//| - SMA(3)/SMA(20) crossover entry (configurable) |
//| - Fixed TP/SL per order (in points) |
//| - Optional Martingale: multiplier, max steps, distance (points) |
//| - Close-all by total profit (money) |
//+------------------------------------------------------------------+
#property strict
#property copyright "Generated by ChatGPT"
#property version "1.00"
//--- input parameters
input double LotsInitial = 0.01; // initial lot (e.g. 0.01 cent lot)
input int SMA_Fast = 3; // fast SMA period
input int SMA_Slow = 20; // slow SMA period
input ENUM_TIMEFRAMES SMA_Timeframe = PERIOD_M5; // timeframe for SMA
input bool UseCrossoverOnly = true; // use crossover on bar close
input int TP_points = 100; // take profit in points (e.g. 100 points = 10 pip if 1 pip=10 point)
input int SL_points = 150; // stop loss in points
input bool UseMartingale = false; // enable martingale averaging
input double MartingaleMultiplier = 2.0; // lot multiplier each averaging step
input int MaxMartingaleSteps = 4; // maximum averaging steps (including initial = step 1)
input int MartingaleDistancePoints = 300; // distance in points between averaging entries
input double CloseAllProfitMoney = 2.0; // close all positions when total profit >= this amount (USD), 0 to disable
input int MagicNumber = 20250831; // magic number
input bool TradeOnMonday = true;
input bool TradeOnFriday = true;
input int MaxOrdersPerDirection = 10;
//--- globals
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
int OnInit()
{
lastBarTime = 0;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
void OnTick()
{
// Only trade XAUUSD symbol
string sym = Symbol();
// disable on markets other than XAUUSD if you want
// Check daily trading day filters
if(!IsTradingDay()) return;
// Close all by total profit money
if(CloseAllProfitMoney>0)
{
double totalProfit = GetTotalProfit();
if(totalProfit>=CloseAllProfitMoney) CloseAllPositions();
}
// handle new bar based signals
MqlRates rates[];
if(CopyRates(sym,SMA_Timeframe,0,3,rates)<=0) return;
datetime curBar = rates[0].time;
if(UseCrossoverOnly)
{
// on new bar
if(curBar==lastBarTime) return;
lastBarTime = curBar;
// evaluate crossover
double fastPrev = iMA(sym,SMA_Timeframe,SMA_Fast,0,MODE_SMA,PRICE_CLOSE,1);
double slowPrev = iMA(sym,SMA_Timeframe,SMA_Slow,0,MODE_SMA,PRICE_CLOSE,1);
double fastCur = iMA(sym,SMA_Timeframe,SMA_Fast,0,MODE_SMA,PRICE_CLOSE,0);
double slowCur = iMA(sym,SMA_Timeframe,SMA_Slow,0,MODE_SMA,PRICE_CLOSE,0);
// bullish crossover
if(fastPrev<=slowPrev && fastCur>slowCur)
{
// open buy sequence (if not short positions open)
if(!HasOppositeOrders(ORDER_TYPE_BUY))
OpenInitialOrder(ORDER_TYPE_BUY);
}
// bearish crossover
if(fastPrev>=slowPrev && fastCurslow && !HasOppositeOrders(ORDER_TYPE_BUY)) OpenInitialOrder(ORDER_TYPE_BUY);
if(fast=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
{
if(OrderMagicNumber()!=MagicNumber) continue;
tp += OrderProfit()+OrderSwap()+OrderCommission();
}
}
return(tp);
}
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
{
if(OrderMagicNumber()!=MagicNumber) continue;
int type = OrderType();
bool res=false;
if(type==OP_BUY) res = OrderClose(OrderTicket(),OrderLots(),Bid,5);
if(type==OP_SELL) res = OrderClose(OrderTicket(),OrderLots(),Ask,5);
}
}
}
//+------------------------------------------------------------------+
bool HasOppositeOrders(int willOpenType)
{
// willOpenType: ORDER_TYPE_BUY or ORDER_TYPE_SELL
// Check exist opposite orders
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
if(OrderMagicNumber()!=MagicNumber) continue;
if(willOpenType==ORDER_TYPE_BUY && OrderType()==OP_SELL) return true;
if(willOpenType==ORDER_TYPE_SELL && OrderType()==OP_BUY) return true;
}
return false;
}
//+------------------------------------------------------------------+
void OpenInitialOrder(int dir)
{
string sym = Symbol();
double lots = LotsInitial;
double price = (dir==ORDER_TYPE_BUY)? Ask : Bid;
double sl = (dir==ORDER_TYPE_BUY)? price - SL_points*Point : price + SL_points*Point;
double tp = (dir==ORDER_TYPE_BUY)? price + TP_points*Point : price - TP_points*Point;
int ticket = OrderSend(sym,(dir==ORDER_TYPE_BUY?OP_BUY:OP_SELL),lots,price,5,sl,tp,"XAU_Scalper",MagicNumber,0,clrBlue);
if(ticket<0)
{
int err = GetLastError();
Print("OrderSend failed: ",err);
ResetLastError();
}
}
//+------------------------------------------------------------------+
void ManageMartingale()
{
string sym = Symbol();
// For each direction, check last order and place averaging if price reached distance
// BUY side
ManageDirectionMartingale(OP_BUY);
// SELL side
ManageDirectionMartingale(OP_SELL);
}
//+------------------------------------------------------------------+
void ManageDirectionMartingale(int dir)
{
// Count existing orders of this magic and type
int count=0;
double lastPrice=0.0;
double lotsLast=0.0;
int lastTicket=0;
for(int i=0;i0 && OrderOpenTime()>=lastPrice){}
// find most recent order (by ticket)
if(OrderTicket()>lastTicket)
{
lastTicket=OrderTicket();
lastPrice=OrderOpenPrice();
lotsLast=OrderLots();
}
}
if(dir==OP_SELL && OrderType()==OP_SELL)
{
count++;
if(OrderTicket()>lastTicket)
{
lastTicket=OrderTicket();
lastPrice=OrderOpenPrice();
lotsLast=OrderLots();
}
}
}
if(count==0) return; // no base order to average
if(count>=MaxMartingaleSteps) return; // reached max
// determine current price and check distance
double curPrice = (dir==OP_BUY)? Ask : Bid;
double distPoints = MathAbs(curPrice - lastPrice)/Point;
if(distPoints >= MartingaleDistancePoints)
{
// place next averaging order
double nextLots = NormalizeLot(lotsLast*MartingaleMultiplier);
// safety: do not exceed a maximum per direction
if(count+1 > MaxOrdersPerDirection) return;
double sl = (dir==OP_BUY)? curPrice - SL_points*Point : curPrice + SL_points*Point;
double tp = (dir==OP_BUY)? curPrice + TP_points*Point : curPrice - TP_points*Point;
int ticket = OrderSend(sym,(dir==OP_BUY?OP_BUY:OP_SELL),nextLots,curPrice,5,sl,tp,"XAU_Scalper_AVG",MagicNumber,0,clrRed);
if(ticket<0)
{
Print("Martingale OrderSend failed: ",GetLastError());
ResetLastError();
}
}
}
//+------------------------------------------------------------------+
double NormalizeLot(double lot)
{
// Normalize to broker lot step (assume 0.01 min step)
double step = MarketInfo(Symbol(),MODE_LOTSTEP);
double minlot = MarketInfo(Symbol(),MODE_MINLOT);
double maxlot = MarketInfo(Symbol(),MODE_MAXLOT);
double l = MathMax(minlot,MathFloor(lot/step+0.5)*step);
if(l>maxlot) l = maxlot;
return(l);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+