Friday, January 24, 2025

Author: Roberto Jacobs (3rjfx) | Featured on Forex Home Expert

In this article, I will discuss the creation of the On Balance Volume Multi Timeframe indicator for MT5. This indicator will calculate the comparison of OBV movements in percentage terms across 21 different timeframes, ranging from M1 to MN1.

The On Balance Volume Multi-Timeframe indicator for MT5 (OBV_MTF) utilizes a template and displays a panel on the chart. Its function, operation, and usage are identical to the Strength of Price Movement Multi Timeframe indicator (SPM_MTF) as detailed in the previous article. For a comprehensive guide on creating a multi-timeframe indicator, you can refer to the following resources: Strength of Price Movement Multi Timeframe indicator for MT5

Introduction of On-Balance Volume Multi-Time Frame (OBV_MTF) Indicator

The primary function of the On-Balance Volume Multi-Time Frame (OBV_MTF) indicator is to enhance the traditional On-Balance Volume (OBV) indicator by incorporating analysis across multiple time frames. This approach offers a more comprehensive view of market trends and volume dynamics.

USDCHFH1_OBV_MTF

Key Components and Functions:

  • 1. Volume Analysis: The OBV_MTF indicator utilizes trading volume as a critical parameter. By adding the volume on up days and subtracting the volume on down days, it creates a cumulative total that helps identify the strength of price movements.
  • 2. Multi-Time Frame Analysis: Unlike the traditional OBV, which operates on a single time frame, the OBV_MTF indicator aggregates volume data from multiple time frames. This broader perspective allows traders to detect longer-term trends that might be overlooked when only considering one time frame.
  • 3. Trend Confirmation: The OBV_MTF indicator aids in confirming trends by comparing OBV values across different time frames. A rising OBV across multiple time frames typically indicates strong buying pressure, while a falling OBV suggests selling pressure.
  • 4. Divergence Detection: The OBV_MTF indicator can identify divergences between price movements and volume. For instance, if prices are rising but OBV is falling across multiple time frames, it could signal a potential reversal.
  • 5. Customizable Parameters: This indicator often includes customizable settings, allowing traders to adjust time frames and other parameters to fit their trading strategies and preferences.
  • 6. Visual Representation: The OBV_MTF indicator provides visual cues, such as arrows and colors, to highlight significant changes in volume and trends, making it easier for traders to interpret the data.
  • 7. Buffering and Storage: It maintains buffers for open, high, low, close prices, and tickvolume data. It also keeps track of time and various display settings to ensure accurate and timely updates.

Before explaining the differences between the SPM_MTF indicator and the OBV_MTF indicator, it is important to note that all functions for running this multi-timeframe indicator are managed by the class program for the MTF indicator, which is contained in the MTF_Indi class.

Class for MTF indicator:


//+------------------------------------------------------------------+
//| class for MTF indicator                                          |
//+------------------------------------------------------------------+
class MTF_Indi
  {
   //---
   public:
   //--
   int               fbar;
   int               star,
                     tstar,
                     bstar;
   int               TNamex,
                     TNamexn,
                     TNamey1,
                     TNamey2,
                     TNamey3,
                     TNamey1n,
                     TNamey2n,
                     TNamey3n;
   int               tfxar;
   int               up,dw;
   int               tfhalf;
   int               maxbar;
   int               ttlbars;
   int               scaleX,
                     scaleA,
                     scaleY,
                     horizL1,
                     horizL2,
                     horizL3,
                     horizL4,
                     horizL5,
                     horizL6,
                     vertiL1,
                     vertiL2,
                     vertiL3,
                     vertiL4,
                     vertiL5,
                     vertiL6,
                     offsetX,
                     offsetY,
                     fontSize,
                     windchar,
                     windsize;
   int               corx,
                     cory,
                     txttf;
   int               curmin,
                     prvmin;
   int               corpos;
   int               pospos,
                     postop,
                     posbot;
   int               curAlert;
   int               prvAlert;
   //--
   long              CI;
   string            posisi,
                     sigpos,
                     indname,
                     msgText,
                     ObjName;
   string            cstar,
                     artop,
                     arbot;
   string            hName1,
                     hName2,
                     hName3;
   string            font_mode;
   bool              display;
   double            OPEN[],
                     HIGH[],
                     LOW[],
                     CLOSE[];
   long              VOLUME[];
   datetime          TIME[];
   datetime          cbartime;
   //--
   int               year,  // Year
                     mon,   // Month
                     day,   // Day
                     hour,  // Hour
                     min,   // Minutes
                     sec,   // Seconds
                     dow,   // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
                     doy;   // Day number of the year (January 1st is assigned the number value of zero)
   //---- buffers
   double            PowerMove[];
   string            TFSc[];
   color             Arwcolor[];
   color             TColor;
   ENUM_TIMEFRAMES   TFId[];
   ENUM_BASE_CORNER  bcor;
   ENUM_ANCHOR_POINT ancp;
   //---
   //---
   //- Constructor
                     MTF_Indi(void):
                     year(0),
                     mon(1),
                     day(2),
                     hour(3),
                     min(4),
                     sec(5),
                     dow(6),
                     doy(7),
                     fbar(125),
                     star(181),
                     maxbar(3),
                     pospos(0),
                     postop(0),
                     posbot(1),
                     tstar(217),
                     bstar(218),
                     tfhalf(11),
                     scaleX(35),
                     scaleA(36),
                     scaleY(50),
                     offsetY(18),
                     offsetX(120),
                     fontSize(7),
                     cbartime(0),
                     posisi(""),
                     sigpos(""),
                     msgText(""),
                     curAlert(0),
                     prvAlert(0),
                     ttlbars(125),
                     windsize(12),
                     windchar(108),
                     CI(ChartID()),
                     display(false),
                     ObjName("OBV_"),
                     font_mode(FontsModel(f_model)),
                     cstar(CharToString((uchar)star)),
                     artop(CharToString((uchar)tstar)),
                     arbot(CharToString((uchar)bstar)),
                     indname(MQLInfoString(MQL_PROGRAM_NAME))
     {
     }
   //---
   //- Destructor
                    ~MTF_Indi(void)
     {
     }
   //---

Code Explanation of the MTF_Indi Class:

  • Class Definition:
    • The MTF_Indi class is designed to handle the Multi-Time Frame (MTF) indicator for trading analysis. This class encapsulates various properties and methods that are essential for analyzing and displaying trading data across multiple time frames.
  • Public Members:
    • The class defines numerous public member variables for storing values such as chart coordinates, alert settings, font details, and indicator data buffers. These variables are used throughout the class methods to perform various indicator functions.
  • Key Components and Functions:
    • 1. Public Data Members:
      • fbar: An integer representing the function bar for analysis.
      • star, tstar, bstar: Integers representing specific chart arrow or markers.
      • TNamex, TNamexn, TNamey1, TNamey2, TNamey3, TNamey1n, TNamey2n, TNamey3n: Integers for naming conventions and chart positions.
      • tfxar: An integer for time frame-related calculations.
      • up, dw: Integers for tracking upward and downward movements.
      • tfhalf: An integer representing the midpoint of a time frame.
      • maxbar: An integer for the maximum number of bars considered.
      • ttlbars: An integer for the total number of bars.
      • scaleX, scaleA, scaleY: Integers for scaling the chart in different dimensions.
      • horizL1, horizL2, horizL3, horizL4, horizL5, horizL6: Integers for horizontal line positions.
      • vertiL1, vertiL2, vertiL3, vertiL4, vertiL5, vertiL6: Integers for vertical line positions.
      • offsetX, offsetY: Integers for positional offsets.
      • fontSize: An integer for the font size used in chart displays.
      • windchar, windsize: Integers related to window characteristics.
      • corx, cory: Integers for x and y coordinates.
      • txttf: An integer for text time frame.
    • 2. Time and Alert Variables:
      • curmin, prvmin: Integers for current and previous minutes.
      • corpos: An integer for core position.
      • pospos, postop, posbot: Integers for positional tracking.
      • curAlert, prvAlert: Integers for current and previous alert states.
      • year, mon, day, hour, min, sec, dow, doy: Integers representing date and time components.
    • 3. String Members:
      • posisi, sigpos, indname, msgText, ObjName, cstar, artop, arbot, hName1, hName2, hName3, font_mode: Strings for various naming conventions, messages, and display texts.
    • 4. Boolean Member:
      • display: A boolean flag indicating whether the display is active.
    • 5. Data Buffers:
      • OPEN[], HIGH[], LOW[], CLOSE[]: Arrays for storing open, high, low, and close prices.
      • VOLUME[]: An array for storing trading volume data.
      • TIME[]: An array for storing time data.
      • cbartime: A datetime variable for bar time tracking.
      • PowerMove[]: An array for storing calculated power move values.
      • TFSc[]: An array for storing time frame string components.
      • Arwcolor[]: An array for storing arrow colors.
      • TColor: A variable for text color.
    • 6. Enumerations:
      • TFId[]: An array of enumeration type ENUM_TIMEFRAMES for different time frames.
      • bcor: An enumeration of type ENUM_BASE_CORNER for base corner positions.
      • ancp: An enumeration of type ENUM_ANCHOR_POINT for anchor points.
    • 7. Constructor: The constructor initializes the member variables with default values, ensuring that the indicator is set up correctly when instantiated.
      • MTF_Indi(void): The constructor initializes the class members with default values.
    • 8. Destructor: The destructor is defined but currently does not perform any specific actions.
      • ~MTF_Indi(void): The destructor is provided to clean up resources when the object is destroyed.

    As I explained above, the differences between the On Balance Volume Multi-Timeframe indicator (OBV_MTF) and the Strength of Price Movement Multi Timeframe indicator (SPM_MTF) lie in two key functions:

    1. UpdatePrice(ENUM_TIMEFRAMES xtf):

    • In the SPM_MTF indicator program, the UpdatePrice(ENUM_TIMEFRAMES xtf) function does not perform a TickVolume update because it is not necessary.

    2. OBVDirectionScan(const ENUM_TIMEFRAMES stf, int shift):

    • The SPM_MTF indicator program uses the PriceStrengthScan(const ENUM_TIMEFRAMES stf, int shift) function, which scans the price and compares the current bar's price to the previous bar's price in percentage terms.
    • Conversely, the OBV_MTF indicator scans the movement of the OBV value on the current bar and compares it with the OBV value on the previous bar in percentage terms.

    Additionally, the value of the On Balance Volume indicator in the OBV_MTF indicator is not obtained using the Indicator Handle. Instead, the structure and code of the On Balance Volume indicator only use the closing price plus or minus TickVolume, allowing the OBV indicator code to be easily written directly into the OBVDirectionScan() function.

    Explanation of the UpdatePrice() Function in the SPM_MTF Indicator.

    The UpdatePrice() function in the Strength of Price Movement Multi Timeframe (SPM_MTF) indicator is designed to update price data across various timeframes. Below is a detailed breakdown of each step within this function:

    
       //---
       void              UpdatePrice(ENUM_TIMEFRAMES xtf)
         {
          //-- Clear existing arrays
          ArrayFree(OPEN);
          ArrayFree(HIGH);
          ArrayFree(LOW);
          ArrayFree(CLOSE);
          ArrayFree(TIME);
          //-- Resize arrays
          ArrayResize(OPEN,maxbar,maxbar);
          ArrayResize(HIGH,maxbar,maxbar);
          ArrayResize(LOW,maxbar,maxbar);
          ArrayResize(CLOSE,maxbar,maxbar);
          ArrayResize(TIME,maxbar,maxbar);
          //-- Set arrays as series
          ArraySetAsSeries(OPEN,true);
          ArraySetAsSeries(HIGH,true);
          ArraySetAsSeries(LOW,true);
          ArraySetAsSeries(CLOSE,true);
          ArraySetAsSeries(TIME,true);
          //-- Initialize arrays
          ArrayInitialize(OPEN,0.0);
          ArrayInitialize(HIGH,0.0);
          ArrayInitialize(LOW,0.0);
          ArrayInitialize(CLOSE,0.0);
          ArrayInitialize(TIME,0);
          //-- Calculate the number of bars based on the timeframe
          int barx=PeriodSeconds(xtf)/60*maxbar;
          RefreshPrice(PERIOD_M1,maxbar);
          RefreshPrice(xtf,barx);
          //-- Copy price data from the specified symbol and timeframe
          int co=CopyOpen(Symbol(),xtf,0,maxbar,OPEN);
          int ch=CopyHigh(Symbol(),xtf,0,maxbar,HIGH);
          int cl=CopyLow(Symbol(),xtf,0,maxbar,LOW);
          int cc=CopyClose(Symbol(),xtf,0,maxbar,CLOSE);
          int ct=CopyTime(Symbol(),xtf,0,maxbar,TIME);
          //--
          return;
          //---
         } //-end UpdatePrice()
       //---
    

    Steps in the UpdatePrice() Function in the SPM_MTF Indicator:

    • 1. Clear Existing Arrays:
      • The ArrayFree() function is used to clear the existing data in the OPEN, HIGH, LOW, CLOSE, and TIME arrays.
    • 2. Resize Arrays:
      • ArrayResize() function adjusts the size of these arrays based on the maximum number of bars (maxbar).
    • 3, Set Arrays as Series:
      • ArraySetAsSeries() sets the arrays as series, meaning that the most recent element is at index 0.
    • 4. Initialize Arrays:
      • ArrayInitialize() initializes the array values. The initial values are set to 0.0 for prices and 0 for time.
    • 5. Calculate the Number of Bars:
      • int barx = PeriodSeconds(xtf) / 60 * maxbar; calculates the number of bars required based on the specified timeframe (xtf).
    • 6. Refresh Price Data:
      • RefreshPrice(PERIOD_M1, maxbar); and RefreshPrice(xtf, barx); refresh the price data for the M1 timeframe and the specified timeframe.
    • 7. Copy Price Data:
      • CopyOpen(), CopyHigh(), CopyLow(), CopyClose(), and CopyTime() functions copy the open, high, low, close, and time data into their respective arrays.

    This function helps update and manage price data for further analysis across multiple timeframes without the need for TickVolume updates, as they are not relevant to the SPM_MTF indicator.

    Explanation of the UpdatePrice() Function in the OBV_MTF Indicator.

    The UpdatePrice() function in the On Balance Volume Multi-Timeframe (OBV_MTF) indicator is designed to update price and TickVolume data across various timeframes. Below is a detailed breakdown of each step within this function:

    
       //---
       void              UpdatePrice(ENUM_TIMEFRAMES xtf)
         {
          maxbar=fbar;
          //-- Clear existing arrays
          ArrayFree(OPEN);
          ArrayFree(HIGH);
          ArrayFree(LOW);
          ArrayFree(CLOSE);
          ArrayFree(TIME);
          ArrayFree(VOLUME);
          //-- Resize arrays
          ArrayResize(OPEN,maxbar,maxbar);
          ArrayResize(HIGH,maxbar,maxbar);
          ArrayResize(LOW,maxbar,maxbar);
          ArrayResize(CLOSE,maxbar,maxbar);
          ArrayResize(TIME,maxbar,maxbar);
          ArrayResize(VOLUME,maxbar,maxbar);
          //-- Set arrays as series
          ArraySetAsSeries(OPEN,true);
          ArraySetAsSeries(HIGH,true);
          ArraySetAsSeries(LOW,true);
          ArraySetAsSeries(CLOSE,true);
          ArraySetAsSeries(TIME,true);
          ArraySetAsSeries(VOLUME,true);
          //-- Initialize arrays
          ArrayInitialize(OPEN,0.0);
          ArrayInitialize(HIGH,0.0);
          ArrayInitialize(LOW,0.0);
          ArrayInitialize(CLOSE,0.0);
          ArrayInitialize(TIME,0);
          ArrayInitialize(VOLUME,0);
          //-- Calculate the number of bars based on the timeframe
          int barx=PeriodSeconds(xtf)/60*maxbar;
          RefreshPrice(PERIOD_M1,maxbar);
          RefreshPrice(xtf,barx);
          //-- Copy price and TickVolume data from the specified symbol and timeframe
          int co=CopyOpen(Symbol(),xtf,0,maxbar,OPEN);
          int ch=CopyHigh(Symbol(),xtf,0,maxbar,HIGH);
          int cl=CopyLow(Symbol(),xtf,0,maxbar,LOW);
          int cc=CopyClose(Symbol(),xtf,0,maxbar,CLOSE);
          int ct=CopyTime(Symbol(),xtf,0,maxbar,TIME);
          int cv=CopyTickVolume(Symbol(),xtf,0,maxbar,VOLUME);
          //--
          return;
          //---
         } //-end UpdatePrice()
       //---
    

    Steps in the UpdatePrice() Function in the OBV_MTF Indicator:

    • 1. Set Maximum Bars:
      • maxbar = fbar; sets the maximum number of bars to be processed.
    • 2. Clear Existing Arrays:
      • The ArrayFree() function is used to clear the existing data in the OPEN, HIGH, LOW, CLOSE, TIME, and VOLUME arrays.
    • 3. Resize Arrays:
      • ArrayResize() adjusts the size of these arrays based on the maximum number of bars (maxbar).
    • 4. Set Arrays as Series:
      • ArraySetAsSeries() sets the arrays as series, meaning that the most recent element is at index 0.
    • 5. Initialize Arrays:
      • ArrayInitialize() initializes the array values. The initial values are set to 0.0 for prices and TickVolume, and 0 for time.
    • 6. Calculate the Number of Bars:
      • int barx = PeriodSeconds(xtf) / 60 * maxbar; calculates the number of bars required based on the specified timeframe (xtf).
    • 7. Refresh Price Data:
      • RefreshPrice(PERIOD_M1, maxbar); and RefreshPrice(xtf, barx); refresh the price data for the M1 timeframe and the specified timeframe.
    • 8. Copy Price and TickVolume Data:
      • CopyOpen(), CopyHigh(), CopyLow(), CopyClose(), CopyTime(), and CopyTickVolume() is functions copy the open, high, low, close, time, and TickVolume data into their respective arrays.

    Explanation of the PriceStrengthScan() Function in the SPM_MTF Indicator.

    The PriceStrengthScan() function in the Strength of Price Movement Multi Timeframe (SPM_MTF) indicator is designed to scan and compare price data to determine the direction of price movement in percentage terms. Below is a detailed breakdown of each step within this function:

    
    //---
       int               PriceStrengthScan(const ENUM_TIMEFRAMES stf,int shift) // Price Bar Direction in percent
         {
          //-- Initialize return value and direction indicators
          int ret=0;
          int rise=1,
              down=-1;
          //-- Set the number of bars to be scanned
          int br=shift+2;
          double res=0.0;
          double move=0.0;
          //-- Update price data for the specified timeframe
          UpdatePrice(stf);
          //-- Declare and resize the close price array
          double CL[];
          ArrayResize(CL,br,br);
          ArraySetAsSeries(CL,true);
          //-- Populate the close price array with data
          for(int x=br-1; x>=shift; x--)
             CL[x]=CLOSE[x];
          //-- Get the current and previous close prices
          double close_now  = CL[shift];
          double close_prev = CL[shift+1];
          //-- Calculate the percentage change in close prices
          if((close_now==0 || close_now==EMPTY_VALUE) || (close_prev==0 || close_prev==EMPTY_VALUE))
             res=0.0;
          else
             res=NormalizeDouble((close_now / close_prev * 100) - 100,3);
          //-- Adjust the result for percentage comparison
          res=NormalizeDouble(res*100,3); // because its value less than 1 then multiplied with 100.
          //-- Determine the direction of price movement
          if(res>move)
             ret=rise;
          if(res<move)
             ret=down;
          //--
          return(ret);
          //---
         } //-end PriceStrengthScan()
       //---
    

    Steps in the PriceStrengthScan() Function in the SPM_MTF Indicator:

    • 1. Initialization:
      • Initialize the return value (ret) to 0.
      • Define constants for rising and falling prices (rise and down).
    • 2. Set the Number of Bars to Scan:
      • Calculate the number of bars to scan (br) based on the shift parameter.
    • 3. Update Price Data:
      • Call the UpdatePrice(stf) function to update the price data for the specified timeframe (stf).
    • 4.Declare and Resize the Close Price Array:
      • Declare the CL array to store close prices.
      • Resize the CL array to accommodate the number of bars (br) and set it as a series.
    • 5. Populate the Close Price Array:
      • Use a loop to populate the CL array with close prices from the CLOSE array.
    • 6. Get the Current and Previous Close Prices:
      • Retrieve the current close price (close_now) and the previous close price (close_prev) from the CL array.
    • 7. Calculate the Percentage Change in Close Prices:
      • If either close_now or close_prev is 0 or empty, set res to 0.0.
      • Otherwise, calculate the percentage change in close prices and normalize the result.
    • 8. Adjust the Result for Percentage Comparison:
      • Multiply the result by 100 to adjust for percentage comparison.
    • 9. Determine the Direction of Price Movement:
      • Compare the result (res) with move to determine if the price is rising or falling.
      • Set the return value (ret) accordingly.

    This function helps determine the direction and strength of price movement by analyzing the percentage change in close prices across different timeframes.

    Explanation of the OBVDirectionScan() Function in the OBV_MTF Indicator.

    The OBVDirectionScan() function in the On Balance Volume Multi-Timeframe (OBV_MTF) indicator is designed to scan and compare the OBV values to determine the direction of OBV movement in percentage terms. Below is a detailed breakdown of each step within this function:

    
    //---
       int               OBVDirectionScan(const ENUM_TIMEFRAMES stf,int shift) // Scan OBV Direction
         {
          //-- Initialize return value and direction indicators
          int ret=0;
          int rise=1,
              down=-1;
          //-- Set the number of bars to be scanned
          int br=shift+2;
          double res=0.0;
          double move=0.0;
          //-- Update price data for the specified timeframe
          UpdatePrice(stf);
          //-- Declare and resize the OBV array
          double OBV[];
          ArrayResize(OBV,br,br);
          ArraySetAsSeries(OBV,true);
          //-- Retrieve volume and close prices
          double vol=(double)VOLUME[shift];
          double prev_close=CLOSE[shift+1];
          double curr_close=CLOSE[shift];
          //--- Fill OBV Buffer
          if(curr_close<prev_close)
             OBV[shift]=OBV[shift+1]-vol;
          else
            {
             if(curr_close>prev_close)
                OBV[shift]=OBV[shift+1]+vol;
             else
                OBV[shift]=OBV[shift+1];
            }
          //-- Get the current and previous OBV values
          double close_now  = OBV[shift];
          double close_prev = OBV[shift+1];
          //-- Calculate the percentage change in OBV values
          if((close_now==0 || close_now==EMPTY_VALUE) || (close_prev==0 || close_prev==EMPTY_VALUE))
             res=0.0;
          else
             res=NormalizeDouble((close_now / close_prev * 100) - 100,3);
          //-- Adjust the result for percentage comparison
          res=NormalizeDouble(res*100,3); // because its value less than 1 then multiplied with 100.
          //-- Determine the direction of OBV movement
          if(res>move)
             ret=rise;
          if(res<move)
             ret=down;
          //--
          return(ret);
          //---
         } //-end OBVDirectionScan()
       //---
    

    Steps in the OBVDirectionScan() Function in the OBV_MTF Indicator:

    • 1. Initialization:
      • Initialize the return value (ret) to 0.
      • Define constants for rising and falling OBV (rise and down).
    • 2. Set the Number of Bars to Scan:
      • Calculate the number of bars to scan (br) based on the shift parameter.
    • 3. Update Price Data:
      • Call the UpdatePrice(stf) function to update the price data for the specified timeframe (stf).
    • 4. Declare and Resize the OBV Array:
      • Declare the OBV array to store OBV values.
      • Resize the OBV array to accommodate the number of bars (br) and set it as a series.
    • 5. Retrieve Volume and Close Prices:
      • Get the volume (vol), previous close price (prev_close), and current close price (curr_close) from their respective arrays.
    • 6. Fill OBV Buffer:
      • Calculate the OBV value for the current bar based on the comparison of curr_close and prev_close.
      • Update the OBV array with the calculated OBV value.
    • 7. Get the Current and Previous OBV Values:
      • Retrieve the current OBV value (close_now) and the previous OBV value (close_prev) from the OBV array.
    • 8. Calculate the Percentage Change in OBV Values:
      • If either close_now or close_prev is 0 or empty, set res to 0.0.
      • Otherwise, calculate the percentage change in OBV values and normalize the result.
    • 9. Adjust the Result for Percentage Comparison:
      • Multiply the result by 100 to adjust for percentage comparison.
    • 10. Determine the Direction of OBV Movement:
      • Compare the result (res) with move to determine if the OBV is rising or falling.
      • Set the return value (ret) accordingly.

    This function helps determine the direction and strength of OBV movement by analyzing the percentage change in OBV values across different timeframes.

    The On Balance Volume Multi-Timeframe (OBV_MTF) indicator is designed for MetaTrader 5 (MT5). It calculates the strength of price movement as a percentage on each timeframe bar, assisting traders in analyzing market trends and making informed trading decisions.

    Key Functions of the OBV_MTF Indicator:

    • 1. Displays Multiple Timeframes: Allows traders to view various timeframes on a single chart, enhancing their ability to analyze market movements effectively.
    • 2. Identifies Market Trends: By viewing the indicator across multiple timeframes, traders can more easily identify both larger and smaller market trends.
    • 3. Supports Trading Decisions: Assists traders in making informed decisions on entering or exiting trades based on observed market conditions across different timeframes.

    Detailed Breakdown of Functions:

    • 1. OBV_MTF_Config(): Configures the multi-timeframe settings and prepares the indicator for use.
    • 2. PositionCore(): Sets up core position settings for the indicator elements on the chart.
    • 3. DrawOBVObject(): Draws various elements of the indicator on the chart.
    • 4. PanelPosChange(int inpos): Changes the panel position based on the input position value.
    • 5. UpdatePrice(ENUM_TIMEFRAMES xtf): Updates price data, including TickVolume arrays, for a given timeframe.
    • 6. RefreshPrice(ENUM_TIMEFRAMES xtf, int bars): Refreshes price data for a given timeframe and a specified number of bars.
    • 7. OBVDirectionScan(const ENUM_TIMEFRAMES stf, int shift): Scans and calculates the direction of the OBV indicator value in percentage terms.
    • 8. OBVMovementCalculation(int barCnt): Scans the direction of OBV indicator value for each timeframe, calculates how much it has moved up or down, and compares them to determine the signal strength.
    • 9. ThisTime(const int reqmode): Retrieves current time information based on the requested mode.
    • 10. Do_Alerts(string msg): Generates various types of alerts (print, alert, email, and notification) based on the provided message.
    • 11. FontsModel(int mode): Returns the font name based on the input mode.
    • 12. ChangeChartSymbol(string tf_name, ENUM_TIMEFRAMES stf): Changes the chart symbol and timeframe based on the input parameters.
    • 13. DisplayPanelButton(): Displays panel buttons on the chart.
    • 14. DisplayButtonClick(string actreq): Handles the creation and display of various buttons on the chart.
    • 15. DeletedOBVObject(): Deletes all objects related to the OBV_MTF indicator from the chart.
    • 16. strTF(ENUM_TIMEFRAMES period): Converts timeframe enumerations to string representations.
    • 17. getUninitReasonText(int reasonCode): Provides descriptive reasons for the uninitialization of the Expert Advisor (EA) based on the given reason code.
    • 18. WS(int width): Calculates the width scaling factor for buttons based on screen DPI.
    • 19. CreateButtonClick(): Creates a clickable button on the chart with specified properties.
    • 20. CreateArrowLabel(): Creates an arrow label on the chart with specified properties.
    • 21. CreateButtonTemplate(): Creates a rectangular button on the chart with specified properties.

    Main Functions of the Program:

    • 1. OnInit(): Initializes the custom indicator, configuring indicator buffers and setting indicator properties.
    • 2. OnDeinit(const int reason): Deinitializes the custom indicator, cleans up indicator objects, and prints the reason for deinitialization.
    • 3. OnCalculate(): Handles the main calculations for the indicator on every price tick, including scanning price strength and generating alerts.
    • 4. OnChartEvent(): Handles chart events, particularly button clicks, to manage the indicator's display and settings.

    Summary

    The OBV_MTF indicator is a comprehensive tool that provides traders with valuable insights into price movements across multiple timeframes. Its various functions ensure an enhanced trading experience by configuring, updating, and displaying the indicator's elements effectively. This helps traders identify market trends and make well-informed trading decisions.

    We hope that this article and the OBV_MTF or On Balance Volume Multi Timeframe indicator for MT5 program will be useful for traders in learning and generating new ideas, which is ultimately expected to be successful in making money from forex trading.

    Vital Records

    If you would like to receive a source program for this article, please send a request via Contact Us form page, and I will send it to your email, source code: On Balance Volume Multi Timeframe indicator

    If you think the On Balance Volume Multi Timeframe indicator is worthy of being used for automated trading as an Expert Advisor, please leave a comment below this article.

    If at least 25 people agree that this indicator is worthy of being used as an Expert Advisor, I will create an Expert Advisor based on its signals and share it on this blog.

    Thanks for reading this article.

    Risk Warning: Trading Forex and CFDs involves significant risk and may not be suitable for all investors. All content provided is for educational purposes only.

    
    //+------------------------------------------------------------------+
    //|                                                      OBV_MTF.mq5 |
    //|                           Copyright 2025, Roberto Jacobs (3rjfx) |
    //|                              https://www.mql5.com/en/users/3rjfx |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, Roberto Jacobs (3rjfx) ~ Date: 2025-01-13"
    #property link      "https://www.mql5.com/en/users/3rjfx"
    #property version   "1.00"
    #property description "OBV_MTF is the On Balance Volume Indicator in Multi Timeframe"
    #property description "for MT5 which is calculated and scan OBV on each timeframe."
    //---
    #property indicator_chart_window
    #property indicator_plots   1
    #property indicator_buffers 1
    //---
    //--
    enum YN
      {
       No,
       Yes
      };
    //--
    enum fonts
      {
       Verdana,
       Bodoni_MT_Black
      };
    //--
    //---
    input group   "====   Indicator Color and Font   ===="
    input color              ArrowUp = clrMediumSeaGreen; // Arrow Up Color
    input color              ArrowDn = clrDeepPink;       // Arrow Down Color
    input color              NTArrow = clrGold;           // Arrow No Signal
    input fonts              f_model = Bodoni_MT_Black;   // Select Font Model
    input group   "====  Input parameters for alerts  ===="
    input YN                  alerts = Yes;               // Display Alerts Pop-up on Chart (Yes) or (No)
    input YN           UseEmailAlert = No;                // Email Alert (Yes) or (No)
    input YN           UseSendnotify = No;                // Send Notification (Yes) or (No)
    //---
    //---------//
    //+------------------------------------------------------------------+
    //| class for MTF indicator                                          |
    //+------------------------------------------------------------------+
    class MTF_Indi
      {
       //---
       public:
       //--
       int               fbar;
       int               star,
                         tstar,
                         bstar;
       int               TNamex,
                         TNamexn,
                         TNamey1,
                         TNamey2,
                         TNamey3,
                         TNamey1n,
                         TNamey2n,
                         TNamey3n;
       int               tfxar;
       int               up,dw;
       int               tfhalf;
       int               maxbar;
       int               ttlbars;
       int               scaleX,
                         scaleA,
                         scaleY,
                         horizL1,
                         horizL2,
                         horizL3,
                         horizL4,
                         horizL5,
                         horizL6,
                         vertiL1,
                         vertiL2,
                         vertiL3,
                         vertiL4,
                         vertiL5,
                         vertiL6,
                         offsetX,
                         offsetY,
                         fontSize,
                         windchar,
                         windsize;
       int               corx,
                         cory,
                         txttf;
       int               curmin,
                         prvmin;
       int               corpos;
       int               pospos,
                         postop,
                         posbot;
       int               curAlert;
       int               prvAlert;
       //--
       long              CI;
       string            posisi,
                         sigpos,
                         indname,
                         msgText,
                         ObjName;
       string            cstar,
                         artop,
                         arbot;
       string            hName1,
                         hName2,
                         hName3;
       string            font_mode;
       bool              display;
       double            OPEN[],
                         HIGH[],
                         LOW[],
                         CLOSE[];
       long              VOLUME[];
       datetime          TIME[];
       datetime          cbartime;
       //--
       int               year,  // Year
                         mon,   // Month
                         day,   // Day
                         hour,  // Hour
                         min,   // Minutes
                         sec,   // Seconds
                         dow,   // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
                         doy;   // Day number of the year (January 1st is assigned the number value of zero)
       //---- buffers
       double            PowerMove[];
       string            TFSc[];
       color             Arwcolor[];
       color             TColor;
       ENUM_TIMEFRAMES   TFId[];
       ENUM_BASE_CORNER  bcor;
       ENUM_ANCHOR_POINT ancp;
       //---
       //---
       //- Constructor
                         MTF_Indi(void):
                         year(0),
                         mon(1),
                         day(2),
                         hour(3),
                         min(4),
                         sec(5),
                         dow(6),
                         doy(7),
                         fbar(125),
                         star(181),
                         maxbar(3),
                         pospos(0),
                         postop(0),
                         posbot(1),
                         tstar(217),
                         bstar(218),
                         tfhalf(11),
                         scaleX(35),
                         scaleA(36),
                         scaleY(50),
                         offsetY(18),
                         offsetX(120),
                         fontSize(7),
                         cbartime(0),
                         posisi(""),
                         sigpos(""),
                         msgText(""),
                         curAlert(0),
                         prvAlert(0),
                         ttlbars(125),
                         windsize(12),
                         windchar(108),
                         CI(ChartID()),
                         display(false),
                         ObjName("OBV_"),
                         font_mode(FontsModel(f_model)),
                         cstar(CharToString((uchar)star)),
                         artop(CharToString((uchar)tstar)),
                         arbot(CharToString((uchar)bstar)),
                         indname(MQLInfoString(MQL_PROGRAM_NAME))
         {
         }
       //---
       //- Destructor
                        ~MTF_Indi(void)
         {
         }
       //---
       //---
       virtual void      OBV_MTF_Config(void)
         {
          //--
          ENUM_TIMEFRAMES TFIx[]= {PERIOD_M1,PERIOD_M2,PERIOD_M3,PERIOD_M4,PERIOD_M5,PERIOD_M6,PERIOD_M10,PERIOD_M12,PERIOD_M15,
                                   PERIOD_M20,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,
                                   PERIOD_D1,PERIOD_W1,PERIOD_MN1};
          tfxar=ArraySize(TFIx);
          ArrayResize(TFId,tfxar,tfxar);
          ArrayResize(Arwcolor,tfxar,tfxar);
          ArrayCopy(TFId,TFIx,0,0,WHOLE_ARRAY);
          //--
          string TFxc[]= {"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1",
                          "H2","H3","H4","H6","H8","H12","D1","W1","MN1"}; // 21 Timeframes
          //--
          ArrayResize(TFSc,tfxar,tfxar);
          ArrayCopy(TFSc,TFxc,0,0,WHOLE_ARRAY);
          //--
          DeletedOBVObject();
          OBVMovementCalculation(25);
          PositionCore();
          //--
          if(display)
             DrawOBVObject();
          //---
         }
       //---
       //---
       void              PositionCore(void)
         {
          corpos=pospos;
          if(corpos>-1)
            {
             if(corpos==postop)
               {
                bcor=CORNER_LEFT_UPPER;
                ancp=ANCHOR_CENTER;
                corx=155;
                cory=13;
                txttf=45;
                horizL1=8;
                horizL2=-5;
                horizL3=8;
                horizL4=-5;
                horizL5=11;
                horizL6=10;
                vertiL1=39;
                vertiL2=18;
                vertiL3=69;
                vertiL4=48;
                vertiL5=52;
                vertiL6=67;
                TNamex=554;
                TNamexn=562;
                TNamey1=30;
                TNamey2=46;
                TNamey3=62;
                TNamey1n=38;
                TNamey2n=54;
                TNamey3n=70;
                hName1="O";
                hName2="B";
                hName3="V";
                //--
                DisplayButtonClick("cstar");
                DisplayButtonClick("arbot");
                //--
               }
             if(corpos==posbot)
               {
                bcor=CORNER_LEFT_LOWER;
                ancp=ANCHOR_CENTER;
                corx=155;
                cory=74;
                txttf=45;
                horizL1=8;
                horizL2=-5;
                horizL3=8;
                horizL4=-5;
                horizL5=11;
                horizL6=10;
                vertiL1=34;
                vertiL2=29;
                vertiL3=65;
                vertiL4=59;
                vertiL5=49;
                vertiL6=61;
                TNamex=554;
                TNamexn=562;
                TNamey1=13;
                TNamey2=46;
                TNamey3=62;
                TNamey1n=18;
                TNamey2n=33;
                TNamey3n=49;
                hName1="V";
                hName2="B";
                hName3="O";
                //--
                DisplayButtonClick("cstar");
                DisplayButtonClick("artop");
                //--
               }
             display=true;
            }
         }
       //---
       //---
       void              DrawOBVObject(void)
         {
          //--
          CreateButtonTemplate(CI,ObjName+"Template",397,66,STYLE_SOLID,9,BORDER_RAISED,clrMistyRose,clrLavenderBlush,clrWhite,bcor,corx,cory,true);
          for(int x=0; x<tfhalf; x++)
            {
             CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                                 txttf+horizL1+(x*scaleX)+offsetX+x,vertiL1,true,"Arrow_"+TFSc[x]);
             CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                                  bcor,txttf+horizL2+(x*scaleA)+offsetX,vertiL2,true,
                                  "Change Timeframe to : "+TFSc[x]);
            }
          for(int x=tfhalf, x2=0; x<tfxar; x++, x2++)
            {
             CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                                 txttf+horizL3+(x2*scaleX)+offsetX+x2,vertiL3,true,"Arrow_"+TFSc[x]);
             CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                                  bcor,txttf+horizL4+(x2*scaleA)+offsetX,vertiL4,true,
                                  "Change Timeframe to : "+TFSc[x]);
             //--
             if(x==20)
               {
                int arrowChar=TColor==ArrowUp ? 200 : TColor==ArrowDn ? 202 : windchar;
                CreateArrowLabel(CI,ObjName+"_tfx_arrow_"+string(x+1),"Move",font_mode,fontSize,clrBlue,bcor,
                                    519+horizL5,vertiL5,true,"Move");
                CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x+1),CharToString((uchar)arrowChar),"Wingdings",15,TColor,bcor,
                                    522+horizL6,vertiL6,true,"Arrow Indicator Movement");
               }
            }
          DisplayButtonClick("X");
          CreateButtonTemplate(CI,ObjName+"TemplateName1",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey1,true);
          CreateButtonTemplate(CI,ObjName+"TemplateName2",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey2,true);
          CreateButtonTemplate(CI,ObjName+"TemplateName3",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey3,true);
          CreateArrowLabel(CI,ObjName+"_name1",hName1,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey1n,true,hName1);
          CreateArrowLabel(CI,ObjName+"_name2",hName2,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey2n,true,hName2);
          CreateArrowLabel(CI,ObjName+"_name3",hName3,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey3n,true,hName3);
          //--
          if(corpos==postop)
            {
              DisplayButtonClick("cstar");
              DisplayButtonClick("arbot");
            }
          if(corpos==posbot)
            {
              DisplayButtonClick("cstar");
              DisplayButtonClick("artop");
            }
          //--
          return;
          //---
         } //-end DrawOBVObject()
       //---
       //---
       void              PanelPosChange(int inpos)
         {
          corpos=inpos;
          //--
          if(inpos>=0)
            {
             if(inpos==postop)
               {
                bcor=CORNER_LEFT_UPPER;
                ancp=ANCHOR_CENTER;
                corx=155;
                cory=13;
                txttf=45;
                horizL1=8;
                horizL2=-5;
                horizL3=8;
                horizL4=-5;
                horizL5=11;
                horizL6=10;
                vertiL1=39;
                vertiL2=18;
                vertiL3=69;
                vertiL4=48;
                vertiL5=52;
                vertiL6=67;
                TNamex=554;
                TNamexn=562;
                TNamey1=30;
                TNamey2=46;
                TNamey3=62;
                TNamey1n=38;
                TNamey2n=54;
                TNamey3n=70;
                hName1="O";
                hName2="B";
                hName3="V";
                //--
                DisplayButtonClick("cstar");
                DisplayButtonClick("arbot");
                //--
               }
             if(inpos==posbot)
               {
                bcor=CORNER_LEFT_LOWER;
                ancp=ANCHOR_CENTER;
                corx=155;
                cory=74;
                txttf=45;
                horizL1=8;
                horizL2=-5;
                horizL3=8;
                horizL4=-5;
                horizL5=11;
                horizL6=10;
                vertiL1=34;
                vertiL2=29;
                vertiL3=65;
                vertiL4=59;
                vertiL5=49;
                vertiL6=61;
                TNamex=554;
                TNamexn=562;
                TNamey1=25;
                TNamey2=41;
                TNamey3=57;
                TNamey1n=18;
                TNamey2n=33;
                TNamey3n=49;
                hName1="V";
                hName2="B";
                hName3="O";
                //--
                DisplayButtonClick("cstar");
                DisplayButtonClick("artop");
                //--
               }
             display=true;
            }
          //---
         }
       //---
       //---
       void              UpdatePrice(ENUM_TIMEFRAMES xtf)
         {
          maxbar=fbar;
          //--
          ArrayFree(OPEN);
          ArrayFree(HIGH);
          ArrayFree(LOW);
          ArrayFree(CLOSE);
          ArrayFree(TIME);
          ArrayFree(VOLUME);
          //--
          ArrayResize(OPEN,maxbar,maxbar);
          ArrayResize(HIGH,maxbar,maxbar);
          ArrayResize(LOW,maxbar,maxbar);
          ArrayResize(CLOSE,maxbar,maxbar);
          ArrayResize(TIME,maxbar,maxbar);
          ArrayResize(VOLUME,maxbar,maxbar);
          //--
          ArraySetAsSeries(OPEN,true);
          ArraySetAsSeries(HIGH,true);
          ArraySetAsSeries(LOW,true);
          ArraySetAsSeries(CLOSE,true);
          ArraySetAsSeries(TIME,true);
          ArraySetAsSeries(VOLUME,true);
          //--
          ArrayInitialize(OPEN,0.0);
          ArrayInitialize(HIGH,0.0);
          ArrayInitialize(LOW,0.0);
          ArrayInitialize(CLOSE,0.0);
          ArrayInitialize(TIME,0);
          ArrayInitialize(VOLUME,0);
          //--
          int barx=PeriodSeconds(xtf)/60*maxbar;
          RefreshPrice(PERIOD_M1,maxbar);
          RefreshPrice(xtf,barx);
          //--
          int co=CopyOpen(Symbol(),xtf,0,maxbar,OPEN);
          int ch=CopyHigh(Symbol(),xtf,0,maxbar,HIGH);
          int cl=CopyLow(Symbol(),xtf,0,maxbar,LOW);
          int cc=CopyClose(Symbol(),xtf,0,maxbar,CLOSE);
          int ct=CopyTime(Symbol(),xtf,0,maxbar,TIME);
          int cv=CopyTickVolume(Symbol(),xtf,0,maxbar,VOLUME);
          //--
          return;
          //---
         } //-end UpdatePrice()
       //---
       //---
       void              RefreshPrice(ENUM_TIMEFRAMES xtf,int bars)
         {
          //--
          MqlRates parray[];
          ArraySetAsSeries(parray,true);
          int copied=CopyRates(Symbol(),xtf,0,bars,parray);
          //--
          return;
          //---
         } //-end RefreshPrice()
       //---
       //---
       int               OBVDirectionScan(const ENUM_TIMEFRAMES stf,int shift) // Scan OBV Direction
         {
          //--
          int ret=0;
          int rise=1,
              down=-1;
          //--
          int br=shift+2;
          double res=0.0;
          double move=0.0;
          UpdatePrice(stf);
          //--
          double OBV[];
          ArrayResize(OBV,br,br);
          ArraySetAsSeries(OBV,true);
          //--
          double vol=(double)VOLUME[shift];
          double prev_close=CLOSE[shift+1];
          double curr_close=CLOSE[shift];
          //--- fill OBV Buffer
          if(curr_close<prev_close)
             OBV[shift]=OBV[shift+1]-vol;
          else
            {
             if(curr_close>prev_close)
                OBV[shift]=OBV[shift+1]+vol;
             else
                OBV[shift]=OBV[shift+1];
            }
          //--
          double close_now  = OBV[shift];
          double close_prev = OBV[shift+1];
          //--
          if((close_now==0 || close_now==EMPTY_VALUE) || (close_prev==0 || close_prev==EMPTY_VALUE))
             res=0.0;
          else
             res=NormalizeDouble((close_now / close_prev * 100) - 100,3);
          res=NormalizeDouble(res*100,3); // because its value less than 1 then multiplied with 100.
          //--
          if(res>move)
             ret=rise;
          if(res<move)
             ret=down;
          //--
          return(ret);
          //---
         } //-end OBVDirectionScan()
       //---
       //---
       void              OBVMovementCalculation(int barCnt) // Scan the direction of iOBV movement on each timeframe
         {
          //--
          ArrayResize(PowerMove,barCnt,barCnt);
          ArraySetAsSeries(PowerMove,true);
          //--
          for(int i=barCnt-1; i>=0; i--)
            {
             up=0;
             dw=0;
             //--
             for(int x=0; x<tfxar; x++)
               {
                Arwcolor[x]=NTArrow;
                PowerMove[i]=0.0;
                int PPM=OBVDirectionScan(TFId[x],0);
                if(PPM>0)
                  {
                   up++;
                   Arwcolor[x]=ArrowUp;
                  }
                if(PPM<0)
                  {
                   dw++;
                   Arwcolor[x]=ArrowDn;
                  }
                if(x==tfxar-1)
                  {
                   if(up>dw+1)
                     {
                      PowerMove[i]=1.0;
                      TColor=ArrowUp;
                      curAlert=1;
                     }
                   if(dw>up+1)
                     {
                      PowerMove[i]=-1.0;
                      TColor=ArrowDn;
                      curAlert=-1;
                     }
                  }
               }
            }
          //--
          return;
          //---
         } //-end OBVMovementCalculation()
       //---
       //---
       int               ThisTime(const int reqmode)
         {
          //--
          MqlDateTime tm;
          TimeCurrent(tm);
          int valtm=0;
          //--
          switch(reqmode)
            {
             case 0:
                valtm=tm.year;
                break;        // Return Year
             case 1:
                valtm=tm.mon;
                break;        // Return Month
             case 2:
                valtm=tm.day;
                break;        // Return Day
             case 3:
                valtm=tm.hour;
                break;        // Return Hour
             case 4:
                valtm=tm.min;
                break;        // Return Minutes
             case 5:
                valtm=tm.sec;
                break;        // Return Seconds
             case 6:
                valtm=tm.day_of_week;
                break;        // Return Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
             case 7:
                valtm=tm.day_of_year;
                break;        // Return Day number of the year (January 1st is assigned the number value of zero)
            }
          //--
          return(valtm);
          //---
         } //-end ThisTime()
       //---
       //---
       void              Do_Alerts(string msg)
         {
          //--
          Print("--- "+Symbol()+","+strTF(Period())+": "+msg+
                "\n--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
          //--
          if(alerts==Yes)
            {
             Alert("--- "+Symbol()+","+strTF(Period())+": "+msg+
                   "--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
            }
          //--
          if(UseEmailAlert==Yes)
             SendMail(indname,"--- "+Symbol()+" "+strTF(Period())+": "+msg+
                      "\n--- at: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
          //--
          if(UseSendnotify==Yes)
             SendNotification(Symbol()+"--- "+Symbol()+" "+strTF(Period())+": "+msg+
                              "\n--- at: "+TimeToString(iTime(Symbol(),0,0),TIME_DATE|TIME_MINUTES));
          //--
          return;
          //---
         } //-end Do_Alerts()
       //---
       //---
       string            FontsModel(int mode)
         {
          string str_font;
          switch(mode)
            {
             case 0:
                str_font="Verdana";
                break;
             case 1:
                str_font="Bodoni MT Black";
                break;
            }
          //--
          return(str_font);
          //---
         } //-end FontsModel()
       //---
       //---
       void              ChangeChartSymbol(string tf_name,ENUM_TIMEFRAMES stf)
         {
          //---
          //--- unpress the button
          ObjectSetInteger(CI,tf_name,OBJPROP_STATE,false);
          ObjectSetInteger(CI,tf_name,OBJPROP_ZORDER,0);
          //--
          DeletedOBVObject();
          PanelPosChange(corpos);
          ChartSetSymbolPeriod(CI,Symbol(),stf);
          if(display)
             DrawOBVObject();
          //--
          ChartRedraw(CI);
          //--
          return;
          //---
         } //-end ChangeChartSymbol()
       //---
       //---
       void              DisplayPanelButton(void)
         {
          //--
          ObjectDelete(CI,cstar);
          ObjectDelete(CI,artop);
          ObjectDelete(CI,arbot);
          //--
          CreateButtonClick(CI,cstar,20,20,"Wingdings",13,BORDER_FLAT,cstar,clrWhite,clrWhite,TColor,CORNER_RIGHT_UPPER,25,40,true,"Open Panel Indicator");
          CreateButtonClick(CI,artop,18,18,"Wingdings",11,BORDER_FLAT,artop,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,20,true,"Change Panel to Top");
          CreateButtonClick(CI,arbot,18,18,"Wingdings",11,BORDER_FLAT,arbot,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,62,true,"Change Panel to Bottom");
          //--
          ChartRedraw(CI);
          //--
          return;
          //---
         } //-end DisplayPanelButton()
       //---
       //---
       void              DisplayButtonClick(string actreq)
         {
          //--
          if(actreq=="cstar")
             CreateButtonClick(CI,cstar,20,20,"Wingdings",13,BORDER_FLAT,cstar,clrWhite,clrWhite,TColor,CORNER_RIGHT_UPPER,25,40,true,"Open Panel Indicator");
          if(actreq=="artop")
             CreateButtonClick(CI,artop,18,18,"Wingdings",11,BORDER_FLAT,artop,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,20,true,"Change Panel to Top");
          if(actreq=="arbot")
             CreateButtonClick(CI,arbot,18,18,"Wingdings",11,BORDER_FLAT,arbot,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,62,true,"Change Panel to Bottom");
          if(actreq=="X")
             CreateButtonClick(CI,"X",17,15,"Arial Black",fontSize,BORDER_FLAT,"X",clrWhite,clrWhite,clrRed,bcor,txttf-7+(11*scaleA)+offsetX,cory,true,"Close Panel");
          //--
          ChartRedraw(CI);
          //--
          return;
          //---
         } //-end DisplayButtonClick()
       //---
       //---
       void              DeletedOBVObject(void)
         {
          //--
          string name;
          for(int i=ObjectsTotal(CI,-1,-1)-1; i>=0; i--)
            {
             name=ObjectName(CI,i,-1,-1);
             if(StringFind(name,ObjName,0)>-1)
                ObjectDelete(CI,name);
             for(int x=0; x<tfxar; x++)
               {
                if(StringFind(name,TFSc[x],0)>-1)
                   ObjectDelete(CI,name);
               }
             //--
             ObjectDelete(CI,"X");
             ObjectDelete(CI,cstar);
             ObjectDelete(CI,artop);
             ObjectDelete(CI,arbot);
            }
          //--
          return;
          //---
         } //-end DeletedOBVObject()
       //---
       //---
       string            strTF(ENUM_TIMEFRAMES period)
         {
          string intf="";
          //--
          switch(period)
            {
             //--
             case PERIOD_M1:
               {intf="M1";  break;}
             case PERIOD_M2:
               {intf="M2";  break;}
             case PERIOD_M3:
               {intf="M3";  break;}
             case PERIOD_M4:
               {intf="M4";  break;}
             case PERIOD_M5:
               {intf="M5";  break;}
             case PERIOD_M6:
               {intf="M6";  break;}
             case PERIOD_M10:
               {intf="M10"; break;}
             case PERIOD_M12:
               {intf="M12"; break;}
             case PERIOD_M15:
               {intf="M15"; break;}
             case PERIOD_M20:
               {intf="M20"; break;}
             case PERIOD_M30:
               {intf="M30"; break;}
             case PERIOD_H1:
               {intf="H1";  break;}
             case PERIOD_H2:
               {intf="H2";  break;}
             case PERIOD_H3:
               {intf="H3";  break;}
             case PERIOD_H4:
               {intf="H4";  break;}
             case PERIOD_H6:
               {intf="H6";  break;}
             case PERIOD_H8:
               {intf="H8";  break;}
             case PERIOD_H12:
               {intf="H12"; break;}
             case PERIOD_D1:
               {intf="D1";  break;}
             case PERIOD_W1:
               {intf="W1";  break;}
             case PERIOD_MN1:
               {intf="MN1"; break;}
                //--
            }
          return(intf);
          //---
         } //-end strTF()
       //---
       //---
       string            getUninitReasonText(int reasonCode)
         {
          //--
          string text="";
          //--
          switch(reasonCode)
            {
             case REASON_PROGRAM:
                text="The EA has stopped working calling by remove function.";
                break;
             case REASON_REMOVE:
                text="Program "+__FILE__+" was removed from chart";
                break;
             case REASON_RECOMPILE:
                text="Program recompiled.";
                break;
             case REASON_CHARTCHANGE:
                text="Symbol or timeframe was changed";
                break;
             case REASON_CHARTCLOSE:
                text="Chart was closed";
                break;
             case REASON_PARAMETERS:
                text="Input-parameter was changed";
                break;
             case REASON_ACCOUNT:
                text="Account was changed";
                break;
             case REASON_TEMPLATE:
                text="New template was applied to chart";
                break;
             case REASON_INITFAILED:
                text="The OnInit() handler returned a non-zero value.";
                break;
             case REASON_CLOSE:
                text="Terminal closed.";
                break;
             default:
                text="Another reason";
                break;
            }
          //--
          return text;
          //---
         } //-end getUninitReasonText()
       //---
       //---
       int               WS(int width) // Width Scaling factor wide button
         {
          //--
          int res=0;
          int reswidth=0;
          //-- Calculating the scaling factor wide button on a screen
          int scale_factor=(TerminalInfoInteger(TERMINAL_SCREEN_DPI));
          // The basic width in the screen points for standard monitors with DPI=96
          //-- Use of the scaling factor
          reswidth=(width * scale_factor) / 96;
          res=(int)NormalizeDouble(reswidth*1.25,0);
          //--
          return(res);
          //---
         } //-end WS()
       //---
       //---
       void              CreateButtonClick(long   chartid,
                                           string button_name,
                                           int    button_x_size,
                                           int    button_y_size,
                                           string button_font_model,
                                           int    button_font_size,
                                           int    button_border,
                                           string button_name_text,
                                           color  button_bord_color,
                                           color  button_bg_color,
                                           color  button_color,
                                           int    button_corner,
                                           int    button_xdist,
                                           int    button_ydist,
                                           bool   button_hidden,
                                           string tooltip)
         {
          //--
          ObjectCreate(chartid,button_name,OBJ_BUTTON,0,0,0); // create button
          ObjectSetInteger(chartid,button_name,OBJPROP_XSIZE,WS(button_x_size));
          ObjectSetInteger(chartid,button_name,OBJPROP_YSIZE,WS(button_y_size));
          ObjectSetString(chartid,button_name,OBJPROP_TEXT,button_name_text);
          ObjectSetString(chartid,button_name,OBJPROP_FONT,button_font_model);
          ObjectSetInteger(chartid,button_name,OBJPROP_FONTSIZE,WS(button_font_size));
          ObjectSetInteger(chartid,button_name,OBJPROP_BORDER_TYPE,WS(button_border));
          ObjectSetInteger(chartid,button_name,OBJPROP_BORDER_COLOR,button_bord_color);
          ObjectSetInteger(chartid,button_name,OBJPROP_BGCOLOR,button_bg_color);
          ObjectSetInteger(chartid,button_name,OBJPROP_COLOR,button_color);
          ObjectSetInteger(chartid,button_name,OBJPROP_ANCHOR,ancp);
          ObjectSetInteger(chartid,button_name,OBJPROP_CORNER,button_corner);
          ObjectSetInteger(chartid,button_name,OBJPROP_XDISTANCE,WS(button_xdist));
          ObjectSetInteger(chartid,button_name,OBJPROP_YDISTANCE,WS(button_ydist));
          ObjectSetInteger(chartid,button_name,OBJPROP_HIDDEN,button_hidden);
          ObjectSetString(chartid,button_name,OBJPROP_TOOLTIP,tooltip);
          ChartRedraw(chartid);
          //--
          return;
          //---
         } //-end CreateButtonClick()
       //---
       //---
       bool              CreateArrowLabel(long   chart_id,
                                          string lable_name,
                                          string label_text,
                                          string font_model,
                                          int    font_size,
                                          color  label_color,
                                          int    chart_corner,
                                          int    x_cor,
                                          int    y_cor,
                                          bool   price_hidden,
                                          string tooltip)
         {
          //--
          ObjectDelete(chart_id,lable_name);
          //--
          if(!ObjectCreate(chart_id,lable_name,OBJ_LABEL,0,0,0,0,0)) // create Label
            {
             Print(__FUNCTION__, ": failed to create \"Arrow Label\" sign! Error code = ",GetLastError());
             return(false);
            }
          //--
          ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
          ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model);
          ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,WS(font_size));
          ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
          ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
          ObjectSetInteger(chart_id,lable_name,OBJPROP_ANCHOR,ancp);
          ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,WS(x_cor));
          ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,WS(y_cor));
          ObjectSetInteger(chart_id,lable_name,OBJPROP_HIDDEN,price_hidden);
          ObjectSetString(chart_id,lable_name,OBJPROP_TOOLTIP,tooltip);
          //-- successful execution
          return(true);
          //---
         } //-end CreateArrowLabel()
       //---
       //---
       void              CreateButtonTemplate(long chartid,
                                              string obj_name,
                                              int    x_size,
                                              int    y_size,
                                              int    style,
                                              int    width,
                                              int    border,
                                              color  bordcolor,
                                              color  bgcolor,
                                              color  objcolor,
                                              int    corner,
                                              int    x_dist,
                                              int    y_dist,
                                              bool   hidden)
         {
          //--
          ObjectCreate(chartid,obj_name,OBJ_RECTANGLE_LABEL,0,0,0); // create Rectangle Label
          ObjectSetInteger(chartid,obj_name,OBJPROP_XSIZE,WS(x_size));
          ObjectSetInteger(chartid,obj_name,OBJPROP_YSIZE,WS(y_size));
          ObjectSetInteger(chartid,obj_name,OBJPROP_STYLE,style);
          ObjectSetInteger(chartid,obj_name,OBJPROP_WIDTH,WS(width));
          ObjectSetInteger(chartid,obj_name,OBJPROP_BORDER_TYPE,WS(border));
          ObjectSetInteger(chartid,obj_name,OBJPROP_BORDER_COLOR,bordcolor);
          ObjectSetInteger(chartid,obj_name,OBJPROP_BGCOLOR,bgcolor);
          ObjectSetInteger(chartid,obj_name,OBJPROP_COLOR,objcolor);
          ObjectSetInteger(chartid,obj_name,OBJPROP_CORNER,corner);
          ObjectSetInteger(chartid,obj_name,OBJPROP_XDISTANCE,WS(x_dist));
          ObjectSetInteger(chartid,obj_name,OBJPROP_YDISTANCE,WS(y_dist));
          ObjectSetInteger(chartid,obj_name,OBJPROP_HIDDEN,hidden);
          ChartRedraw(chartid);
          //--
          return;
          //---
         } //-end CreateButtonTemplate()
       //---
       //---
      }; //-end class MTF_Indi()
    //---------//
    
    MTF_Indi mi;
    
    //---------//
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       mi.OBV_MTF_Config();
    //--
       SetIndexBuffer(0,mi.PowerMove,INDICATOR_DATA);
       PlotIndexSetString(0,PLOT_LABEL,"Move");
    //--
       IndicatorSetString(INDICATOR_SHORTNAME,mi.indname);
       IndicatorSetInteger(INDICATOR_DIGITS,2);
    //---
       return(INIT_SUCCEEDED);
      }
    //---------//
    //+------------------------------------------------------------------+
    //| Custom indicator deinitialization function                       |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //---
       Comment("");
       Print(mi.getUninitReasonText(reason));
    //--
       mi.DeletedOBVObject();
    //--
       ChartRedraw(mi.CI);
    //---
       return;
    //---
      } //-end OnDeinit()
    //-------//
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
       ResetLastError();
       //--
       int limit;
       limit=rates_total-prev_calculated;
       if(prev_calculated>0)
          limit++;
       if(limit>mi.ttlbars)
          limit=mi.ttlbars;
       if(limit-3<=1)
          limit=3;
       //--
       mi.OBVMovementCalculation(limit);
       //--
       if(alerts==Yes||UseEmailAlert==Yes||UseSendnotify==Yes)
         {
           mi.curmin=mi.ThisTime(mi.min);
           if(mi.curmin!=mi.prvmin && mi.curAlert==1 && mi.curAlert!=mi.prvAlert)
             {
               string AlertTxt="The strength of the OBV movement appears to be Rise.";
               mi.Do_Alerts(AlertTxt);
               mi.prvAlert=mi.curAlert;
               mi.prvmin=mi.curmin;
             }
           if(mi.curmin!=mi.prvmin && mi.curAlert==-1 && mi.curAlert!=mi.prvAlert)
             {
               string AlertTxt="The strength of the OBV movement appears to be Down.";
               mi.Do_Alerts(AlertTxt);
               mi.prvAlert=mi.curAlert;
               mi.prvmin=mi.curmin;
             }
         }
       //--
       if(mi.display)
         mi.DrawOBVObject();
       //---
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //---------//
    //+------------------------------------------------------------------+
    
    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //---
    //--- handling CHARTEVENT_CLICK event ("Clicking the chart")
       ResetLastError();
    //--
       if(id==CHARTEVENT_OBJECT_CLICK)
         {
          //--- if "X" button is click
          if(sparam=="X")
            {
             mi.DeletedOBVObject();
             //--- unpress the button
             ObjectSetInteger(mi.CI,"X",OBJPROP_STATE,false);
             ObjectSetInteger(mi.CI,"X",OBJPROP_ZORDER,0);
             //--
             mi.display=false;
             ObjectDelete(mi.CI,"X");
             mi.DisplayPanelButton();
            }
          //--- if "cstar" button is click
          if(sparam==mi.cstar)
            {
             mi.DeletedOBVObject();
             mi.DisplayPanelButton();
             //--- unpress the button
             ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_STATE,false);
             ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_ZORDER,0);
             if(!mi.display)
                mi.display=true;
             if(mi.corpos==mi.posbot) ObjectDelete(mi.CI,mi.arbot);
             if(mi.corpos==mi.postop) ObjectDelete(mi.CI,mi.artop);
             mi.DrawOBVObject();
             //--
             ChartRedraw(mi.CI);
            }
          //--- if "artop" button is click
          if(sparam==mi.artop)
            {
             mi.DeletedOBVObject();
             mi.DisplayPanelButton();
             //--- unpress the button
             ObjectSetInteger(mi.CI,mi.artop,OBJPROP_STATE,false);
             ObjectSetInteger(mi.CI,mi.artop,OBJPROP_ZORDER,0);
             if(!mi.display)
                mi.display=true;
             ObjectDelete(mi.CI,mi.artop);
             mi.PanelPosChange(mi.postop);
             //--
             ObjectDelete(mi.CI,"X");
             mi.DisplayButtonClick("arbot");
             mi.DrawOBVObject();
             //--
             ChartRedraw(mi.CI);
            }
          //--- if "arbot" button is click
          if(sparam==mi.arbot)
            {
             mi.DeletedOBVObject();
             mi.DisplayPanelButton();
             //--- unpress the button
             ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_STATE,false);
             ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_ZORDER,0);
             if(!mi.display)
                mi.display=true;
             ObjectDelete(mi.CI,mi.arbot);
             mi.PanelPosChange(mi.posbot);
             //--
             ObjectDelete(mi.CI,"X");
             mi.DisplayButtonClick("artop");
             mi.DrawOBVObject();
             //--
             ChartRedraw(mi.CI);
            }
          //--- if TF button is click
          //--
          if(sparam==mi.TFSc[0])
            {
             mi.ChangeChartSymbol(mi.TFSc[0],mi.TFId[0]);
            }
          //--
          if(sparam==mi.TFSc[1])
            {
             mi.ChangeChartSymbol(mi.TFSc[1],mi.TFId[1]);
            }
          //--
          if(sparam==mi.TFSc[2])
            {
             mi.ChangeChartSymbol(mi.TFSc[2],mi.TFId[2]);
            }
          //--
          if(sparam==mi.TFSc[3])
            {
             mi.ChangeChartSymbol(mi.TFSc[3],mi.TFId[3]);
            }
          //--
          if(sparam==mi.TFSc[4])
            {
             mi.ChangeChartSymbol(mi.TFSc[4],mi.TFId[4]);
            }
          //--
          if(sparam==mi.TFSc[5])
            {
             mi.ChangeChartSymbol(mi.TFSc[5],mi.TFId[5]);
            }
          //--
          if(sparam==mi.TFSc[6])
            {
             mi.ChangeChartSymbol(mi.TFSc[6],mi.TFId[6]);
            }
          //--
          if(sparam==mi.TFSc[7])
            {
             mi.ChangeChartSymbol(mi.TFSc[7],mi.TFId[7]);
            }
          //--
          if(sparam==mi.TFSc[8])
            {
             mi.ChangeChartSymbol(mi.TFSc[8],mi.TFId[8]);
            }
          //--
          if(sparam==mi.TFSc[9])
            {
             mi.ChangeChartSymbol(mi.TFSc[9],mi.TFId[9]);
            }
          //--
          if(sparam==mi.TFSc[10])
            {
             mi.ChangeChartSymbol(mi.TFSc[10],mi.TFId[10]);
            }
          //--
          if(sparam==mi.TFSc[11])
            {
             mi.ChangeChartSymbol(mi.TFSc[11],mi.TFId[11]);
            }
          //--
          if(sparam==mi.TFSc[12])
            {
             mi.ChangeChartSymbol(mi.TFSc[12],mi.TFId[12]);
            }
          //--
          if(sparam==mi.TFSc[13])
            {
             mi.ChangeChartSymbol(mi.TFSc[13],mi.TFId[13]);
            }
          //--
          if(sparam==mi.TFSc[14])
            {
             mi.ChangeChartSymbol(mi.TFSc[14],mi.TFId[14]);
            }
          //--
          if(sparam==mi.TFSc[15])
            {
             mi.ChangeChartSymbol(mi.TFSc[15],mi.TFId[15]);
            }
          //--
          if(sparam==mi.TFSc[16])
            {
             mi.ChangeChartSymbol(mi.TFSc[16],mi.TFId[16]);
            }
          //--
          if(sparam==mi.TFSc[17])
            {
             mi.ChangeChartSymbol(mi.TFSc[17],mi.TFId[17]);
            }
          //--
          if(sparam==mi.TFSc[18])
            {
             mi.ChangeChartSymbol(mi.TFSc[18],mi.TFId[18]);
            }
          //--
          if(sparam==mi.TFSc[19])
            {
             mi.ChangeChartSymbol(mi.TFSc[19],mi.TFId[19]);
            }
          //--
          if(sparam==mi.TFSc[20])
            {
             mi.ChangeChartSymbol(mi.TFSc[20],mi.TFId[20]);
            }
          //--
         }
    //---
      } //-end OnChartEvent()
    //---------//
    

    Please download the OBV_MTF indicator: On Balance Volume Multi Timeframe indicator


    © 2025 OBV_MTF - Developed by Roberto Jacobs (3rjfx)

Tagged: , , , , , ,

0 comments:

Post a Comment

Featured Post

How to create a simple Multi-Currency Expert Advisor using MQL5 with Zigzag and RSI Indicators Signal

Author: Roberto Jacobs (3rjfx) | Featured on Forex Home Expert Introduction The Expert Advisor discussed in this article is a mult...