Problem Description
Solution
Here is a complete, fully functional Expert Advisor (EA) based on the Relative Vigor Index (RVI) for MetaTrader 4.
Strategy Logic
This EA uses the classic RVI crossover strategy:
- Buy Signal: The RVI Main line crosses above the Signal line.
- Sell Signal: The RVI Main line crosses below the Signal line.
- Execution: The EA checks for crossovers on closed bars (indices 1 and 2) to prevent "repainting" (signals flickering while the current candle is forming).
MQL4 Code
//+------------------------------------------------------------------+
//| RVI_EA.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 string Inp_Settings = "--- Strategy Settings ---"; // .
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 ID
input int Slippage = 3; // Max Slippage (in points)
input bool CloseOpposite = true; // Close opposite trade on new signal
//--- Global Variables
double ppoint; // Adjusted point value for 3/5 digit brokers
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust point for 5-digit and 3-digit brokers
if(Digits == 5 || Digits == 3)
ppoint = Point * 10;
else
ppoint = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up if necessary
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if we have enough bars to calculate
if(Bars < RVI_Period + 2) return;
// --- 1. Get RVI Values ---
// We use shift 1 (previous closed bar) and shift 2 (bar before that)
// to detect a confirmed crossover.
// Main Line
double rvi_main_curr = iRVI(NULL, 0, RVI_Period, MODE_MAIN, 1);
double rvi_main_prev = iRVI(NULL, 0, RVI_Period, MODE_MAIN, 2);
// Signal Line
double rvi_sig_curr = iRVI(NULL, 0, RVI_Period, MODE_SIGNAL, 1);
double rvi_sig_prev = iRVI(NULL, 0, RVI_Period, MODE_SIGNAL, 2);
// --- 2. Determine Signals ---
bool isBuySignal = (rvi_main_prev < rvi_sig_prev) && (rvi_main_curr > rvi_sig_curr);
bool isSellSignal = (rvi_main_prev > rvi_sig_prev) && (rvi_main_curr < rvi_sig_curr);
// --- 3. Trade Management ---
// Check existing orders
int total = OrdersTotal();
bool buyOpen = false;
bool sellOpen = false;
for(int i = 0; i < total; i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY) buyOpen = true;
if(OrderType() == OP_SELL) sellOpen = true;
// Close Opposite Logic
if(CloseOpposite)
{
if(OrderType() == OP_BUY && isSellSignal)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrRed);
if(closed) buyOpen = false;
}
if(OrderType() == OP_SELL && isBuySignal)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrBlue);
if(closed) sellOpen = false;
}
}
}
}
}
// --- 4. Execute New Trades ---
// Open Buy
if(isBuySignal && !buyOpen)
{
double sl = (StopLoss > 0) ? Ask - StopLoss * ppoint : 0;
double tp = (TakeProfit > 0) ? Ask + TakeProfit * ppoint : 0;
// Normalize prices
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, tp, "RVI Buy", MagicNumber, 0, clrGreen);
if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
}
// Open Sell
if(isSellSignal && !sellOpen)
{
double sl = (StopLoss > 0) ? Bid + StopLoss * ppoint : 0;
double tp = (TakeProfit > 0) ? Bid - TakeProfit * ppoint : 0;
// Normalize prices
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, tp, "RVI Sell", MagicNumber, 0, clrRed);
if(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_EA".
- Delete everything in the new file and paste the code above.
- Press
F7to Compile. Ensure there are no errors. - Go back to the MT4 Terminal.
- Find "RVI_EA" in the Navigator window under Expert Advisors.
- Drag it onto a chart (e.g., EURUSD H1).
- Ensure "Allow Live Trading" is checked in the Common tab and the AutoTrading button at the top of MT4 is Green.
Key Features of this Code
- 5-Digit Broker Support: The code automatically detects if your broker uses 5 decimal places (points) or 4, ensuring Stop Loss and Take Profit are calculated correctly.
- No Repainting: It uses
shift 1andshift 2. This means it looks at the completed candle. It will not open a trade mid-candle if the lines cross temporarily and then un-cross before the close. - Magic Number: It uses a unique ID (
MagicNumber) so it only manages its own trades and ignores manual trades or trades from other EAs. - Close Opposite: If
CloseOppositeis set totrue, a Buy signal will automatically close any existing Sell trades managed by this EA before opening the Buy, and vice versa.
Q&A: Quantitative Strategy & MQL4
Q: Why use the RVI indicator for algorithmic trading?
A: The Relative Vigor Index is essentially an energy oscillator. In quantitative analysis, it assumes that in a bullish market, the closing price is generally higher than the opening price. It smooths this data using Simple Moving Averages (SMA). It is useful for identifying trend reversals when the RVI diverges from price action or when the signal lines cross.
Q: Why does the code use iRVI(..., MODE_MAIN, ...) and MODE_SIGNAL?
A: The iRVI function in MQL4 returns two different data buffers. MODE_MAIN (index 0) returns the calculated RVI value, while MODE_SIGNAL (index 1) returns the 4-period weighted moving average of that RVI line. The crossover of these two lines generates the trade signal.
Q: How can I optimize this EA?
A: You can use the MT4 Strategy Tester (Ctrl+R). Select this EA and set "Optimization" to true. You can then optimize the RVI_Period, StopLoss, and TakeProfit variables to find the best combination for a specific currency pair and timeframe.
Q: What is the risk of using this EA on a live account?
A: This is a trend-following/reversal oscillator strategy. In ranging (sideways) markets, crossover strategies often generate false signals (whipsaws), leading to consecutive losses. It is recommended to add a trend filter (like a 200-period Moving Average) to only take Buy signals when the price is above the MA and Sell signals when below.