preview
Trading Insights Through Volume: Moving Beyond OHLC Charts

Trading Insights Through Volume: Moving Beyond OHLC Charts

MetaTrader 5Examples | 25 November 2024, 10:38
1 718 2
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introduction

In the field of technical analysis, traders have historically relied on price fluctuations as their primary indicator of market activity. This work presents a unique methodology that combines advanced mathematical analysis of volume with machine learning techniques, with an emphasis on how volume patterns may provide deeper insights into market dynamics. We provide a sophisticated system that exceeds conventional technical analysis methods by combining LSTM neural networks with calculus-based volume derivatives.

The article is broken up into three main sections: the first discusses the fundamental importance of volume analysis and its mathematical bases; the second examines the practical applications of volume derivatives and LSTM predictions in identifying market opportunities; and the third explains how volume divergence analysis, trend confirmation, and breakout validation can be used to enhance trading decisions.


Understanding Volume's Core Function

The total number of shares or contracts traded during a specific time period is known as volume, and it is a crucial indicator of market activity and liquidity. While price shows the market's direction, volume shows the confidence behind a market movement, highlighting the underlying strength and durability of trends. It is the force behind price action and has the ability to support or contradict price movements since significant price changes with high volume frequently indicate higher levels of market confidence than those with little volume. Furthermore, volume patterns often predict price changes, making them valuable indications for traders who understand how to use them in conjunction with price action and market context.


Why Volume Matters

Simple Example:

The crucial importance of volume analysis may be demonstrated using a sophisticated mathematical technique that goes beyond standard analysis, providing traders with a more advanced framework for comprehending market dynamics than is possible with conventional indicators alone. By focusing just on volume dynamics, independent of OHLC data, we can find important market insights through the use of calculus and machine learning, with a special emphasis on volume derivatives and LSTM neural networks. This novel method uses sophisticated mathematical ideas to identify minute shifts in trade and market participation, giving early warnings of possible market movements before they show up in price action.

Traders may detect new trends and possible reversals more precisely than is possible with conventional volume analysis techniques by looking at the rate of change in volume (first derivative) and its acceleration (second derivative) in conjunction with machine learning forecasts. When applied correctly, this mathematical basis provides a strong approach to market analysis that might be especially helpful in today's algorithmically driven markets, when conventional technical analysis might not be sufficient.

This method looks at three important volume components: the first derivative (rate of volume change), the second derivative (acceleration of volume change), and LSTM projections of future volume that confirm the same volume derivates. By removing pricing noise, this approach focuses solely on market participation dynamics. 

The EA begins with standard MetaTrader declarations. It includes the Trade library for order management and the custom Volume_LSTM library for predictions. The indicator_separate_window property indicates this will display in a separate window, and it allocates three indicator buffers for visualization.
#property copyright "Your Name"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 3
#include <Trade\Trade.mqh>
#include <Volume_LSTM.mqh>
CTrade trade;

The parameters are organized into logical groups. The LSTM configuration section controls the neural network behavior, while the entry parameters manage the trading rules. Each parameter has a specific purpose: the LSTM parameters control the prediction model's complexity and memory. The trading parameters define position sizing and profit targets and the risk management parameters set loss limits and trailing stops.

input group "Configuración LSTM"
input bool     UseLSTM = true;              
input int      LSTMHiddenSize = 32;         
input int      LSTMLookback = 10;           

input group "Entry Parameters"
input double   Lots = 0.1;         
input double   TPPoints = 100;      
input int      Periods = 4;         
input int      AccelBars = 4;       
input int      MaxBarsInTrade = 2; 
input double   ProfitClose = 15;    
input double   MaxDailyLoss = -25; 
input double   TrailingStart = 5;  
input double   TrailingStep = 5;

This function calculates the rate of change in volume (first derivative) and the acceleration of volume change (second derivative). It shifts existing derivatives to maintain history, calculates new first derivative as the difference between current and previous volume, calculates new second derivative as the change in first derivatives and provides insights into volume momentum and acceleration.

By looking at the first derivative of volume, we are essentially tracking whether trading activity is increasing or decreasing over time. This pattern of volume fluctuation gives us our first layer of insight into market momentum. A consistently positive first derivative suggests increasing market interest, whereas a negative one suggests decreasing engagement.

void CalculateDerivatives() {
    for(int i = 0; i < ArraySize(volumes)-1; i++) {
        firstDerivative[i] = firstDerivative[i+1];
        secondDerivative[i] = secondDerivative[i+1];
    }

    firstDerivative[ArraySize(volumes)-1] = volumes[ArraySize(volumes)-1] - volumes[ArraySize(volumes)-2];
    secondDerivative[ArraySize(volumes)-1] = firstDerivative[ArraySize(volumes)-1] - firstDerivative[ArraySize(volumes)-2];
}

These functions manage the trade lifecycle, TrackNewTrade records new positions in the first available slot,  RemoveTrade clears the record when positions are closed. Both functions maintain the integrity of the trade tracking system.

void TrackNewTrade(ulong ticket) {
    for(int i = 0; i < ArraySize(openTrades); i++) {
        if(openTrades[i].ticket == 0) {
            openTrades[i].ticket = ticket;
            openTrades[i].openTime = TimeCurrent();
            openTrades[i].barsOpen = 0;
            break;
        }
    }
}

void RemoveTrade(ulong ticket) {
    for(int i = 0; i < ArraySize(openTrades); i++) {
        if(openTrades[i].ticket == ticket) {
            openTrades[i].ticket = 0;
            openTrades[i].openTime = 0;
            openTrades[i].barsOpen = 0;
            break;
        }
    }
}

This profit tracking function:

  1. Identifies the current trading day
  2. Calculates realized profit from closed trades
  3. Adds unrealized profit from open positions
  4. Maintains daily profit tracking across multiple trades
  5. Helps enforce daily loss limits
double GetDailyProfit() {
    datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));

    if(lastDayChecked != today) {
        lastDayChecked = today;
        dailyProfit = 0;

        HistorySelect(today, TimeCurrent());
        int deals = HistoryDealsTotal();

        for(int i = 0; i < deals; i++) {
            ulong ticket = HistoryDealGetTicket(i);
            if(ticket > 0) {
                if(HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol)
                    dailyProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT);
            }
        }
    }
    
    // Add floating P/L
    double floatingProfit = 0;
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(ticket > 0 && PositionSelectByTicket(ticket)) {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol)
                floatingProfit += PositionGetDouble(POSITION_PROFIT);
        }
    }

    return dailyProfit + floatingProfit;
}

The trailing stop function:

  1. Iterates through all open positions
  2. Calculates profit in points for each position
  3. Updates stop loss levels based on profit
  4. Implements a trailing mechanism to lock in profits
  5. Handles both buy and sell positions differently
void ApplyTrailingStop(double point) {
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(ticket <= 0) continue;

        if(PositionSelectByTicket(ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            double currentSL = PositionGetDouble(POSITION_SL);
            long posType = PositionGetInteger(POSITION_TYPE);

            // Calculate and update trailing stop
            double profitPoints;
            if(posType == POSITION_TYPE_BUY) {
                profitPoints = (currentPrice - openPrice) / point;
                if(profitPoints >= TrailingStart) {
                    double newSL = openPrice + (profitPoints - TrailingStep) * point;
                    if(currentSL < newSL || currentSL == 0) {
                        trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
                    }
                }
            }
            // Similar logic for SELL positions
        }
    }
}

By showing the acceleration or slowdown of trade activity, the second derivative of volume provides even more detailed information. A positive value for this second derivative is a strong indication of growing market momentum as it shows that volume is rising and doing so at an accelerating rate. Because it indicates quickly increasing market participant interest, this volume acceleration frequently occurs before notable market movements. We provide a strong framework for predicting market behavior by fusing these derivatives with LSTM neural network predictions of future volume patterns. Our study gains an extra layer of validation from the LSTM's capacity to identify intricate patterns in volume sequences, which aids in determining if the observed volume trends are likely to persist.

The main trading logic:

  1. Checks for new bar formation
  2. Verifies daily loss limits
  3. Updates trade information
  4. Performs volume analysis
  5. Generates LSTM predictions if enabled
  6. Makes trading decisions based on multiple factors
  7. Opens positions when conditions align
void OnTick() {
    static datetime lastBar = 0;
    datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);

    if(lastBar == currentBar) return;
    lastBar = currentBar;

    if(GetDailyProfit() < MaxDailyLoss) {
        CloseAllPositions();
        return;
    }

    // Update and calculate
    UpdateTradesInfo();
    CheckTimeBasedClose();
    
    if(!CanTrade()) return;

    // Volume analysis and LSTM prediction
    // ... volume calculations ...
    
    if(UseLSTM && volumePredictor != NULL) {
        // LSTM prediction logic
        // ... prediction calculations ...
    }

    CalculateDerivatives();
    
    // Trading decisions based on calculations
    if(consecutiveAccel >= AccelBars) {
        if((!UseADX && !UseOBV) || ConfirmLongSignal()) {
            // Open buy position
        }
    }
    else if(consecutiveAccel <= -AccelBars) {
        if(!UseADX && !UseOBV || ConfirmShortSignal()) {
            // Open sell position
        }
    }
}

The technical indicator calculations:

  1. Set up data buffers for indicator values
  2. Copy indicator data from handles
  3. Update local arrays with new values
  4. Maintain indicator data for decision making
void CalculateADX() {
    if(!UseADX) return;

    double adx_buffer[];
    double plusdi_buffer[];
    double minusdi_buffer[];
    ArraySetAsSeries(adx_buffer, true);
    ArraySetAsSeries(plusdi_buffer, true);
    ArraySetAsSeries(minusdi_buffer, true);

    CopyBuffer(adx_handle, MAIN_LINE, 0, ArraySize(adx_main), adx_buffer);
    CopyBuffer(adx_handle, PLUSDI_LINE, 0, ArraySize(adx_plus), plusdi_buffer);
    CopyBuffer(adx_handle, MINUSDI_LINE, 0, ArraySize(adx_minus), minusdi_buffer);

    ArrayCopy(adx_main, adx_buffer);
    ArrayCopy(adx_plus, plusdi_buffer);
    ArrayCopy(adx_minus, minusdi_buffer);
}

Each component works together to create a comprehensive trading system that combines volume analysis, LSTM predictions, and traditional technical indicators while maintaining robust risk management practices.

The Volume_LSTM.mqh file implements a Long Short-Term Memory (LSTM) neural network specifically designed for predicting trading volumes.

The code starts with a fundamental Matrix2D structure that handles 2D matrix operations:

struct Matrix2D {
    double values[];
    int rows;
    int cols;
    
    void Init(int r, int c) {
        rows = r;
        cols = c;
        ArrayResize(values, rows * cols);
        ArrayInitialize(values, 0);
    }
    // ... other methods
}

The LSTM cell is implemented as a comprehensive structure:

struct LSTMCell {
    double forget_gate[];
    double input_gate[];
    double cell_state[];
    double output_gate[];
    double hidden_state[];
    
    Matrix2D Wf;  // Forget gate weights
    Matrix2D Wi;  // Input gate weights
    Matrix2D Wc;  // Cell state weights
    Matrix2D Wo;  // Output gate weights
    
    double bf[];  // Forget gate bias
    double bi[];  // Input gate bias
    double bc[];  // Cell state bias
    double bo[];  // Output gate bias
    
    int hidden_size;
    int input_size;
};

The implementation includes standard neural network activation functions:

double Sigmoid(double x) {
    return 1.0 / (1.0 + MathExp(-x));
}

double Tanh(double x) {
    return (MathExp(x) - MathExp(-x)) / (MathExp(x) + MathExp(-x));
}

The weights are initialized using a variation of Xavier/Glorot initialization:

void InitializeWeights() {
    double scale = MathSqrt(2.0 / (lstm.input_size + lstm.hidden_size));
    
    // Initialize weight matrices
    lstm.Wf.Init(lstm.hidden_size, lstm.input_size);
    lstm.Wi.Init(lstm.hidden_size, lstm.input_size);
    // ... other initializations

    // Random initialization with scaling
    for(int i = 0; i < lstm.hidden_size; i++) {
        for(int j = 0; j < lstm.input_size; j++) {
            lstm.Wf.Set(i, j, (MathRand() / 32768.0 - 0.5) * scale);
            // ... other weight initializations
        }
    }
}

The implementation includes data normalization:

void NormalizeVolumes() {
    volume_mean = 0;
    double sum_squared = 0;
    
    // Calculate mean
    for(int i = 0; i < ArraySize(historical_volumes); i++) {
        volume_mean += historical_volumes[i];
    }
    volume_mean /= ArraySize(historical_volumes);
    
    // Calculate standard deviation
    for(int i = 0; i < ArraySize(historical_volumes); i++) {
        sum_squared += MathPow(historical_volumes[i] - volume_mean, 2);
    }
    volume_std = MathSqrt(sum_squared / ArraySize(historical_volumes));
    
    // Normalize volumes
    if(volume_std == 0) volume_std = 1;  // Prevent division by zero
    for(int i = 0; i < ArraySize(historical_volumes); i++) {
        historical_volumes[i] = (historical_volumes[i] - volume_mean) / volume_std;
    }
}

The core prediction logic:

double PredictNextVolume() {
    if(!is_initialized) return 0;
    
    double input1 = historical_volumes[ArraySize(historical_volumes)-1];
    
    // LSTM forward pass
    for(int h = 0; h < lstm.hidden_size; h++) {
        double ft = 0, it = 0, ct = 0, ot = 0;
        
        // Calculate gates
        for(int i = 0; i < lstm.input_size; i++) {
            ft += lstm.Wf.Get(h, i) * input1;
            it += lstm.Wi.Get(h, i) * input1;
            ct += lstm.Wc.Get(h, i) * input1;
            ot += lstm.Wo.Get(h, i) * input1;
        }
        
        // Apply gates and calculate states
        lstm.forget_gate[h] = Sigmoid(ft + lstm.bf[h]);
        lstm.input_gate[h] = Sigmoid(it + lstm.bi[h]);
        double c_tilde = Tanh(ct + lstm.bc[h]);
        lstm.cell_state[h] = lstm.forget_gate[h] * lstm.cell_state[h] + 
                            lstm.input_gate[h] * c_tilde;
        lstm.output_gate[h] = Sigmoid(ot + lstm.bo[h]);
        lstm.hidden_state[h] = lstm.output_gate[h] * Tanh(lstm.cell_state[h]);
    }
    
    // Calculate final prediction
    double prediction = 0;
    for(int h = 0; h < lstm.hidden_size; h++)
        prediction += lstm.hidden_state[h];
    prediction /= lstm.hidden_size;
    
    return prediction * volume_std + volume_mean;  // Denormalize
}


Results

EURUSD, this are the results for EURUSD with 30 min time periods. This are the settings  Setting_eurusd.set

Settings

Graph

Backtesting

GBPUSD with 30 min time frame period.

GBPUSD settings.set

Graph

Backtesting

A big optimization might lead to get better results in big pairs, and lack of time don't help to extend this article. I strongly recommend to do a big optimization for each pair, and hope some users add comments with new possible setups at comments.


Advanced Techniques

Trend Confirmation

High volume during price increases indicates strong market support and a likely continuation of the trend. Conversely, high volume during price decreases reflects strong selling pressure and confirms a negative trend. Low volume during price movements warns of weak or unsustainable trends, often signaling false breakouts or manipulation.

Breakout Validation

Breakouts on high volume confirm market conviction, leading to sustained price changes. Low-volume breakouts often result in false signals, such as "bull traps" or "bear traps," and are likely to fail. Volume spikes during breakouts strongly validate the move and indicate robust market support.

Volume Divergence

Volume divergence, where price and volume trends differ, signals potential reversals or weakness in trends. Falling volume with rising prices indicates a weakening uptrend, while falling volume with declining prices suggests reduced selling pressure and possible market bottoming. These patterns can help identify turning points when combined with other indicators.


Conclusion

By fusing cutting-edge machine learning methods with conventional volume analysis, the Volume-Based Trading EA offers an advanced method of algorithmic trading. The system exhibits a strong approach for market research and trade execution by utilizing LSTM neural networks for volume prediction in addition to traditional technical indicators like ADX and OBV.

The EA's multi-layered approach to risk management, which includes stringent daily loss limits, dynamic trailing stops, and thorough position tracking, is its strongest point. This emphasis on risk management, together with adjustable entry parameters and variable session management, gives traders a flexible tool that can adjust to different market situations while upholding disciplined trading procedures.

Most significantly, a special hybrid system is produced by combining volume derivatives analysis with LSTM predictions. Because of this combination, the EA may be able to spot market changes before they completely materialize, and the conventional indicators' validation helps cut down on false signals. Future improvements and simple maintenance are also made possible by the modular code structure.

Users should be aware, however, that due to the complexity of the system, meticulous backtesting and parameter tuning are necessary before live deployment. Despite its strength, the LSTM component requires a large amount of processing power and appropriate training data. Despite these issues, the EA is a useful tool for traders looking to automate their volume-based trading methods because of its thorough commitment to fusing cutting-edge technology with tried-and-true trading concepts.

I hope this article is useful to considerate the use OHLCV and not just prices.


File Name Description
MQL5/Profiles/Tester/Setting_eurusd.set Setting inputs for example with EURUSD
MQL5/Profiles/Tester/GBPUSD_settings.set
Setting inputs for example GBPUSD
MQL5/Experts/Volume_high_low_difference_LSTM.mq5 Expert Advisor to use for the examples
MQL5/Include/Volume_LSTM.mqh Include mqh file. Remember to change the path in the EA as to where you saved it


Last comments | Go to discussion (2)
TaiKingFX
TaiKingFX | 25 Nov 2024 at 19:54
You are a gen. I have been looking for an approach of this level of analysis with volumes. Have considered using zigzag indicators for price direction and a little of demand and Discount zone? Will try to do this if time permits. 
Javier Santiago Gaston De Iriarte Cabrera
TaiKingFX #:
You are a gen. I have been looking for an approach of this level of analysis with volumes. Have considered using zigzag indicators for price direction and a little of demand and Discount zone? Will try to do this if time permits. 

Hi Thanks! not very sure about the gen ... but found this interesting. I will make an article with zigzag indicator and volumes. I will also continue with more strategies using volumes. Moving of country and will need some time to work more.

MQL5 Wizard Techniques you should know (Part 49): Reinforcement Learning with Proximal Policy Optimization MQL5 Wizard Techniques you should know (Part 49): Reinforcement Learning with Proximal Policy Optimization
Proximal Policy Optimization is another algorithm in reinforcement learning that updates the policy, often in network form, in very small incremental steps to ensure the model stability. We examine how this could be of use, as we have with previous articles, in a wizard assembled Expert Advisor.
Developing a Replay System (Part 53): Things Get Complicated (V) Developing a Replay System (Part 53): Things Get Complicated (V)
In this article, we'll cover an important topic that few people understand: Custom Events. Dangers. Advantages and disadvantages of these elements. This topic is key for those who want to become a professional programmer in MQL5 or any other language. Here we will focus on MQL5 and MetaTrader 5.
Creating a Trading Administrator Panel in MQL5 (Part VII): Trusted User, Recovery and Cryptography Creating a Trading Administrator Panel in MQL5 (Part VII): Trusted User, Recovery and Cryptography
Security prompts, such as those triggered every time you refresh the chart, add a new pair to the chat with the Admin Panel EA, or restart the terminal, can become tedious. In this discussion, we will explore and implement a feature that tracks the number of login attempts to identify a trusted user. After a set number of failed attempts, the application will transition to an advanced login procedure, which also facilitates passcode recovery for users who may have forgotten it. Additionally, we will cover how cryptography can be effectively integrated into the Admin Panel to enhance security.
Creating a Trading Administrator Panel in MQL5 (Part VI):Trade Management Panel (II) Creating a Trading Administrator Panel in MQL5 (Part VI):Trade Management Panel (II)
In this article, we enhance the Trade Management Panel of our multi-functional Admin Panel. We introduce a powerful helper function that simplifies the code, improving readability, maintainability, and efficiency. We will also demonstrate how to seamlessly integrate additional buttons and enhance the interface to handle a wider range of trading tasks. Whether managing positions, adjusting orders, or simplifying user interactions, this guide will help you develop a robust, user-friendly Trade Management Panel.