Re: Stop Trading Fixed Lot Sizes: Why Sizing by Pip Distance Saved My Account
Posted: Sat Sep 26, 2026 6:23 pm
2. MetaTrader 4 Version (MQL4)
Code: Select all
//+------------------------------------------------------------------+
//| DynamicRiskExecutor.mq4 |
//+------------------------------------------------------------------+
#property copyright "Dynamic Risk Sizer"
#property link ""
#property version "1.20"
#property strict
enum ENUM_SL_MODE
{
SL_MANUAL_PIPS, // Manual Pips
SL_ATR_VOLATILITY // ATR Volatility
};
// --- Inputs ---
input string InpHeader1 = "=== Risk & Stop Loss ===";
input double InpRiskPct = 0.5; // Risk Per Trade (%)
input ENUM_SL_MODE InpSlMode = SL_MANUAL_PIPS; // Stop Loss Mode
input double InpManualPips = 10.0; // Manual SL (Pips)
input int InpAtrPeriod = 14; // ATR Period
input double InpAtrMultiplier = 1.5; // ATR Multiplier
input double InpRiskRewardRatio = 2.0; // Risk:Reward Ratio (0 = No TP)
input string InpHeader2 = "=== Trade Management ===";
input bool InpUseBreakeven = true; // Auto Move SL to Breakeven
input double InpBreakevenTriggerR = 1.0; // Breakeven Trigger (Multiple of Risk/R)
input string InpHeader3 = "=== System ===";
input int InpMagicNumber = 223344; // Magic Number
double currentLots = 0.0;
double currentSlPips = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
CreateUI();
UpdateCalculations();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectDelete(0, "DRS_BG");
ObjectDelete(0, "DRS_LABEL");
ObjectDelete(0, "DRS_BUY_BTN");
ObjectDelete(0, "DRS_SELL_BTN");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
UpdateCalculations();
if(InpUseBreakeven) CheckBreakeven();
}
//+------------------------------------------------------------------+
//| Auto Breakeven Logic |
//+------------------------------------------------------------------+
void CheckBreakeven()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
{
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
double currentTP = OrderTakeProfit();
int type = OrderType();
double normOpenPrice = NormalizeDouble(openPrice, Digits);
if(type == OP_BUY)
{
if(currentSL < normOpenPrice) // SL is still below entry price
{
double slDistance = openPrice - currentSL;
double triggerPrice = openPrice + (slDistance * InpBreakevenTriggerR);
double currentBid = MarketInfo(Symbol(), MODE_BID);
if(currentBid >= triggerPrice)
{
bool res = OrderModify(OrderTicket(), normOpenPrice, normOpenPrice, currentTP, 0, clrBlue);
if(res) Print("Buy trade moved to breakeven.");
}
}
}
else if(type == OP_SELL)
{
if(currentSL > normOpenPrice || currentSL == 0) // SL is still above entry price
{
double slDistance = currentSL - openPrice;
double triggerPrice = openPrice - (slDistance * InpBreakevenTriggerR);
double currentAsk = MarketInfo(Symbol(), MODE_ASK);
if(currentAsk <= triggerPrice && slDistance > 0)
{
bool res = OrderModify(OrderTicket(), normOpenPrice, normOpenPrice, currentTP, 0, clrRed);
if(res) Print("Sell trade moved to breakeven.");
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Core Calculation Logic |
//+------------------------------------------------------------------+
void UpdateCalculations()
{
double balance = AccountBalance();
double riskDollars = balance * (InpRiskPct / 100.0);
double pipSize = (Digits == 3 || Digits == 5) ? Point * 10 : Point;
if(InpSlMode == SL_MANUAL_PIPS)
{
currentSlPips = InpManualPips;
}
else
{
double atrVal = iATR(Symbol(), 0, InpAtrPeriod, 1);
currentSlPips = (atrVal / pipSize) * InpAtrMultiplier;
}
if(currentSlPips <= 0) return;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
if(tickSize == 0 || tickValue == 0) return;
double slDistancePrice = currentSlPips * pipSize;
double riskPerLot = (slDistancePrice / tickSize) * tickValue;
if(riskPerLot <= 0) return;
double rawLots = riskDollars / riskPerLot;
double stepLot = MarketInfo(Symbol(), MODE_LOTSTEP);
currentLots = MathFloor(rawLots / stepLot) * stepLot;
if(currentLots < MarketInfo(Symbol(), MODE_MINLOT)) currentLots = MarketInfo(Symbol(), MODE_MINLOT);
if(currentLots > MarketInfo(Symbol(), MODE_MAXLOT)) currentLots = MarketInfo(Symbol(), MODE_MAXLOT);
string mode = (InpSlMode == SL_MANUAL_PIPS) ? "Pips" : "Pips (ATR)";
string tpMode = (InpRiskRewardRatio > 0) ? StringFormat("\nTP: 1:%.1f (%.1f Pips)", InpRiskRewardRatio, currentSlPips * InpRiskRewardRatio) : "\nTP: None";
string info = StringFormat("Risk: $%.2f (%.1f%%)\nSL: %.1f %s%s\nLots: %.2f",
riskDollars, InpRiskPct, currentSlPips, mode, tpMode, currentLots);
ObjectSetString(0, "DRS_LABEL", OBJPROP_TEXT, info);
}
//+------------------------------------------------------------------+
//| Chart Event Handler (Clicks) |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
if(id != CHARTEVENT_OBJECT_CLICK) return;
double pipSize = (Digits == 3 || Digits == 5) ? Point * 10 : Point;
double slOffset = currentSlPips * pipSize;
if(sparam == "DRS_BUY_BTN")
{
RefreshRates();
double ask = MarketInfo(Symbol(), MODE_ASK);
double sl = NormalizeDouble(ask - slOffset, Digits);
double tp = (InpRiskRewardRatio > 0) ? NormalizeDouble(ask + (slOffset * InpRiskRewardRatio), Digits) : 0;
int ticket = OrderSend(Symbol(), OP_BUY, currentLots, ask, 3, sl, tp, "DynamicRisk", InpMagicNumber, 0, clrSeaGreen);
if(ticket < 0) Print("Buy order failed. Error: ", GetLastError());
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_STATE, false);
}
else if(sparam == "DRS_SELL_BTN")
{
RefreshRates();
double bid = MarketInfo(Symbol(), MODE_BID);
double sl = NormalizeDouble(bid + slOffset, Digits);
double tp = (InpRiskRewardRatio > 0) ? NormalizeDouble(bid - (slOffset * InpRiskRewardRatio), Digits) : 0;
int ticket = OrderSend(Symbol(), OP_SELL, currentLots, bid, 3, sl, tp, "DynamicRisk", InpMagicNumber, 0, clrFireBrick);
if(ticket < 0) Print("Sell order failed. Error: ", GetLastError());
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_STATE, false);
}
}
//+------------------------------------------------------------------+
//| UI Builder |
//+------------------------------------------------------------------+
void CreateUI()
{
int x = 20; int y = 40;
ObjectCreate(0, "DRS_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, "DRS_BG", OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, "DRS_BG", OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, "DRS_BG", OBJPROP_XSIZE, 150);
ObjectSetInteger(0, "DRS_BG", OBJPROP_YSIZE, 135);
ObjectSetInteger(0, "DRS_BG", OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, "DRS_BG", OBJPROP_BORDER_COLOR, clrDodgerBlue);
ObjectSetInteger(0, "DRS_BG", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectCreate(0, "DRS_LABEL", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "DRS_LABEL", OBJPROP_XDISTANCE, x + 10);
ObjectSetInteger(0, "DRS_LABEL", OBJPROP_YDISTANCE, y + 10);
ObjectSetInteger(0, "DRS_LABEL", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "DRS_LABEL", OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, "DRS_LABEL", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectCreate(0, "DRS_BUY_BTN", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_XDISTANCE, x + 10);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_YDISTANCE, y + 85);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_XSIZE, 60);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_YSIZE, 35);
ObjectSetString(0, "DRS_BUY_BTN", OBJPROP_TEXT, "BUY");
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_BGCOLOR, clrSeaGreen);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "DRS_BUY_BTN", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectCreate(0, "DRS_SELL_BTN", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_XDISTANCE, x + 80);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_YDISTANCE, y + 85);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_XSIZE, 60);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_YSIZE, 35);
ObjectSetString(0, "DRS_SELL_BTN", OBJPROP_TEXT, "SELL");
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_BGCOLOR, clrFireBrick);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "DRS_SELL_BTN", OBJPROP_CORNER, CORNER_LEFT_UPPER);
}