🚀 AI One-Click Generation of mql4 Strategy Code

Mql4 Strategy Guide: Trading with the On-Balance Volume (OBV) Indicator

mql4 | Published on: 2025/12/3
The following content is generated by EasyQuant .

Problem Description

Please create a trading bot that uses the On-Balance Volume indicator to make trading decisions.

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.

  1. The Indicator: We calculate the standard On-Balance Volume.
  2. The Signal Line: We calculate a Simple Moving Average (SMA) of the OBV values over a specific period.
  3. Buy Signal: When the OBV line crosses above its Moving Average.
  4. 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

  1. Open MetaEditor: In MT4, press F4 or click the "MetaEditor" button.
  2. Create New File: Click "New" -> "Expert Advisor (template)" -> Name it OBV_Trader.
  3. Paste Code: Delete everything in the new file and paste the code provided above.
  4. Compile: Press F7 or click "Compile". Ensure there are no errors in the "Errors" tab at the bottom.
  5. Attach to Chart: Go back to the MT4 terminal. Find OBV_Trader in the "Navigator" window under "Expert Advisors". Drag it onto a chart (e.g., EURUSD H1).
  6. Configure: In the settings window, ensure "Allow live trading" is checked in the "Common" tab. You can adjust LotSize, StopLoss, or OBV_MA_Period in 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) and shift 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().