Problem Description
Solution
To create a custom indicator that plots arrows in MQL4, you need to configure the indicator buffers to use the DRAW_ARROW style and assign specific Wingdings codes to represent the arrow shapes.
Below is a complete, fully commented example of a custom indicator. This example implements a simple Moving Average Crossover strategy: it plots a Blue Up Arrow when a fast MA crosses above a slow MA, and a Red Down Arrow when it crosses below.
MQL4 Code: Arrow Indicator Example
//+------------------------------------------------------------------+
//| SimpleArrowIndicator.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- 1. Indicator Settings
#property indicator_chart_window // Draw on the main chart
#property indicator_buffers 2 // We need 2 buffers: one for Buy arrows, one for Sell arrows
#property indicator_color1 clrBlue // Color for Buffer 0 (Buy)
#property indicator_color2 clrRed // Color for Buffer 1 (Sell)
//--- 2. Input Parameters
input int FastMA_Period = 9;
input int SlowMA_Period = 21;
input ENUM_MA_METHOD MA_Method = MODE_EMA;
//--- 3. Indicator Buffers
double BuyArrowBuffer[];
double SellArrowBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 4. Configure Buffer 0 (Buy Arrows)
SetIndexBuffer(0, BuyArrowBuffer);
SetIndexStyle(0, DRAW_ARROW); // Set drawing style to ARROW
SetIndexArrow(0, 233); // Wingdings code 233 is an Up Arrow
SetIndexLabel(0, "Buy Signal");
SetIndexEmptyValue(0, 0.0); // Value to indicate "no drawing"
//--- 5. Configure Buffer 1 (Sell Arrows)
SetIndexBuffer(1, SellArrowBuffer);
SetIndexStyle(1, DRAW_ARROW); // Set drawing style to ARROW
SetIndexArrow(1, 234); // Wingdings code 234 is a Down Arrow
SetIndexLabel(1, "Sell Signal");
SetIndexEmptyValue(1, 0.0); // Value to indicate "no drawing"
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 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[])
{
//--- 6. Determine the range of bars to calculate
int limit = rates_total - prev_calculated;
// If this is the first run, calculate all bars
if(prev_calculated == 0) limit = rates_total - 1;
// Main calculation loop (running from oldest to newest)
for(int i = limit; i > 0; i--)
{
// Calculate Moving Averages for current bar (i) and previous bar (i+1)
double fastCurr = iMA(NULL, 0, FastMA_Period, 0, MA_Method, PRICE_CLOSE, i);
double slowCurr = iMA(NULL, 0, SlowMA_Period, 0, MA_Method, PRICE_CLOSE, i);
double fastPrev = iMA(NULL, 0, FastMA_Period, 0, MA_Method, PRICE_CLOSE, i+1);
double slowPrev = iMA(NULL, 0, SlowMA_Period, 0, MA_Method, PRICE_CLOSE, i+1);
//--- 7. Logic for Buy Signal (Crossover Up)
if(fastPrev < slowPrev && fastCurr > slowCurr)
{
// Place arrow slightly below the Low price
BuyArrowBuffer[i] = low[i] - 10 * Point;
SellArrowBuffer[i] = 0.0; // Ensure no sell signal here
}
//--- 8. Logic for Sell Signal (Crossover Down)
else if(fastPrev > slowPrev && fastCurr < slowCurr)
{
// Place arrow slightly above the High price
SellArrowBuffer[i] = high[i] + 10 * Point;
BuyArrowBuffer[i] = 0.0; // Ensure no buy signal here
}
//--- No Signal
else
{
BuyArrowBuffer[i] = 0.0;
SellArrowBuffer[i] = 0.0;
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
Key Functions Explained
-
#property indicator_chart_window:
This tells MT4 to draw the indicator directly on the price chart (where candles are), rather than in a separate window at the bottom (like RSI or MACD). -
SetIndexStyle(index, DRAW_ARROW):
This is the most critical function. It changes the drawing mode from the default line to symbols. -
SetIndexArrow(index, code):
This defines which symbol to draw. Thecodecorresponds to the Wingdings font character map.- 233: Up Arrow
- 234: Down Arrow
- 241: Thumb Up
- 242: Thumb Down
- 251: X mark
- 159: Big Dot
-
SetIndexEmptyValue(index, 0.0):
Unlike a line indicator which draws a value at every bar, arrows only appear on specific events. This function tells MT4: "If the value in the buffer is 0.0, do not draw anything." -
Positioning (
low[i] - 10 * Point):
When assigning a value to the buffer inOnCalculate, you are setting the Y-axis price where the arrow appears.- For a Buy arrow, we usually set it slightly below the
Lowof the candle so it doesn't overlap the chart. - For a Sell arrow, we set it slightly above the
High.
- For a Buy arrow, we usually set it slightly below the
How to Install
- Open MetaTrader 4.
- Press F4 to open the MetaEditor.
- Click New -> Custom Indicator -> Name it "SimpleArrowIndicator".
- Paste the code above into the file, replacing everything.
- Press F7 to Compile.
- Go back to the main MT4 window, find "SimpleArrowIndicator" in the Navigator, and drag it onto a chart.
Q&A
Q: How do I change the size of the arrows?
A: You can use the SetIndexStyle function with an additional parameter for width. For example: SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID, 3); where 3 is the thickness (size) of the arrow.
Q: Can I use custom images instead of Wingdings?
A: No, standard MQL4 buffers (DRAW_ARROW) only support Wingdings font codes. To use custom images (bmp), you would need to use ObjectCreate with OBJ_BITMAP_LABEL, which is significantly more complex and slower for backtesting.
Q: Why do my arrows disappear when I refresh the chart?
A: This usually happens if the logic inside OnCalculate relies on static variables that reset, or if the limit calculation is incorrect. Ensure your loop correctly handles prev_calculated to recalculate history if necessary, or simply recalculate the visible bars.
Q: How do I find other arrow shapes?
A: In MetaEditor, go to Help -> MQL4 Reference. Search for "Wingdings". It will provide a table of all available icons and their corresponding integer codes.