Problem Description
Solution
Here is a complete, fully functional Expert Advisor (trading robot) for MetaTrader 4 based on the Relative Vigor Index (RVI) crossover strategy.
Strategy Logic
- Indicator: Relative Vigor Index (RVI).
- Buy Signal: The RVI Main line crosses above the RVI Signal line.
- Sell Signal: The RVI Main line crosses below the RVI Signal line.
- Execution: The robot checks for these crossovers on the close of the previous bar (Shift 1) to ensure the signal is confirmed and does not repaint.
- Risk Management: It uses fixed Stop Loss and Take Profit levels. It only allows one open trade at a time per symbol to manage risk.
MQL4 Code
//+------------------------------------------------------------------+
//| RVI_Trader.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int RVI_Period = 10; // RVI Period
input double LotSize = 0.1; // Fixed Lot Size
input int StopLoss = 50; // Stop Loss in points
input int TakeProfit = 100; // Take Profit in points
input int MagicNumber = 123456; // Magic Number to identify orders
input int Slippage = 3; // Maximum Slippage in points
//--- Global Variables
int g_ticket = 0;
double g_point;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust point value for 3/5 digit brokers
if(Digits == 3 || Digits == 5)
g_point = Point * 10;
else
g_point = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up if necessary
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Basic Checks
if(!IsTradeAllowed()) return;
if(Bars < RVI_Period) return;
// 2. Check for existing open positions for this EA
if(CountOpenTrades() > 0) return; // Only allow one trade at a time
// 3. Calculate RVI Values
// We look at Shift 1 (Previous closed bar) and Shift 2 (Bar before that)
// to detect a crossover that has just completed.
// Main Line
double rviMain1 = iRVI(NULL, 0, RVI_Period, MODE_MAIN, 1);
double rviMain2 = iRVI(NULL, 0, RVI_Period, MODE_MAIN, 2);
// Signal Line
double rviSig1 = iRVI(NULL, 0, RVI_Period, MODE_SIGNAL, 1);
double rviSig2 = iRVI(NULL, 0, RVI_Period, MODE_SIGNAL, 2);
// 4. Determine Signals
bool buySignal = (rviMain2 < rviSig2) && (rviMain1 > rviSig1); // Cross Up
bool sellSignal = (rviMain2 > rviSig2) && (rviMain1 < rviSig1); // Cross Down
// 5. Execute Trades
if(buySignal)
{
OpenBuy();
}
else if(sellSignal)
{
OpenSell();
}
}
//+------------------------------------------------------------------+
//| Helper: Count Open Trades for this Magic Number |
//+------------------------------------------------------------------+
int CountOpenTrades()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Helper: Open Buy Order |
//+------------------------------------------------------------------+
void OpenBuy()
{
double sl = 0, tp = 0;
if(StopLoss > 0) sl = Ask - StopLoss * g_point;
if(TakeProfit > 0) tp = Ask + TakeProfit * g_point;
// Normalize prices
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
g_ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, tp, "RVI Buy", MagicNumber, 0, clrBlue);
if(g_ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Helper: Open Sell Order |
//+------------------------------------------------------------------+
void OpenSell()
{
double sl = 0, tp = 0;
if(StopLoss > 0) sl = Bid + StopLoss * g_point;
if(TakeProfit > 0) tp = Bid - TakeProfit * g_point;
// Normalize prices
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
g_ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, tp, "RVI Sell", MagicNumber, 0, clrRed);
if(g_ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
How to Install and Use
- Open MetaTrader 4.
- Press
F4to open the MetaEditor. - Click New -> Expert Advisor (template) -> Name it "RVI_Trader".
- Delete everything in the new file and paste the code provided above.
- Press
F7or click Compile. Ensure there are no errors in the "Errors" tab at the bottom. - Return to the main MT4 window.
- Find "RVI_Trader" in the Navigator panel under Expert Advisors.
- Drag it onto a chart (e.g., EURUSD H1).
- Ensure the "Allow live trading" checkbox is ticked in the Common tab.
- Ensure the AutoTrading button on the top toolbar is green.
Q&A: RVI Trading Strategy
Q: What is the best timeframe for this RVI strategy?
A: The RVI is a momentum oscillator. It typically performs best on H1 (1-hour) and H4 (4-hour) timeframes where price noise is reduced. On M1 or M5, you may experience many false signals (whipsaws).
Q: How does the robot handle 5-digit brokers?
A: The code includes a logic block in OnInit() that detects if the broker uses 3 or 5 digits. It adjusts the g_point variable automatically so that if you input a Stop Loss of 50, it remains 50 pips (500 points) on a 5-digit broker, ensuring consistent risk management.
Q: Can I run this on multiple currency pairs simultaneously?
A: Yes. However, you should ensure that you assign a unique MagicNumber for each chart if you want to track performance separately, or simply leave it as is (the code checks OrderSymbol(), so it won't confuse trades between EURUSD and GBPUSD even with the same Magic Number).
Q: Why does the robot check Shift 1 and Shift 2 instead of Shift 0?
A: Shift 0 represents the currently forming candle. The RVI values on the current candle change with every tick. If we traded on Shift 0, the signal could appear and disappear before the candle closes (repainting). By checking Shift 1 (the last closed candle) and Shift 2, we ensure the crossover is confirmed and permanent.