Summary: A practical guide to coding a hammer candlestick pattern indicator in MQL5, with a focus on the math behind body/wick ratios and a unique hack for exporting detection results for external analysis.




I was manually scanning charts for pin bars—what most people call "hammers" when they're bullish. After about the 50th chart, I realized two things: my eyes were playing tricks on me, and I was wasting time I could spend actually trading. So I decided to code an indicator that does the heavy lifting. But here's the thing—the official documentation tells you how to access price data, but it doesn't tell you the practical pitfalls of coding pattern recognition. I'm going to walk you through the actual process I followed, including a few lessons I learned the hard way.

The Basic Setup: Accessing Price Data



The first step is understanding how MQL5 references candles. According to the MQL5 documentation (docs.mql5.com), the Close[], Open[], High[], and Low[] arrays are time-series, meaning index 0 is the current (still-forming) candle, index 1 is the previous completed candle, and so on. This is straightforward, but I've seen people trip up by assuming index 0 is the oldest—it's not. Always remember: [0] is now, [1] is one bar ago.

Here's the basic structure I used in the OnCalculate() function to grab the data for a single candle:

``cpp
double openPrice = Open[1]; // previous completed candle
double closePrice = Close[1];
double highPrice = High[1];
double lowPrice = Low[1];
`

The Hammer Logic: Body vs. Wicks



The classic hammer pattern—what MQL5 articles often refer to as a bullish pin bar—has three defining characteristics:

  • A small real body at the top of the candle's range

  • A long lower wick (shadow) that's at least twice the length of the body

  • A very short or non-existent upper wick


  • The mathematical implementation requires calculating the absolute difference for the body and wick lengths. Here's the exact code I use:

    `cpp
    double candleBody = MathAbs(closePrice - openPrice);
    double lowerWick = MathMin(openPrice, closePrice) - lowPrice;
    double upperWick = highPrice - MathMax(openPrice, closePrice);

    if(lowerWick >= 2.0 candleBody && upperWick <= candleBody)
    {
    // We have a hammer pattern
    }
    `

    One thing I learned from a detailed MQL5 article on price action analysis is the importance of using the
    MathAbs function for the body calculation. Without it, a bearish candle would give you a negative body length, breaking the logic.

    The Hidden Pitfall: Fixed Thresholds Don't Work



    The
    real* world experience here—and this is something the official docs don't explicitly spell out—is that using a fixed point or pip value to define the "minimum body size" is a recipe for disaster. A 10-point body on a EURUSD H1 chart might be significant. On a GBPJPY M5 chart? It's meaningless noise. And on a daily Gold chart? Forget about it.

    This is where a more advanced implementation comes in. Instead of fixed thresholds, I use the Average True Range (ATR) to dynamically adjust the detection logic. The ATR provides a volatility-adjusted baseline. My implementation looks like this:

    `cpp
    // Calculate ATR first
    double atrValue = iATR(_Symbol, _Period, 14, 1);

    // Dynamic threshold: body must be at least 30% of ATR
    if(candleBody >= 0.3 atrValue && lowerWick >= 2.0 candleBody && upperWick <= candleBody)
    {
    // Valid hammer pattern confirmed
    }
    `

    This way, the indicator adapts to the current market conditions. It's not the simplest approach, but it's the difference between an indicator that works and one that generates endless false signals.

    Visual Feedback and Data Export



    To make the indicator useful, I need to see the results on the chart. I use
    PlotIndexSetInteger() to color the detected hammers differently—typically a bright color like cyan or magenta so they stand out from normal candles.

    But here's a pro tip that I haven't seen documented anywhere in the official MetaQuotes help center: export the detection results to a CSV file for external backtesting. This is something the MQL5 documentation doesn't cover, but it's incredibly useful.

    I added a file-handling routine in the
    OnDeinit() function:

    `cpp
    void OnDeinit(const int reason)
    {
    ResetLastError();
    int fileHandle = FileOpen("Hammer_Detection_Export.csv", FILE_WRITE|FILE_CSV|FILE_READ, ",");

    if(fileHandle != INVALID_HANDLE)
    {
    FileWrite(fileHandle, "Date", "Open", "High", "Low", "Close", "Hammer_Detected");
    // Loop through historical data and write results
    for(int i = 0; i < 100; i++)
    {
    datetime time = Time[i];
    string dateStr = TimeToString(time);
    FileWrite(fileHandle, dateStr, Open[i], High[i], Low[i], Close[i], hammerDetected[i]);
    }
    FileClose(fileHandle);
    Print("Export completed successfully");
    }
    else
    {
    Print("File open failed, error: ", GetLastError());
    }
    }
    ``

    This gives me a dataset I can analyze in Excel or Python to refine my strategy. It's not a feature you'll find in the official docs, but it's a game-changer for backtesting.

    Compiling and Testing



    After writing the code in MetaEditor, I compile it (F7). The MetaTrader 4 help guide notes that if compilation fails with errors, the indicator won't attach to the chart, and an entry will appear in the experts journal. Always check the journal tab for error messages—they're usually quite descriptive.

    Once compiled, I drag the indicator from the Navigator window onto the chart. The real test is running it on different timeframes and instruments to see how the ATR adjustment performs.

    Reference: MQL5 Documentation (docs.mql5.com) for price arrays and indicator buffers; MQL5 Articles on price action and K-line coding (mql5.com).

    本文首发于FXEAR.com,原创内容,未经授权禁止转载。