//+------------------------------------------------------------------+
//| Custom Buy Sell EA with Martingale & Stats |
//+------------------------------------------------------------------+
#property strict
#property version "1.10"
#property copyright "OpenAI Enhanced Version"
#property link "https://www.mql5.com"
//--- input parameters
input group "Signal Settings"
input int DominanceThreshold = 3;
input ENUM_TIMEFRAMES SignalTimeframe = PERIOD_M1;
input group "Risk Management"
input double LotSize = 0.01;
input int TakeProfitPips = 1000;
input int StopLossPips = 2000;
input int TrailingStopPips = 200;
input int TrailingStartPips = 1200;
input group "Alert Settings"
input bool EnableAlerts = true;
//--- global variables
datetime lastCandleTime = 0;
double CurrentLot = 0.0;
int MartingaleLevel = 0;
int MaxMartingaleLevel = 8;
int TotalWins = 0;
int TotalLosses = 0;
int TotalTrades = 0;
double TotalProfitUSD = 0.0;
double TotalLossUSD = 0.0;
string lotFileName = "martingale_lot.txt";
string statsFile = "martingale_stats.txt";
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
LoadLotFromFile();
LoadStatsFromFile();
PrintStats();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert tick |
//+------------------------------------------------------------------+
void OnTick()
{
datetime currentCandleTime = iTime(_Symbol, _Period, 0);
if(currentCandleTime == lastCandleTime)
return;
lastCandleTime = currentCandleTime;
UpdateLotBasedOnLastTrade();
CheckForSignals();
ManageTrailingStop();
DisplayStatsOnChart();
}
//+------------------------------------------------------------------+
//| Check for trading signals |
//+------------------------------------------------------------------+
void CheckForSignals()
{
double open[], close[];
ArraySetAsSeries(open, true);
ArraySetAsSeries(close, true);
if(CopyOpen(_Symbol, SignalTimeframe, 0, 5, open) < 5 ||
CopyClose(_Symbol, SignalTimeframe, 0, 5, close) < 5)
return;
int bullishCount = 0;
int bearishCount = 0;
for(int i = 1; i <= 4; i++)
{
if(close[i] > open[i]) bullishCount++;
else if(close[i] < open[i]) bearishCount++;
}
if(bullishCount >= DominanceThreshold)
PlaceOrder(ORDER_TYPE_BUY);
if(bearishCount >= DominanceThreshold)
PlaceOrder(ORDER_TYPE_SELL);
}
//+------------------------------------------------------------------+
//| Place market order |
//+------------------------------------------------------------------+
void PlaceOrder(ENUM_ORDER_TYPE orderType)
{
if(PositionSelect(_Symbol))
{
Print("Position already open for ", _Symbol);
return;
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double price = (orderType == ORDER_TYPE_BUY) ? ask : bid;
double stopLoss = 0, takeProfit = 0;
if(orderType == ORDER_TYPE_BUY)
{
stopLoss = price - StopLossPips * _Point;
takeProfit = price + TakeProfitPips * _Point;
}
else
{
stopLoss = price + StopLossPips * _Point;
takeProfit = price - TakeProfitPips * _Point;
}
MqlTradeRequest request = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = CurrentLot;
request.type = orderType;
request.price = price;
request.sl = stopLoss;
request.tp = takeProfit;
request.deviation = 10;
request.magic = 12345;
request.comment = "M1_Dominance_Martingale";
MqlTradeResult result = {};
if(!OrderSend(request, result))
{
Print("OrderSend failed. Code: ", result.retcode);
}
else if(EnableAlerts)
{
Alert("Order placed: ", EnumToString(orderType), " at ", price);
}
}
//+------------------------------------------------------------------+
//| Trailing stop management |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStopPips <= 0 || !PositionSelect(_Symbol)) return;
ulong ticket = PositionGetInteger(POSITION_TICKET);
double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
int posType = (int)PositionGetInteger(POSITION_TYPE);
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double profitPips = MathAbs((currentPrice - entryPrice) / _Point);
if(profitPips < TrailingStartPips) return;
double currentSL = PositionGetDouble(POSITION_SL);
double newSL = 0;
if(posType == POSITION_TYPE_BUY)
{
newSL = currentPrice - TrailingStopPips * _Point;
if(newSL > currentSL)
ModifyPosition(ticket, newSL);
}
else
{
newSL = currentPrice + TrailingStopPips * _Point;
if(newSL < currentSL || currentSL == 0)
ModifyPosition(ticket, newSL);
}
}
//+------------------------------------------------------------------+
//| Modify SL only |
//+------------------------------------------------------------------+
void ModifyPosition(ulong ticket, double newSL)
{
MqlTradeRequest request = {};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = newSL;
request.tp = 0;
MqlTradeResult result = {};
if(!OrderSend(request, result))
Print("Modify SL failed. Code: ", result.retcode);
else if(EnableAlerts)
Alert("Stop Loss modified to ", newSL);
}
//+------------------------------------------------------------------+
//| Update Martingale Lot |
//+------------------------------------------------------------------+
void UpdateLotBasedOnLastTrade()
{
// Select last week history
datetime fromTime = TimeCurrent() - 7 * 86400;
datetime toTime = TimeCurrent();
if(!HistorySelect(fromTime, toTime))
return;
ulong lastTicket = 0;
datetime lastTime = 0;
int dealsTotal = HistoryDealsTotal();
for(int i = dealsTotal - 1; i >= 0; i--)
{
ulong ticket = HistoryDealGetTicket(i);
if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol)
continue;
datetime dealTime = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
if(dealTime > lastTime)
{
lastTime = dealTime;
lastTicket = ticket;
}
}
if(lastTicket == 0)
{
CurrentLot = LotSize;
MartingaleLevel = 0;
return;
}
double profit = HistoryDealGetDouble(lastTicket, DEAL_PROFIT);
TotalTrades++;
if(profit >= 0)
{
TotalWins++;
TotalProfitUSD += profit;
MartingaleLevel = 0;
CurrentLot = LotSize;
}
else
{
TotalLosses++;
TotalLossUSD += profit;
if(MartingaleLevel < MaxMartingaleLevel)
MartingaleLevel++;
CurrentLot = LotSize * MathPow(2, MartingaleLevel);
}
// Safety cap max lot size (adjust as needed)
if(CurrentLot > 10.0)
CurrentLot = 10.0;
SaveLotToFile();
SaveStatsToFile();
PrintStats();
}
//+------------------------------------------------------------------+
//| File Save/Load |
//+------------------------------------------------------------------+
void SaveLotToFile()
{
int file = FileOpen(lotFileName, FILE_WRITE|FILE_TXT);
if(file != INVALID_HANDLE)
{
FileWrite(file, DoubleToString(CurrentLot, 2));
FileWrite(file, IntegerToString(MartingaleLevel));
FileClose(file);
}
}
void LoadLotFromFile()
{
int file = FileOpen(lotFileName, FILE_READ|FILE_TXT);
if(file != INVALID_HANDLE)
{
string l = FileReadString(file);
string lv = FileReadString(file);
FileClose(file);
CurrentLot = StringToDouble(l);
MartingaleLevel = StringToInteger(lv);
if(CurrentLot <= 0) CurrentLot = LotSize;
}
else
{
CurrentLot = LotSize;
MartingaleLevel = 0;
}
}
void SaveStatsToFile()
{
int file = FileOpen(statsFile, FILE_WRITE|FILE_TXT);
if(file != INVALID_HANDLE)
{
FileWrite(file, TotalTrades);
FileWrite(file, TotalWins);
FileWrite(file, TotalLosses);
FileWrite(file, TotalProfitUSD);
FileWrite(file, TotalLossUSD);
FileClose(file);
}
}
void LoadStatsFromFile()
{
int file = FileOpen(statsFile, FILE_READ|FILE_TXT);
if(file != INVALID_HANDLE)
{
TotalTrades = FileReadInteger(file);
TotalWins = FileReadInteger(file);
TotalLosses = FileReadInteger(file);
TotalProfitUSD = FileReadDouble(file);
TotalLossUSD = FileReadDouble(file);
FileClose(file);
}
}
//+------------------------------------------------------------------+
//| Print Statistik ke Log |
//+------------------------------------------------------------------+
void PrintStats()
{
double winRate = (TotalTrades > 0) ? (100.0 * TotalWins / TotalTrades) : 0.0;
PrintFormat("Martingale Stats: Trades=%d Wins=%d Losses=%d WinRate=%.2f%%
Profit=%.2f Loss=%.2f Lot=%.2f Level=%d",
TotalTrades, TotalWins, TotalLosses, winRate, TotalProfitUSD,
TotalLossUSD, CurrentLot, MartingaleLevel);
}
//+------------------------------------------------------------------+
//| Display statistik di chart |
//+------------------------------------------------------------------+
void DisplayStatsOnChart()
{
double winRate = (TotalTrades > 0) ? (100.0 * TotalWins / TotalTrades) : 0.0;
string dashboard =
"📊 Martingale Stats\n" +
"Total Trades: " + IntegerToString(TotalTrades) + "\n" +
"Wins: " + IntegerToString(TotalWins) +
" | Losses: " + IntegerToString(TotalLosses) + "\n" +
"Win Rate: " + DoubleToString(winRate, 2) + "%\n" +
"Profit: $" + DoubleToString(TotalProfitUSD, 2) +
" | Loss: $" + DoubleToString(TotalLossUSD, 2) + "\n" +
"Current Lot: " + DoubleToString(CurrentLot, 2) +
" (Level " + IntegerToString(MartingaleLevel) + ")";
Comment(dashboard);
}
//+------------------------------------------------------------------+