Larry Williams Market Secrets (Part 14): Detecting Hidden Smash Day Reversals with a Custom Indicator
Introduction
Understanding Hidden Smash Day Reversal Patterns
Hidden Smash Day patterns are a special variation of Larry Williams' Smash Day Reversals. At first glance, these bars support the current market direction. However, a closer look at their internal structure reveals weakness rather than strength. This contradiction between appearance and reality is what gives the pattern its name.
The key idea behind a hidden smash day is failure. The market attempts to move strongly in one direction during the session, but that effort is rejected before the close. Although price may finish the session slightly higher or lower, the closing location within the bar tells a very different story.
The Hidden Smash Day Buy Setup
A hidden smash day buy setup begins with a bar that closes higher than the previous bar. On the surface, this looks bullish. However, the important detail is where that close occurs within the bar’s own range.

For a valid Hidden Smash Day Buy Bar, the close must be located in the lower portion of the bar’s range. In the strongest versions of the pattern, the close is also below the opening price. This means that although the session finished, buyers failed to hold most of the gains made earlier in the bar. The rally was met with selling pressure, and the buying effort was effectively neutralized.
This creates a trap. Traders who focus only on the up close may interpret the bar as strength, while the internal structure signals exhaustion. The pattern is not complete on this bar alone. Confirmation is required.
A bullish signal is confirmed only if the following bar closes above the high of the Hidden Smash Day Bar. When this happens, it shows that the failed move has been reversed and that demand has returned with enough strength to overcome the prior rejection. This sequence of failure followed by immediate recovery forms the foundation of the Hidden Smash Day Buy Reversal.
The Hidden Smash Day Sell Setup
The Hidden Smash Day Sell Setup is the mirror image of the buy pattern. In this case, the bar closes lower than the previous bar, which initially appears bearish. Once again, the internal structure tells the real story.

For a valid Hidden Smash Day Sell Bar, the close must be located in the upper portion of the bar’s range. In the strongest patterns, the close is also above the opening price. This indicates that sellers pushed prices lower during the session but failed to maintain control. Price recovered towards the highs before the close, exposing weakness in the selling pressure.
This creates another trap. Traders may interpret the down close as confirmation of weakness, while the bar itself shows rejection of lower prices.
A bearish signal is confirmed only if the following bar closes below the low of the Hidden Smash Bar. This breakdown confirms that the attempted recovery failed and that selling pressure has returned with conviction. The pattern once again follows the same theme of failure and immediate reversal.
Rather than predicting reversals, Hidden Smash Day Patterns wait for the market to reveal its own weakness, then confirm the shift through price action. This makes them especially useful within a rule-based trading framework and a strong candidate for systematic automation.
How the Hidden Smash Day Indicator Is Designed
Before diving deeper into the implementation, it is important to clearly understand how the indicator is designed to communicate information visually and logically. A good technical indicator should do two things well. It should accurately identify the pattern and present it in an easy-to-read format. This indicator was designed with both goals in mind.
The indicator's visual language is intentionally simple. A sea green upward arrow is plotted directly below a Hidden Smash Day Buy Bar. This arrow highlights moments where bullish pressure failed during the session, setting the stage for a potential upside reversal. In contrast, a black downward arrow is plotted above a Hidden Smash Day Sell Bar. This marks situations where selling pressure appeared dominant early on but ultimately failed, often preceding a downside reversal. By placing the arrows directly at the bar where the pattern forms, the indicator makes it easy to spot these setups without cluttering the chart.
Beyond visuals, flexibility is a core part of the design. Hidden Smash Day patterns can be interpreted in multiple ways, depending on trading style and confirmation preferences. For this reason, the indicator supports two identification modes, selectable via input parameters.
In the first mode, a Hidden Smash Day Bar is marked immediately upon forming and meeting Larry Williams' criteria. This mode is useful for traders who want early awareness of potential reversals and prefer to monitor price behaviour closely after the setup appears.
In the second mode, the indicator waits for confirmation before marking the pattern. For a Hidden Smash Day Buy setup, confirmation occurs only if the next bar closes above the Hidden Smash Day Bar's high. For a sell setup, confirmation requires the next bar to close below the Hidden Smash Day Bar's low. This approach filters out weaker setups and focuses only on patterns in which price action clearly confirms a failed move.
By exposing these modes as configurable inputs, the indicator adapts to different workflows without forcing a single interpretation. Traders can choose between early detection and confirmed signals depending on their strategy and risk tolerance.
To help visualize how all of these design choices come together, a screenshot of the final indicator launched on a live chart is shown below.

This example highlights how clearly the arrows stand out and how naturally the indicator integrates into a clean price chart without overwhelming it.
Implementing the Hidden Smash Day Indicator in MQL5
With the core idea of Hidden Smash Day reversals clearly defined, the next step is to turn that concept into a working custom indicator. In this section, we begin the practical implementation using MQL5. The goal here is not to rush towards signals, but to lay a clean, reliable foundation that supports accurate detection and visual marking of Hidden Smash Day patterns.
Before writing any logic, it is important to properly prepare the development environment. This includes a basic understanding of MQL5 syntax, familiarity with the MetaTrader 5 terminal, and confidence working inside MetaEditor. With these prerequisites in place, we can move forward and build the indicator step by step.
The complete implementation of the indicator is attached to this article as lwHiddenSmashDayIndicator.mq5. Coding alongside the tutorial is strongly encouraged. Writing the code manually and comparing it with the attached version helps reinforce understanding and makes it easier to diagnose issues if they arise.
Creating the Indicator Skeleton
We begin by creating a new empty Custom Indicator file inside MetaEditor. The file name can be chosen freely, though using the same name as the attached source helps maintain consistency. Once the file is created, the initial boilerplate code is pasted in place.
//+------------------------------------------------------------------+ //| lwHiddenSmashDayIndicator.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Custom Indicator specific directives | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_plots 2 #property indicator_buffers 2 //+------------------------------------------------------------------+ //| Defines how Hidden Smash Day patterns are validated | //+------------------------------------------------------------------+ enum ENUM_HIDDEN_SMASH_VALIDATION_MODE { HIDDEN_SMASH_BAR_ONLY, HIDDEN_SMASH_CONFIRMED }; //+------------------------------------------------------------------+ //| Hidden Smash Detection Settings | //+------------------------------------------------------------------+ input ENUM_HIDDEN_SMASH_VALIDATION_MODE hiddenSmashValidationMode = HIDDEN_SMASH_CONFIRMED; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; //+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double buySmashArrowBuffer []; double sellSmashArrowBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance ", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { ArraySetAsSeries(open, false); ArraySetAsSeries(high, false); ArraySetAsSeries(low, false); ArraySetAsSeries(close, false); //--- Temporary buffers used to store price data for rendering custom colored candles double lwOpen []; double lwHigh []; double lwLow []; double lwClose[]; //--- Copy price data into local working buffers to safely manipulate candle values without altering the original price arrays ArrayCopy(lwOpen, open); ArrayCopy(lwHigh, high); ArrayCopy(lwLow, low); ArrayCopy(lwClose, close); //--- This block will be executed whenever the indicator is initially attached to a chart if(prev_calculated == 0){ //--- Start with clean arrays } //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open } //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick } return(rates_total); } //+------------------------------------------------------------------+ //| This function configures the chart's appearance. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
This initial code defines the indicator's structure and prepares all essential components that will be expanded later. At this stage, no trading logic or pattern detection is performed. The focus is on organization, clarity, and future extensibility.
Indicator Properties and Metadata
//+------------------------------------------------------------------+ //| lwHiddenSmashDayIndicator.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00"
The first section of the source file defines general metadata such as copyright information, version number, and reference links. These declarations are not just formalities. They provide clarity about authorship and help with version tracking as the indicator evolves.
Immediately after that, indicator-specific directives are declared. These directives inform the terminal that the indicator will be drawn directly on the chart window and that it will use two plots and two buffers. This decision reflects the design goal of visually marking both bullish and bearish hidden smash bars using arrows.
Validation Mode Configuration
Hidden Smash Day patterns can be interpreted in multiple ways. Some traders prefer to see the pattern as soon as the bar forms, while others require confirmation from the following bar. To support both approaches, a custom enumeration is defined.
//+------------------------------------------------------------------+ //| Defines how Hidden Smash Day patterns are validated | //+------------------------------------------------------------------+ enum ENUM_HIDDEN_SMASH_VALIDATION_MODE { HIDDEN_SMASH_BAR_ONLY, HIDDEN_SMASH_CONFIRMED };
The validation mode enumeration introduces two clear options. One option marks the Hidden Smash Day Bar immediately based on its internal structure. The other option marks the bar only after confirmation in the next session. This choice is exposed as a user input parameter, allowing the indicator to adapt to different analysis styles without requiring code changes.
//+------------------------------------------------------------------+ //| Hidden Smash Detection Settings | //+------------------------------------------------------------------+ input ENUM_HIDDEN_SMASH_VALIDATION_MODE hiddenSmashValidationMode = HIDDEN_SMASH_CONFIRMED;
By introducing this configuration early, the indicator logic remains flexible and avoids hard-coded assumptions.
Global Scope and Timeframe Handling
A global timeframe variable is declared and set to the current chart timeframe.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
This ensures that the indicator automatically adapts to any chart it is attached to. Whether the chart is hourly, four-hour, or daily, the same logic applies without modification.
This approach keeps the indicator consistent with Larry Williams' principles while allowing it to operate across multiple timeframes.
Indicator Buffers and Visual Output
Two indicator buffers are declared.
//+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double buySmashArrowBuffer []; double sellSmashArrowBuffer[];
One buffer is reserved for Bullish Hidden Smash Day Bars, and the other for Bearish Hidden Smash Day Bars. These buffers will later store price values where arrows should be drawn.
At this stage, the buffers are empty by design. The indicator does not mark anything until valid conditions are detected. This keeps the chart clean and avoids misleading signals.
The visual intent is simple and deliberate. A sea green arrow will be drawn below a bullish hidden smash bar, while a black arrow will be drawn above a bearish hidden smash bar. This color and placement choice creates immediate visual contrast and helps traders quickly identify potential reversal zones.
Initialization Logic
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
Inside the initialization function, the indicator configures the chart appearance. The background, candle colors, and grid visibility are adjusted to improve clarity and readability during analysis and testing.
This step does not affect price data or indicator calculations. Its sole purpose is to create a clean visual environment that facilitates pattern recognition. If the chart configuration fails for any reason, the indicator stops loading and reports the error. This protects against partial or misleading visual output.
Calculation Framework
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { ArraySetAsSeries(open, false); ArraySetAsSeries(high, false); ArraySetAsSeries(low, false); ArraySetAsSeries(close, false); //--- Temporary buffers used to store price data for rendering custom colored candles double lwOpen []; double lwHigh []; double lwLow []; double lwClose[]; //--- Copy price data into local working buffers to safely manipulate candle values without altering the original price arrays ArrayCopy(lwOpen, open); ArrayCopy(lwHigh, high); ArrayCopy(lwLow, low); ArrayCopy(lwClose, close); //--- This block will be executed whenever the indicator is initially attached on a chart if(prev_calculated == 0){ //--- Start with clean arrays } //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open } //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick } return(rates_total); }
The calculation function is where all indicator logic will eventually live. For now, it establishes a structured workflow that separates different execution scenarios.
Price arrays are handled carefully by copying them into local working buffers. This allows safe manipulation of data without altering the original price series provided by the terminal.
Three execution paths are clearly separated. One path handles the case where the indicator is attached to a chart for the first time. Another handles the arrival of a new bar. The final path handles incoming tick data while the current bar is forming.
This separation is intentional. Hidden Smash Day patterns depend on completed bars and confirmation logic. By organizing the execution flow early, we ensure that pattern detection occurs at the right time and does not repaint or generate unstable signals.
At this stage, these blocks are placeholders. Their presence signals where logic will be added later and ensures the indicator grows in a controlled, predictable way.
With this foundation in place, the next step is to introduce the actual detection logic for Hidden Smash Day Buy and Sell patterns and connect it to the indicator buffers. From here onward, each addition will build naturally on what has already been defined.
First, we ensure the chart environment is clean and readable. Second, we define reusable functions that identify Hidden Smash Day patterns exactly as described by Larry Williams. Third, we connect those functions to the indicator lifecycle so that patterns are detected historically, on new bars, and in real time. Each step builds directly on the previous one, keeping the indicator structured, predictable, and easy to maintain.
Preparing the Chart for Visual Clarity
Before any detection logic is applied, we reset the chart appearance to ensure that visual signals are easy to interpret during both testing and live use.
//+------------------------------------------------------------------+ //| This function configures the chart's appearance. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } return true; }
The ConfigureChartAppearance function centralizes all chart styling rules in a single location. It sets a light background, removes unnecessary grid lines, and applies contrasting colors to bullish and bearish candles. This creates a neutral visual environment where the indicator's arrows remain clearly visible.
Calling this function from inside OnInit ensures the chart is configured once, immediately after the indicator is attached, and does not interfere with price calculations or buffer updates later.
Registering and Preparing Indicator Buffers
With the chart prepared, the next step is to initialize and register the indicator buffers. The indicator uses two buffers:
- One buffer stores values for Bullish Hidden Smash Day Bars
- The second buffer stores values for Bearish Hidden Smash Day Bars
Both buffers are explicitly set to non-time series indexing.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Set arrays as series ArraySetAsSeries(buySmashArrowBuffer, false); ArraySetAsSeries(sellSmashArrowBuffer, false); return(INIT_SUCCEEDED); }
This means that index 0 represents the earliest bar in history rather than the most recent. This decision is deliberate and critical, as all detection logic assumes forward-indexed arrays when looping through historical data.
After setting the index buffer direction, the buffers are registered with SetIndexBuffer.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Bind arrays to indicator buffers SetIndexBuffer(0, buySmashArrowBuffer, INDICATOR_DATA); SetIndexBuffer(1, sellSmashArrowBuffer, INDICATOR_DATA); return(INIT_SUCCEEDED); }
From this point onward, MetaTrader knows exactly where to read data when rendering arrows on the chart.
Defining Hidden Smash Day Bar Detection Logic
With the buffer infrastructure in place, we can now define the functions that identify Hidden Smash Day patterns. Two base functions are created to detect raw patterns:
One identifies Bullish Hidden Smash Day Bars.
//+------------------------------------------------------------------+ //| Identifies a Bullish Hidden Smash Bar at a specific index | //| Larry Williams definition applied to any timeframe | //+------------------------------------------------------------------+ bool IsHiddenSmashBuyBarAtIndex( const int i, const double &open[], const double &high[], const double &low[], const double &close[], const bool requireCloseBelowOpen = false ) { // We need a previous bar to confirm an up close if(i < 1) return false; // 1. Must close higher than the previous bar if(close[i] <= close[i - 1]) return false; // 2. Use the bar's own range double range = high[i] - low[i]; if(range <= 0.0) return false; // 3. Close must be in the lower 25 percent of the bar range double lowerQuarter = low[i] + (range * 0.25); if(close[i] > lowerQuarter) return false; // 4. Best patterns only if requested if(requireCloseBelowOpen && close[i] >= open[i]) return false; return true; }
The other identifies Bearish Hidden Smash Day Bars.
//+------------------------------------------------------------------+ //| Identifies a Bearish Hidden Smash Bar at a specific index | //| Larry Williams definition applied to any timeframe | //+------------------------------------------------------------------+ bool IsHiddenSmashSellBarAtIndex( const int i, const double &open[], const double &high[], const double &low[], const double &close[], const bool requireCloseAboveOpen = false ) { // We need a previous bar to confirm a down close if(i < 1) return false; // 1. Must close lower than the previous bar if(close[i] >= close[i - 1]) return false; // 2. Use the bar's own range double range = high[i] - low[i]; if(range <= 0.0) return false; // 3. Close must be in the upper 25 percent of the bar range double upperQuarter = high[i] - (range * 0.25); if(close[i] < upperQuarter) return false; // 4. Best patterns only if requested if(requireCloseAboveOpen && close[i] <= open[i]) return false; return true; }
Each function evaluates a single bar at a given index and applies Larry Williams' rules precisely. For bullish setups, the bar must close higher than the previous bar while finishing within the lower quarter of its own range. For bearish setups, the logic is mirrored: a lower close and a finish within the upper quarter of the bar range are required.
Optional conditions allow the strictest versions of the pattern to be enforced, such as requiring the close to finish below or above the open for best quality setups. By isolating this logic into dedicated functions, the indicator remains modular and easy to extend later.
Confirming Hidden Smash Day Patterns
Larry Williams emphasized that the behavior of the following bar confirms the strongest Hidden Smash Day patterns. To support this, two confirmation functions are introduced. A bullish pattern is confirmed only when the next bar closes above the Hidden Smash Bar's high.
//+------------------------------------------------------------------+ //| Returns true if a Hidden Smash Day Buy pattern is confirmed | //| Confirmation requires next bar to close above smash bar high | //+------------------------------------------------------------------+ bool IsConfirmedHiddenSmashBuyAtIndex(const double &open[], const double &high[], const double &low[], const double &close[], int index ) { int confirmationIndex = index + 1; // Ensure confirmation bar exists if(confirmationIndex >= ArraySize(close)) return false; // Smash bar must be valid if(!IsHiddenSmashBuyBarAtIndex(index, open, high, low, close)) return false; // Confirmation close above smash bar high if(close[confirmationIndex] > high[index]) return true; return false; }
A bearish pattern is confirmed only when the next bar closes below the Hidden Smash Day Bar's low.
//+------------------------------------------------------------------+ //| Returns true if a Hidden Smash Day Sell pattern is confirmed | //| Confirmation requires next bar to close below smash bar low | //+------------------------------------------------------------------+ bool IsConfirmedHiddenSmashSellAtIndex(const double &open[], const double &high[], const double &low[], const double &close[], int index ) { int confirmationIndex = index + 1; // Ensure confirmation bar exists if(confirmationIndex >= ArraySize(close)) return false; // Smash bar must be valid if(!IsHiddenSmashSellBarAtIndex(index, open, high, low, close)) return false; // Confirmation close below smash bar low if(close[confirmationIndex] < low[index]) return true; return false; }
These confirmation functions reuse the raw detection logic and add a validation step. This approach avoids duplication and ensures consistent pattern evaluation across the indicator.
Mapping Patterns on Indicator Initialization
When the indicator is first attached to a chart, no historical patterns have been processed yet. To handle this, a dedicated initialization mapping function is introduced.
//+------------------------------------------------------------------+ //| Scans historical bars and maps Hidden Smash patterns on load | //+------------------------------------------------------------------+ void MapHiddenSmashPatternsOnInitialization( const int rates_total, const double &open[], const double &high[], const double &low [], const double &close[], int hidden_SmashValidationMode, double closePercentThreshold = 25.0 ) { //--- Safety check if(rates_total <= 1) return; //--- Loop through all historical bars for(int i = 1; i <= rates_total - 1; i++) { //--- Default to empty buySmashArrowBuffer[i] = EMPTY_VALUE; sellSmashArrowBuffer[i] = EMPTY_VALUE; //--- Mode 1: Bar only identification if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(i, open, high, low, close)) { buySmashArrowBuffer[i] = low[i]; } if(IsHiddenSmashSellBarAtIndex(i, open, high, low, close)) { sellSmashArrowBuffer[i] = high[i]; } } //--- Mode 2: Confirmed pattern identification else if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { if(IsConfirmedHiddenSmashBuyAtIndex(open, high, low, close, i)) { buySmashArrowBuffer[i] = low[i]; } if(IsConfirmedHiddenSmashSellAtIndex(open, high, low, close, i)) { sellSmashArrowBuffer[i] = high[i]; } } } }
This function loops through all available historical bars and evaluates each one according to the selected validation mode. In raw detection mode, bars are evaluated individually. If confirmation mode is selected, bars are evaluated together with their confirming bar.
Each valid pattern results in an arrow value being written to the appropriate buffer. All other bars remain empty, ensuring that only true Hidden Smash Day patterns are displayed. This logic is executed once inside the prev_calculated == 0 block to avoid unnecessary recalculation.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { ... //--- This block will be executed whenever the indicator is initially attached on a chart if(prev_calculated == 0){ //--- Start with clean arrays ArrayInitialize(buySmashArrowBuffer, EMPTY_VALUE); ArrayInitialize(sellSmashArrowBuffer, EMPTY_VALUE); //--- Map all hidden smash bar patterns MapHiddenSmashPatternsOnInitialization(rates_total, lwOpen, lwHigh, lwLow, lwClose, hiddenSmashValidationMode); } ... return(rates_total); }
Updating Patterns on New Bar Formation
Markets do not remain static, so the indicator must react to new bars as they form. When a new bar appears, the indicator evaluates the most recently completed bar and updates buffer values accordingly.
//+------------------------------------------------------------------+ //| Evaluates hidden smash patterns on new bar formation | //| Updates indicator buffers for the recently completed bar | //+------------------------------------------------------------------+ void ProcessHiddenSmashOnNewBar( const double &open[], const double &high[], const double &low[], const double &close[], int rates_total ) { int completedBarIndex = rates_total - 2; int currentBarIndex = rates_total - 1; //--- Safety check if(completedBarIndex < 0) return; //--- Always clear buffers for involved bars buySmashArrowBuffer [completedBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[completedBarIndex] = EMPTY_VALUE; buySmashArrowBuffer [currentBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[currentBarIndex] = EMPTY_VALUE; //--- Mode 1: Detect bar only if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(completedBarIndex, open, high, low, close)) buySmashArrowBuffer[completedBarIndex] = low[completedBarIndex]; if(IsHiddenSmashSellBarAtIndex(completedBarIndex, open, high, low, close)) sellSmashArrowBuffer[completedBarIndex] = high[completedBarIndex]; return; } //--- Mode 2: Confirmed smash if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { int smashBarIndex = completedBarIndex - 1; if(smashBarIndex < 0) return; if(IsHiddenSmashBuyBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[completedBarIndex] > high[smashBarIndex]) buySmashArrowBuffer[smashBarIndex] = low[smashBarIndex]; } if(IsHiddenSmashSellBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[completedBarIndex] < low[smashBarIndex]) sellSmashArrowBuffer[smashBarIndex] = high[smashBarIndex]; } } }
This ensures that patterns are detected promptly without reprocessing the entire history. A separate logic handles raw detection and confirmed detection, ensuring that arrows appear at the correct bar index depending on the selected mode.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { ... //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open ProcessHiddenSmashOnNewBar(lwOpen, lwHigh, lwLow, lwClose, rates_total); } ... return(rates_total); }
This approach balances performance with accuracy and keeps the indicator responsive even on lower timeframes.
Supporting Real-Time Tick Updates
To make the indicator usable in live market conditions, real-time tick processing is also implemented.
//+------------------------------------------------------------------+ //| Evaluates hidden smash patterns on tick updates | //| Supports real-time detection and confirmation logic | //+------------------------------------------------------------------+ void ProcessHiddenSmashOnTick( const double &open[], const double &high[], const double &low[], const double &close[], int rates_total ) { int currentBarIndex = rates_total - 1; int completedBarIndex = rates_total - 2; if(currentBarIndex < 0) return; //--- Always reset current bar buffer values buySmashArrowBuffer [currentBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[currentBarIndex] = EMPTY_VALUE; //--- Mode 1: Bar-only detection in real time if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(currentBarIndex, open, high, low, close)) buySmashArrowBuffer[currentBarIndex] = low[currentBarIndex]; if(IsHiddenSmashSellBarAtIndex(currentBarIndex, open, high, low, close)) sellSmashArrowBuffer[currentBarIndex] = high[currentBarIndex]; return; } //--- Mode 2: Confirmed smash evaluated continuously if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { int smashBarIndex = completedBarIndex; if(smashBarIndex < 0) return; if(IsHiddenSmashBuyBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[currentBarIndex] > high[smashBarIndex]) buySmashArrowBuffer[smashBarIndex] = low[smashBarIndex]; } if(IsHiddenSmashSellBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[currentBarIndex] < low[smashBarIndex]) sellSmashArrowBuffer[smashBarIndex] = high[smashBarIndex]; } } }
On every incoming tick, the indicator evaluates the current bar and dynamically adjusts buffer values.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { ... //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick ProcessHiddenSmashOnTick(lwOpen, lwHigh, lwLow, lwClose, rates_total); } return(rates_total); }
This allows raw Hidden Smash Day Bars to appear immediately as they form and confirmed patterns to update the moment confirmation conditions are met. This real-time responsiveness makes the indicator practical for discretionary analysis and active monitoring.
Configuring Visual Output with Arrow Plots
Even with correct buffer values, nothing will appear on the chart unless the indicator is told how to render its data. Arrow-based plots are configured using PlotIndexSet functions inside OnInit.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Configure Graphic Plots PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(0, PLOT_ARROW, 233); PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrSeaGreen); PlotIndexSetInteger(0, PLOT_ARROW_SHIFT, +20); PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (0, PLOT_LABEL, "Hidden Smash Buy Bar"); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(1, PLOT_ARROW, 234); PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrBlack); PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, -20); PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 2); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (1, PLOT_LABEL, "Hidden Smash Sell bar"); //--- General indicator configurations IndicatorSetString(INDICATOR_SHORTNAME, "Larry Williams Market Structure Indicator"); return(INIT_SUCCEEDED); }
Bullish Hidden Smash Day Bars are marked with upward arrows placed below the price. Bearish Hidden Smash Day Bars are marked with downward arrows placed above the price. Color, size, shift, and labels are defined explicitly to ensure consistent rendering across symbols and timeframes. Once these plots are configured, the indicator becomes visually complete.
At this stage, the indicator is fully functional. It detects Hidden Smash Day patterns using Larry Williams' rules, supports multiple validation modes, dynamically updates as the market evolves, and renders clear visual signals on the chart.
The complete implementation is provided in the attached source file named lwHiddenSmashDayIndicator.mq5. In practice, referencing the attached file is preferred over embedding the full source in the article.
This section concludes the full development of the Hidden Smash Day indicator and sets the stage for testing, interpretation, and further experimentation.
Testing and visual inspection
We begin by compiling the indicator source code inside MetaEditor. If all steps were followed carefully, the code should compile without errors or warnings. A successful compilation confirms that all functions are correctly defined, buffers are properly registered, and the indicator logic is structurally sound.
If any compilation errors appear, the safest approach is to compare the current working file with the attached source code named lwHiddenSmashDayIndicator.mq5. Small differences in function signatures or buffer handling can easily lead to issues, and cross-checking against the reference implementation helps resolve them quickly.
Once compilation succeeds, the indicator can be attached to a price chart. Upon loading, arrows should immediately appear on historical bars where valid Hidden Smash Day patterns were identified, based on the selected validation mode. Bullish patterns are marked with sea green arrows below the price, while bearish patterns are marked with black arrows above the price.
Here's a screenshot showing how the finished indicator looks on a live chart.

This visual confirmation helps validate both the detection logic and the rendering configuration.
From here, it is worth exploring the indicator across different symbols and timeframes. Since the pattern logic is applied to each bar's own range, it adapts naturally to intraday charts as well as higher timeframes. Observing how Hidden Smash Day patterns behave under different market conditions can open up new research ideas and practical applications.Conclusion
- Detect Hidden Smash Day buy and sell setups objectively on a price chart
- Visualise Larry Williams’ reversal pattern directly using a custom indicator
- Study the pattern consistently across instruments and timeframes
- Extend the detection logic for research or integration into automated systems
- Apply additional context filters when analysing potential trading opportunities
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Using the MQL5 Economic Calendar for News Filtering (Part 2): Stop Management Positions During News Releases
Low-Frequency Quantitative Strategies in Metatrader 5: (Part 1) Setting Up An OLAP-Friendly Data Store
Features of Experts Advisors
Unified Validation Pipeline Against Backtest Overfitting
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Thanks for this