Generate code in 3 minutes
Enter your idea and AI generates runnable code.
Here is a complete, fully commented, and functional MQL4 Expert Advisor (trading robot) based on the Williams' Percent Range (WPR) indicator.
The Williams' Percent Range oscillates between 0 and -100.
//+------------------------------------------------------------------+
//| SimpleWPR.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int WPR_Period = 14; // Williams' Percent Range Period
input double Overbought_Level = -20.0; // Level to trigger Sell (e.g., -20)
input double Oversold_Level = -80.0; // Level to trigger Buy (e.g., -80)
input double LotSize = 0.1; // Fixed Lot Size
input int StopLoss = 50; // Stop Loss in points (0 = no SL)
input int TakeProfit = 100; // Take Profit in points (0 = no TP)
input int MagicNumber = 123456; // Unique ID for this EA's trades
input int Slippage = 3; // Max slippage in points
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Basic validation
if(Oversold_Level > Overbought_Level)
{
Alert("Error: Oversold level must be lower than Overbought level (remember WPR is negative).");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Check if we are allowed to trade
if(!IsTradeAllowed()) return;
// 2. Calculate WPR values
// We look at Shift 1 (previous closed candle) and Shift 2 (candle before that)
// to detect a "Crossover" event.
double wpr1 = iWPR(NULL, 0, WPR_Period, 1);
double wpr2 = iWPR(NULL, 0, WPR_Period, 2);
// 3. Check for Buy Signal
// Logic: WPR was below -80 (Oversold) and has now crossed above -80
if(wpr2 < Oversold_Level && wpr1 > Oversold_Level)
{
// Check if we already have a Buy position to avoid stacking
if(CountOrders(OP_BUY) == 0)
{
// Close any existing Sell positions first (Reversal logic)
CloseAllOrders(OP_SELL);
// Open Buy
OpenTrade(OP_BUY);
}
}
// 4. Check for Sell Signal
// Logic: WPR was above -20 (Overbought) and has now crossed below -20
if(wpr2 > Overbought_Level && wpr1 < Overbought_Level)
{
// Check if we already have a Sell position
if(CountOrders(OP_SELL) == 0)
{
// Close any existing Buy positions first
CloseAllOrders(OP_BUY);
// Open Sell
OpenTrade(OP_SELL);
}
}
}
//+------------------------------------------------------------------+
//| Helper: Open a Trade |
//+------------------------------------------------------------------+
void OpenTrade(int type)
{
double price, sl, tp;
color cmdColor;
if(type == OP_BUY)
{
price = Ask;
// Calculate SL/TP for Buy
sl = (StopLoss > 0) ? price - StopLoss * Point : 0;
tp = (TakeProfit > 0) ? price + TakeProfit * Point : 0;
cmdColor = clrBlue;
}
else // OP_SELL
{
price = Bid;
// Calculate SL/TP for Sell
sl = (StopLoss > 0) ? price + StopLoss * Point : 0;
tp = (TakeProfit > 0) ? price - TakeProfit * Point : 0;
cmdColor = clrRed;
}
// Send the order
int ticket = OrderSend(Symbol(), type, LotSize, price, Slippage, sl, tp, "WPR Bot", MagicNumber, 0, cmdColor);
if(ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Helper: Count Open Orders by Type |
//+------------------------------------------------------------------+
int CountOrders(int type)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// Filter by Symbol, Magic Number, and Type
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == type)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Helper: Close All Orders of a Specific Type |
//+------------------------------------------------------------------+
void CloseAllOrders(int type)
{
for(int i = OrdersTotal() - 1; i >= 0; i--) // Loop backwards when closing
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == type)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
bool res = OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrGray);
if(!res) Print("Failed to close order: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
SimpleWPR and click Next/Finish.SimpleWPR in the Navigator panel (under Expert Advisors).MagicNumber) so it only manages its own trades and ignores manual trades or other robots running on the same account.Q: Why use WPR over RSI for this strategy?
A: Williams' Percent Range is very similar to the Stochastic Oscillator. It is often more sensitive to price spikes than RSI. While RSI measures the speed and change of price movements, WPR measures the current close relative to the high-low range over a set period. This makes WPR excellent for identifying immediate reversal points in ranging markets.
Q: How can I optimize this strategy?
A: You can use the MT4 Strategy Tester (Ctrl+R). Set the "Optimization" checkbox and select the WPR_Period, StopLoss, and TakeProfit parameters. The optimizer will run thousands of combinations to find which settings yielded the best profit factor for your specific currency pair and timeframe.
Q: What is the risk of using this robot on a trending market?
A: Oscillators like WPR are "mean-reverting." In a strong uptrend, the indicator can stay "Overbought" (above -20) for a long time. This robot might sell too early (trying to catch a top) and get stopped out as the trend continues up. To fix this, quantitative traders often add a "Trend Filter" (like a 200-period Moving Average) and only take Buy signals if the price is above the Moving Average.