Problem Description
Solution
Here is a complete, fully functional Expert Advisor (trading bot) for MetaTrader 4 written in MQL4.
Strategy Description
This bot uses a Signal Line Crossover strategy involving the On-Balance Volume (OBV). Since OBV is a cumulative indicator without fixed bounds, raw values alone are hard to trade.
- The Indicator: We calculate the standard On-Balance Volume.
- The Signal Line: We calculate a Simple Moving Average (SMA) of the OBV values over a specific period.
- Buy Signal: When the OBV line crosses above its Moving Average.
- Sell Signal: When the OBV line crosses below its Moving Average.
MQL4 Code
//+------------------------------------------------------------------+
//| OBV_Trader.mq4 |
//| Copyright 2023, Quantitative Trading AI |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int MagicNumber = 123456; // Unique ID for this EA's orders
input double LotSize = 0.1; // Fixed Lot Size
input int StopLoss = 50; // Stop Loss in points (0 = none)
input int TakeProfit = 100; // Take Profit in points (0 = none)
input int Slippage = 3; // Max slippage in points
input int OBV_MA_Period = 20; // Period for the OBV Smoothing (Signal Line)
//--- Global Variables
double g_point; // Adjusted point value for 3/5 digit brokers
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust point for 5-digit brokers (0.00001) vs 4-digit (0.0001)
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(Bars < OBV_MA_Period + 2) return; // Not enough history
if(!IsTradeAllowed()) return; // Trading not allowed
// 2. Check for Open Orders
// For this simple bot, we only allow one trade at a time per symbol
if(CountOpenOrders() > 0) return;
// 3. Calculate Indicators
// We need the OBV and the MA of the OBV for the current candle (shift 1) and previous (shift 2)
// We use shift 1 (closed bar) to avoid repainting signals on the current forming bar
double obv_curr = iOBV(NULL, 0, PRICE_CLOSE, 1);
double obv_prev = iOBV(NULL, 0, PRICE_CLOSE, 2);
double ma_obv_curr = CalculateMAofOBV(1, OBV_MA_Period);
double ma_obv_prev = CalculateMAofOBV(2, OBV_MA_Period);
// 4. Trading Logic (Crossover)
// Buy Signal: OBV crosses ABOVE the MA
if(obv_prev <= ma_obv_prev && obv_curr > ma_obv_curr)
{
OpenBuyOrder();
}
// Sell Signal: OBV crosses BELOW the MA
if(obv_prev >= ma_obv_prev && obv_curr < ma_obv_curr)
{
OpenSellOrder();
}
}
//+------------------------------------------------------------------+
//| Helper: Calculate Simple Moving Average of OBV |
//+------------------------------------------------------------------+
double CalculateMAofOBV(int shift, int period)
{
double sum = 0;
for(int i = 0; i < period; i++)
{
sum += iOBV(NULL, 0, PRICE_CLOSE, shift + i);
}
return(sum / period);
}
//+------------------------------------------------------------------+
//| Helper: Count Open Orders for this EA |
//+------------------------------------------------------------------+
int CountOpenOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); 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 OpenBuyOrder()
{
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);
int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, tp, "OBV Buy", MagicNumber, 0, clrGreen);
if(ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Helper: Open Sell Order |
//+------------------------------------------------------------------+
void OpenSellOrder()
{
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);
int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, tp, "OBV Sell", MagicNumber, 0, clrRed);
if(ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
//+------------------------------------------------------------------+
How to Install and Use
- Open MetaEditor: In MT4, press
F4or click the "MetaEditor" button. - Create New File: Click "New" -> "Expert Advisor (template)" -> Name it
OBV_Trader. - Paste Code: Delete everything in the new file and paste the code provided above.
- Compile: Press
F7or click "Compile". Ensure there are no errors in the "Errors" tab at the bottom. - Attach to Chart: Go back to the MT4 terminal. Find
OBV_Traderin the "Navigator" window under "Expert Advisors". Drag it onto a chart (e.g., EURUSD H1). - Configure: In the settings window, ensure "Allow live trading" is checked in the "Common" tab. You can adjust
LotSize,StopLoss, orOBV_MA_Periodin the "Inputs" tab.
Key Features of This Code
- Signal Smoothing: It calculates a Moving Average of the OBV manually (
CalculateMAofOBV). This is crucial because raw OBV is very jagged; the crossover method filters out market noise. - Bar Closing Logic: The bot uses
shift 1(the last closed candle) andshift 2(the candle before that) to generate signals. This prevents "repainting," where a signal appears and disappears while the current candle is still moving. - Broker Compatibility: It includes logic (
g_point) to automatically adjust Stop Loss and Take Profit calculations for 5-digit brokers (which is the standard for most modern forex brokers). - Risk Management: Includes inputs for Stop Loss and Take Profit to protect capital.
Q&A
Q: Why use a Moving Average on OBV instead of just raw OBV?
A: Raw OBV is cumulative. It simply goes up or down based on volume. A Moving Average acts as a dynamic trend line. When OBV crosses above its average, it indicates volume momentum is increasing relative to the recent past, providing a clearer entry signal.
Q: Can I use this on any timeframe?
A: Yes, but OBV is volume-based. It tends to work better on timeframes where volume data is significant, such as H1, H4, or Daily. On M1 or M5, volume noise might generate many false signals.
Q: Does this bot handle money management?
A: It uses a fixed LotSize. To add dynamic money management (e.g., risking 1% of equity), the LotSize calculation logic would need to be updated to calculate lot size based on AccountBalance().