Problem Description
Solution
To build a trading robot that adds to a position when it becomes profitable (often called Pyramiding or Scaling In), you need a strategy that monitors the price distance from your last entry.
Here is a complete, fully commented MQL4 Expert Advisor (EA) that implements this logic.
Strategy Logic
- Initial Entry: Enters a trade based on a simple Moving Average crossover (you can replace this with your own entry logic).
- Scaling In: If a position exists and the price moves in favor of the trade by a specific number of pips (
GridStep), it opens a new order. - Limits: It stops adding trades once
MaxOrdersis reached. - Risk Management: It applies Stop Loss and Take Profit to every new order.
MQL4 Code
//+------------------------------------------------------------------+
//| PyramidingRobot.mq4 |
//| Copyright 2023, Quantitative AI Assistant |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI Assistant"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input string Label1 = "--- Volume Settings ---";
input double BaseLots = 0.1; // Initial trade volume
input double ScalingLots = 0.1; // Volume for subsequent trades
input int MaxOrders = 5; // Maximum number of allowed open orders
input string Label2 = "--- Grid Settings ---";
input int GridStepPips = 20; // Distance in pips to add new trade (Profit required)
input int StopLossPips = 50; // Stop Loss in pips
input int TakeProfitPips = 100; // Take Profit in pips
input string Label3 = "--- Strategy Settings ---";
input int FastMA = 10; // Fast Moving Average Period
input int SlowMA = 20; // Slow Moving Average Period
input int MagicNumber = 123456; // Unique identifier for this EA
input int Slippage = 3; // Max slippage allowed
//--- Global Variables
double pipPoint;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust pip value for 3/5 digit brokers
if(Digits == 3 || Digits == 5) pipPoint = Point * 10;
else pipPoint = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Check if trading is allowed
if(!IsTradeAllowed()) return;
// 2. Analyze current market state
int totalOrders = 0;
int lastType = -1;
double lastOpenPrice = 0;
// Loop through orders to find existing positions for this EA
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalOrders++;
// We want the price of the most recently opened order (highest ticket usually,
// but logic here assumes the one furthest in trend direction for grid calculation)
// For simplicity, we track the last selected order in the loop.
lastType = OrderType();
lastOpenPrice = OrderOpenPrice();
// To be precise for pyramiding, we usually want the 'best' price in the trend direction
// But standard loop usually finds the oldest first. Let's refine logic below.
}
}
}
// Refine Last Open Price: We need the entry price of the LAST added trade to calculate the step
if(totalOrders > 0)
{
FindLastOrderInfo(lastType, lastOpenPrice);
}
// 3. Entry Logic (If no orders exist)
if(totalOrders == 0)
{
double maFast = iMA(NULL, 0, FastMA, 0, MODE_SMA, PRICE_CLOSE, 1);
double maSlow = iMA(NULL, 0, SlowMA, 0, MODE_SMA, PRICE_CLOSE, 1);
double maFastPrev = iMA(NULL, 0, FastMA, 0, MODE_SMA, PRICE_CLOSE, 2);
double maSlowPrev = iMA(NULL, 0, SlowMA, 0, MODE_SMA, PRICE_CLOSE, 2);
// Buy Signal: Fast MA crosses above Slow MA
if(maFast > maSlow && maFastPrev <= maSlowPrev)
{
OpenTrade(OP_BUY, BaseLots);
}
// Sell Signal: Fast MA crosses below Slow MA
else if(maFast < maSlow && maFastPrev >= maSlowPrev)
{
OpenTrade(OP_SELL, BaseLots);
}
}
// 4. Pyramiding Logic (If orders exist and limit not reached)
else if(totalOrders < MaxOrders)
{
// Check for BUY scaling
if(lastType == OP_BUY)
{
// Current Bid price must be higher than last entry + grid step
if(Bid >= lastOpenPrice + (GridStepPips * pipPoint))
{
OpenTrade(OP_BUY, ScalingLots);
}
}
// Check for SELL scaling
else if(lastType == OP_SELL)
{
// Current Ask price must be lower than last entry - grid step
if(Ask <= lastOpenPrice - (GridStepPips * pipPoint))
{
OpenTrade(OP_SELL, ScalingLots);
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Find the Open Price of the most recent order |
//+------------------------------------------------------------------+
void FindLastOrderInfo(int &type, double &price)
{
datetime lastTime = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// We look for the order with the most recent Open Time
if(OrderOpenTime() > lastTime)
{
lastTime = OrderOpenTime();
type = OrderType();
price = OrderOpenPrice();
}
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Open a trade |
//+------------------------------------------------------------------+
void OpenTrade(int type, double volume)
{
double price, sl, tp;
color arrowColor;
if(type == OP_BUY)
{
price = Ask;
sl = (StopLossPips > 0) ? price - StopLossPips * pipPoint : 0;
tp = (TakeProfitPips > 0) ? price + TakeProfitPips * pipPoint : 0;
arrowColor = clrBlue;
}
else
{
price = Bid;
sl = (StopLossPips > 0) ? price + StopLossPips * pipPoint : 0;
tp = (TakeProfitPips > 0) ? price - TakeProfitPips * pipPoint : 0;
arrowColor = clrRed;
}
int ticket = OrderSend(Symbol(), type, volume, price, Slippage, sl, tp, "Pyramiding EA", MagicNumber, 0, arrowColor);
if(ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
How It Works
OnInit: It calculates the value of a "pip" depending on whether your broker uses 4 or 5 digits (e.g., 0.0001 vs 0.00001).OnTick: This function runs every time the price changes.- It counts how many orders are currently open for this specific strategy (
MagicNumber). - It finds the Last Open Price using the
FindLastOrderInfohelper function. This ensures we calculate the distance from the most recent trade added, not the very first one.
- It counts how many orders are currently open for this specific strategy (
- Entry Logic: If there are 0 orders, it checks for a Moving Average crossover. If a cross happens, it opens the
BaseLotssize. - Scaling Logic:
- If we have a BUY position: It checks if the current
Bidprice is higher than theLastOpenPriceplus theGridStepPips. If yes, it means the trade is profitable by the required amount, so it opens a new Buy order withScalingLots. - If we have a SELL position: It checks if the current
Askprice is lower than theLastOpenPriceminus theGridStepPips.
- If we have a BUY position: It checks if the current
- Safety: It checks
MaxOrdersto ensure you don't open too many trades and exhaust your margin.
How to Use This
- Open MetaEditor in MT4 (Press F4).
- Click New -> Expert Advisor (template) -> Name it "PyramidingRobot".
- Paste the code above into the file, replacing everything.
- Click Compile.
- Go back to the MT4 terminal, find the EA in the Navigator, and drag it onto a chart.
Important Considerations for Pyramiding
- Break Even: When pyramiding, it is highly recommended to implement a "Break Even" or "Trailing Stop" logic. As you add more lots, a sudden reversal can turn a large profit into a large loss very quickly.
- Margin: Ensure your account has enough leverage and balance. Adding lots increases margin requirements.
- Trend: This strategy works best in strong trending markets. In ranging markets, it may trigger the first trade but fail to trigger scaling trades, or trigger them just before the price reverses.
Q&A
Q: How can I change the entry signal?
A: Look for the section // 3. Entry Logic in the OnTick function. Replace the Moving Average logic with your own indicator calls (e.g., RSI, MACD).
Q: Does this robot close trades automatically?
A: Yes, individual trades will close when they hit their specific TakeProfit or StopLoss. It does not currently have a "Close All" function based on total equity.
Q: What happens if the terminal restarts?
A: The EA uses MagicNumber to identify its trades. If you restart the terminal, it will find the existing trades and continue managing the scaling logic correctly.