//+------------------------------------------------------------------+
//| MR CAPFREE.mq4 |
//| Copyright 2025, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
// Enum for lot type calculation
enum enumLotType
{
Fixed_Lots = 0,
Pct_of_Balance = 1,
Pct_of_Equity = 2,
Pct_of_Free_Margin = 3
};
//--- Input Parameters
input string GeneralSettings = "=================="; // GENERAL SETTINGS
input int InpMagic = 12345; // Magic Number
input int Slippage = 1; // Slippage
input string TimeSettings = "=================="; // Time Settings
input int StartHour = 16; // START TRADING HOUR
input int EndHour = 22; // End Trading Hour
input int Secs = 60; // ORDER MODIFICATIONS (Should
be same as TF)
input string MoneyManagement = "=================="; // MONEY MANAGEMENT
input enumLotType LotType = Fixed_Lots; // Type of Lotsize calculation
input double FixedLot = 0.01; // Fixed Lots
input double RiskPercent = 0.5; // Risk per MM
input string TradeSettings = "=================="; // TRADE SETTINGS IN POINTS
input double Delta = 0.5; // ORDER DISTANCE
input double MaxDistance = 7; // THETA (Max order distance)
input double Stop = 10; // Stop Loss Size
input double MaxTrailing = 4; // COS (start of trailing
stop)
input int MaxSpread = 20; // Max spread Limit
//--- Global Variables
double DeltaX = 0;
double MinOrderDistance = 0.5;
double MaxTrailingLimit = 7.5;
double OrderModificationFactor = 3;
int TickCounter = 0;
double PriceToPipRatio = 0;
double BaseTrailingStop = 0;
double TrailingStopBuffer = 0;
double TrailingStopIncrement = 0;
double TrailingStopThreshold = 0;
long AccountLeverageValue = 0;
double LotStepSize = 0;
double MaxLotSize = 0;
double MinLotSize = 0;
double MarginPerMinLot = 0;
double MinStopDistance = 0;
int BrokerStopLevel = 0;
double MinFreezeDistance = 0;
int BrokerFreezeLevel = 0;
double CurrentSpread = 0;
double AverageSpread = 0;
int EAModeFlag = 0;
int SpreadArraySize = 0;
int DefaultSpreadPeriod = 30;
double MaxAllowedSpread = 0;
double CalculatedLotSize = 0;
double CommissionPerPip = 0;
int SpreadMultiplier = 0;
double AdjustedOrderDistance = 0;
double MinOrderModification = 0;
double TrailingStopActive = 0;
double TrailingStopMax = 0;
double MaxOrderPlacementDistance = 0;
double OrderPlacementStep = 0;
double CalculatedStopLoss = 0;
double CurrentBuySL = 0;
string OrderCommentText = "Gasper";
int LastBuyOrderTime = 0;
double CurrentSellSL = 0;
int LastSellOrderTime = 0;
int OrderCheckFrequency = 2;
int SpreadCalculationMethod = 1;
bool EnableTrading = false;
double SpreadHistoryArray[];
int LastOrderTime = 0;
int MinOrderInterval = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set chart properties
ChartSetInteger(0, CHART_SHOW_GRID, false);
// Initialize variables
DeltaX = Delta;
if(MinOrderDistance > Delta)
{
DeltaX = MinOrderDistance + 0.1;
}
if(MaxTrailing > MaxTrailingLimit)
{
MaxTrailingLimit = MaxTrailing + 0.1;
}
if(OrderModificationFactor < 1)
{
OrderModificationFactor = 1;
}
TickCounter = 0;
PriceToPipRatio = 0;
BaseTrailingStop = TrailingStopBuffer;
TrailingStopIncrement = TrailingStopThreshold;
AccountLeverageValue = AccountLeverage();
// Get symbol properties
LotStepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
MaxLotSize = MarketInfo(Symbol(), MODE_MAXLOT);
MinLotSize = MarketInfo(Symbol(), MODE_MINLOT);
MarginPerMinLot = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * MinLotSize;
// Get broker restrictions
BrokerStopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
MinStopDistance = 0;
if(BrokerStopLevel > 0)
MinStopDistance = (BrokerStopLevel + 1) * Point;
BrokerFreezeLevel = (int)MarketInfo(Symbol(), MODE_FREEZELEVEL);
MinFreezeDistance = 0;
if(BrokerFreezeLevel > 0)
MinFreezeDistance = (BrokerFreezeLevel + 1) * Point;
if(BrokerStopLevel > 0 || BrokerFreezeLevel > 0)
{
Comment("WARNING: Broker is not suitable, the stoplevel is greater than
zero");
}
// Initialize spread tracking
double ask = Ask;
double bid = Bid;
CurrentSpread = NormalizeDouble(ask - bid, Digits);
AverageSpread = CurrentSpread;
SpreadArraySize = (EAModeFlag == 0) ? DefaultSpreadPeriod : 3;
ArrayResize(SpreadHistoryArray, SpreadArraySize);
ArrayInitialize(SpreadHistoryArray, CurrentSpread);
MaxAllowedSpread = NormalizeDouble(MaxSpread * Point, Digits);
Print("MR CAPFREE EA initialized successfully");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
int CurrentTime = (int)TimeCurrent();
int PendingBuyCount = 0;
int PendingSellCount = 0;
int OpenBuyCount = 0;
int OpenSellCount = 0;
int TotalBuyCount = 0;
int TotalSellCount = 0;
double BuyOrdersPriceSum = 0;
double BuyOrdersLotSum = 0;
double SellOrdersPriceSum = 0;
double SellOrdersLotSum = 0;
double AverageBuyPrice = 0;
double AverageSellPrice = 0;
double LowestBuyPrice = 99999;
double HighestSellPrice = 0;
TickCounter++;
// Calculate price to pip ratio from history (MT4 version)
if(PriceToPipRatio == 0)
{
for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if(OrderSymbol() != Symbol()) continue;
if(OrderProfit() == 0) continue;
if(OrderType() > 1) continue; // Only BUY/SELL orders
double entryPrice = OrderOpenPrice();
double exitPrice = OrderClosePrice();
double profit = OrderProfit();
double commission = OrderCommission();
if(exitPrice != entryPrice)
{
PriceToPipRatio = MathAbs(profit / (exitPrice - entryPrice));
CommissionPerPip = commission / PriceToPipRatio;
break;
}
}
}
}
// Update spread history
double ask = Ask;
double bid = Bid;
double newSpread = NormalizeDouble(ask - bid, Digits);
// Shift array and add new spread
for(int i = 0; i < SpreadArraySize - 1; i++)
{
SpreadHistoryArray[i] = SpreadHistoryArray[i + 1];
}
SpreadHistoryArray[SpreadArraySize - 1] = newSpread;
// Calculate average spread
double sum = 0;
for(int i = 0; i < SpreadArraySize; i++)
{
sum += SpreadHistoryArray[i];
}
CurrentSpread = sum / SpreadArraySize;
// Calculate average spread including commission
AverageSpread = MathMax(SpreadMultiplier * Point, CurrentSpread +
CommissionPerPip);
// Calculate order distances
AdjustedOrderDistance = MathMax(AverageSpread * Delta, MinStopDistance);
MinOrderModification = MathMax(AverageSpread * MinOrderDistance,
MinFreezeDistance);
// Calculate trailing stop values
TrailingStopActive = AverageSpread * MaxTrailing;
TrailingStopMax = AverageSpread * MaxTrailingLimit;
MaxOrderPlacementDistance = AverageSpread * MaxDistance;
OrderPlacementStep = MinOrderModification / OrderModificationFactor;
CalculatedStopLoss = MathMax(AverageSpread * Stop, MinStopDistance);
// Count open positions
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic)
{
double price = OrderOpenPrice();
double lots = OrderLots();
double sl = OrderStopLoss();
if(OrderType() == OP_BUY)
{
OpenBuyCount++;
if(sl == 0 || (sl > 0 && sl < price)) TotalBuyCount++;
CurrentBuySL = sl;
BuyOrdersPriceSum += price * lots;
BuyOrdersLotSum += lots;
if(price < LowestBuyPrice) LowestBuyPrice = price;
}
else if(OrderType() == OP_SELL)
{
OpenSellCount++;
if(sl == 0 || (sl > 0 && sl > price)) TotalSellCount++;
CurrentSellSL = sl;
SellOrdersPriceSum += price * lots;
SellOrdersLotSum += lots;
if(price > HighestSellPrice) HighestSellPrice = price;
}
else if(OrderType() == OP_BUYSTOP)
{
PendingBuyCount++;
TotalBuyCount++;
}
else if(OrderType() == OP_SELLSTOP)
{
PendingSellCount++;
TotalSellCount++;
}
}
}
}
// Calculate average prices
if(BuyOrdersLotSum > 0)
{
AverageBuyPrice = NormalizeDouble(BuyOrdersPriceSum / BuyOrdersLotSum,
Digits);
}
if(SellOrdersLotSum > 0)
{
AverageSellPrice = NormalizeDouble(SellOrdersPriceSum / SellOrdersLotSum,
Digits);
}
// Get broker time
datetime brokerTime = TimeCurrent();
int hour = TimeHour(brokerTime);
// Process pending orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != Symbol() || OrderMagicNumber() != InpMagic) continue;
int ticket = OrderTicket();
int type = OrderType();
double openPrice = OrderOpenPrice();
double sl = OrderStopLoss();
double tp = OrderTakeProfit();
double lots = OrderLots();
// Process buy stop orders
if(type == OP_BUYSTOP)
{
bool allowTrade = (hour >= StartHour && hour <= EndHour);
if(AverageSpread > MaxAllowedSpread || !allowTrade)
{
OrderDelete(ticket);
continue;
}
int timeDiff = CurrentTime - LastBuyOrderTime;
bool needsModification = (timeDiff > Secs) ||
(TickCounter % OrderCheckFrequency == 0 &&
((OpenBuyCount < 1 && (openPrice - Ask) <
MinOrderModification) ||
(openPrice - Ask) < OrderPlacementStep ||
(openPrice - Ask) > MaxOrderPlacementDistance));
if(needsModification)
{
double distance = AdjustedOrderDistance;
if(OpenBuyCount > 0) distance /= OrderModificationFactor;
distance = MathMax(distance, MinStopDistance);
double modifiedPrice = NormalizeDouble(Ask + distance, Digits);
double modifiedSl = (OpenBuyCount > 0) ? CurrentBuySL :
NormalizeDouble(modifiedPrice - CalculatedStopLoss, Digits);
if((OpenBuyCount == 0 || modifiedPrice > AverageBuyPrice) &&
modifiedPrice != openPrice &&
(openPrice - Ask) > MinFreezeDistance)
{
OrderModify(ticket, modifiedPrice, modifiedSl, tp, 0);
LastBuyOrderTime = CurrentTime;
}
}
}
// Process sell stop orders
else if(type == OP_SELLSTOP)
{
bool allowTrade = (hour >= StartHour && hour <= EndHour);
if(AverageSpread > MaxAllowedSpread || !allowTrade)
{
OrderDelete(ticket);
continue;
}
int timeDiff = CurrentTime - LastSellOrderTime;
bool needsModification = (timeDiff > Secs) ||
(TickCounter % OrderCheckFrequency == 0 &&
((OpenSellCount < 1 && (Bid - openPrice) <
MinOrderModification) ||
(Bid - openPrice) < OrderPlacementStep ||
(Bid - openPrice) > MaxOrderPlacementDistance));
if(needsModification)
{
double distance = AdjustedOrderDistance;
if(OpenSellCount > 0) distance /= OrderModificationFactor;
distance = MathMax(distance, MinStopDistance);
double modifiedPrice = NormalizeDouble(Bid - distance, Digits);
double modifiedSl = (OpenSellCount > 0) ? CurrentSellSL :
NormalizeDouble(modifiedPrice + CalculatedStopLoss, Digits);
if((OpenSellCount == 0 || modifiedPrice < AverageSellPrice) &&
modifiedPrice != openPrice &&
(Bid - openPrice) > MinFreezeDistance)
{
OrderModify(ticket, modifiedPrice, modifiedSl, tp, 0);
LastSellOrderTime = CurrentTime;
}
}
}
}
// Process open positions for trailing stops
for(int i = 0; i < OrdersTotal(); i++)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != Symbol() || OrderMagicNumber() != InpMagic) continue;
if(OrderType() > 1) continue; // Skip pending orders
int ticket = OrderTicket();
int type = OrderType();
double openPrice = OrderOpenPrice();
double sl = OrderStopLoss();
double tp = OrderTakeProfit();
// Process Buy Positions
if(type == OP_BUY)
{
double priceMove = MathMax(Bid - openPrice + CommissionPerPip, 0);
double trailDist = CalculateTrailingStop(priceMove, MinStopDistance,
TrailingStopActive, BaseTrailingStop, TrailingStopMax);
double modifiedSl = NormalizeDouble(Bid - trailDist, Digits);
double triggerLevel = openPrice + CommissionPerPip +
TrailingStopIncrement;
if((Bid - triggerLevel) > trailDist &&
(sl == 0 || (Bid - sl) > trailDist) &&
modifiedSl != sl)
{
OrderModify(ticket, openPrice, modifiedSl, tp, 0);
}
}
// Process Sell Positions
else if(type == OP_SELL)
{
double priceMove = MathMax(openPrice - Ask - CommissionPerPip, 0);
double trailDist = CalculateTrailingStop(priceMove, MinStopDistance,
TrailingStopActive, BaseTrailingStop, TrailingStopMax);
double modifiedSl = NormalizeDouble(Ask + trailDist, Digits);
double triggerLevel = openPrice - CommissionPerPip -
TrailingStopIncrement;
if((triggerLevel - Ask) > trailDist &&
(sl == 0 || (sl - Ask) > trailDist) &&
modifiedSl != sl)
{
OrderModify(ticket, openPrice, modifiedSl, tp, 0);
}
}
}
// Place new buy stop order if needed
if(((OrderModificationFactor > 1 && TotalBuyCount < 1) || OpenBuyCount < 1) &&
PendingBuyCount < 1)
{
bool spreadOK = (AverageSpread <= MaxAllowedSpread);
bool timeOK = (hour >= StartHour && hour <= EndHour);
if(spreadOK && timeOK && (CurrentTime - LastOrderTime) > MinOrderInterval &&
EAModeFlag == 0)
{
// Calculate lot size
if(LotType == Fixed_Lots)
{
CalculatedLotSize = MathCeil(FixedLot / LotStepSize) * LotStepSize;
CalculatedLotSize = MathMax(CalculatedLotSize, MinLotSize);
}
else if(LotType > Fixed_Lots)
{
CalculatedLotSize = CalcLots(CalculatedStopLoss);
}
// Check margin requirement
double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED) *
CalculatedLotSize;
if(AccountFreeMargin() > marginRequired)
{
double orderDist = MathMax(MathMax(AdjustedOrderDistance,
MinFreezeDistance), MinStopDistance);
double orderPrice = NormalizeDouble(Ask + orderDist, Digits);
double orderSL = (OpenBuyCount > 0) ? CurrentBuySL :
NormalizeDouble(orderPrice - CalculatedStopLoss, Digits);
int result = OrderSend(Symbol(), OP_BUYSTOP, CalculatedLotSize,
orderPrice, Slippage, orderSL, 0, OrderCommentText, InpMagic);
if(result > 0)
{
LastBuyOrderTime = CurrentTime;
LastOrderTime = CurrentTime;
}
}
}
}
// Place new sell stop order if needed
if(((OrderModificationFactor > 1 && TotalSellCount < 1) || OpenSellCount < 1) &&
PendingSellCount < 1)
{
bool spreadOK = (AverageSpread <= MaxAllowedSpread);
bool timeOK = (hour >= StartHour && hour <= EndHour);
if(spreadOK && timeOK && (CurrentTime - LastOrderTime) > MinOrderInterval &&
EAModeFlag == 0)
{
// Calculate lot size
if(LotType == Fixed_Lots)
{
CalculatedLotSize = MathCeil(FixedLot / LotStepSize) * LotStepSize;
CalculatedLotSize = MathMax(CalculatedLotSize, MinLotSize);
}
else if(LotType > Fixed_Lots)
{
CalculatedLotSize = CalcLots(CalculatedStopLoss);
}
// Check margin requirement
double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED) *
CalculatedLotSize;
if(AccountFreeMargin() > marginRequired)
{
double orderDist = MathMax(MathMax(AdjustedOrderDistance,
MinFreezeDistance), MinStopDistance);
double orderPrice = NormalizeDouble(Bid - orderDist, Digits);
double orderSL = (OpenSellCount > 0) ? CurrentSellSL :
NormalizeDouble(orderPrice + CalculatedStopLoss, Digits);
int result = OrderSend(Symbol(), OP_SELLSTOP, CalculatedLotSize,
orderPrice, Slippage, orderSL, 0, OrderCommentText, InpMagic);
if(result > 0)
{
LastSellOrderTime = CurrentTime;
LastOrderTime = CurrentTime;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Calculate trailing stop distance |
//+------------------------------------------------------------------+
double CalculateTrailingStop(double priceMove, double minDist, double activeDist,
double baseDist, double maxDist)
{
if(maxDist == 0) return MathMax(activeDist, minDist);
double ratio = priceMove / maxDist;
double dynamicDist = (activeDist - baseDist) * ratio + baseDist;
return MathMax(MathMax(dynamicDist, activeDist), minDist);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on risk management |
//+------------------------------------------------------------------+
double CalcLots(double slPoints)
{
double lots = MarketInfo(Symbol(), MODE_MINLOT);
double AccountBalance = AccountBalance();
double EquityBalance = AccountEquity();
double FreeMargin = AccountFreeMargin();
double risk = 0;
switch(LotType)
{
case Fixed_Lots:
lots = FixedLot;
return lots;
case Pct_of_Balance:
risk = AccountBalance * RiskPercent / 100;
break;
case Pct_of_Equity:
risk = EquityBalance * RiskPercent / 100;
break;
case Pct_of_Free_Margin:
risk = FreeMargin * RiskPercent / 100;
break;
}
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(tickSize > 0 && tickValue > 0)
{
double moneyPerLotStep = slPoints / tickSize * tickValue * lotStep;
if(moneyPerLotStep > 0)
lots = MathFloor(risk / moneyPerLotStep) * lotStep;
}
// Apply limits
double minVolume = MarketInfo(Symbol(), MODE_MINLOT);
double maxVolume = MarketInfo(Symbol(), MODE_MAXLOT);
if(maxVolume != 0) lots = MathMin(lots, maxVolume);
if(minVolume != 0) lots = MathMax(lots, minVolume);
lots = NormalizeDouble(lots, 2);
return lots;
}