To enforce your "No Negotiation" rules (Partials, Break-Even, and Time Stops) reliably on a local terminal, we handle the execution differently across the two platforms due to their distinct architectures.
1. The MT4 Base Version (MQL4)
MT4 is strictly an order-based system. The cleanest algorithmic way to manage partials in MT4 without losing track of state after a terminal restart is the Split-Ticket Execution. We send two separate orders simultaneously: one assigned to the Partial, one to the Runner. The broker handles the partial take-profit automatically, and our EA simply listens for the partial to close before moving the runner to Break-Even.
Code: Select all
//+------------------------------------------------------------------+
//| PA_Engine_Base_MT4.mq4 |
//+------------------------------------------------------------------+
#property strict
input double InpLotSize = 0.1; // Total Position Size (Split in half)
input double InpPartialR = 1.0; // Partial Target (R-Multiple)
input double InpRunnerR = 3.0; // Runner Target (R-Multiple)
input int InpTimeStopBars = 12; // Time Stop in Bars
input int BaseMagic = 10000; // Magic Number Base
int MAGIC_PARTIAL = BaseMagic + 1;
int MAGIC_RUNNER = BaseMagic + 2;
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Check if we have open trades
int partialTickets = 0;
int runnerTickets = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == _Symbol)
{
if(OrderMagicNumber() == MAGIC_PARTIAL) partialTickets++;
if(OrderMagicNumber() == MAGIC_RUNNER) runnerTickets++;
}
}
bool hasActiveTrades = (partialTickets > 0 || runnerTickets > 0);
// 2. ENTRY LOGIC (Placeholder for your PA setup)
if(!hasActiveTrades)
{
// Simple Bullish Engulfing for demonstration
bool isBullish = Close[1] > Open[1] && Close[2] < Open[2] && Close[1] > Open[2];
if(isBullish)
{
double sl = Low[iLowest(_Symbol, _Period, MODE_LOW, 3, 1)];
double risk = Ask - sl;
double tpPartial = Ask + (risk * InpPartialR);
double tpRunner = Ask + (risk * InpRunnerR);
double splitLot = NormalizeDouble(InpLotSize / 2.0, 2);
// Execute Split Tickets
OrderSend(_Symbol, OP_BUY, splitLot, Ask, 3, sl, tpPartial, "Partial", MAGIC_PARTIAL);
OrderSend(_Symbol, OP_BUY, splitLot, Ask, 3, sl, tpRunner, "Runner", MAGIC_RUNNER);
}
}
// 3. TRADE MANAGEMENT ENGINE
if(hasActiveTrades)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == _Symbol)
{
int barsInTrade = iBarShift(_Symbol, _Period, OrderOpenTime());
// RULE 1: TIME STOP (Momentum died, flatten everything)
if(barsInTrade >= InpTimeStopBars && partialTickets > 0)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), OrderType() == OP_BUY ? Bid : Ask, 3, clrRed);
Print("Time Stop Triggered. Closing trade.");
continue;
}
// RULE 2: MOVE TO BREAK-EVEN
// If the Partial ticket is gone (TP hit) but Runner is open, move SL to Entry
if(partialTickets == 0 && runnerTickets > 0 && OrderMagicNumber() == MAGIC_RUNNER)
{
// Check if SL is not already at Break-Even
if(OrderStopLoss() != OrderOpenPrice())
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrBlue);
if(modified) Print("Partial hit. Runner moved to Break-Even.");
}
}
}
}
}
}