Spread Monitor and Filter
//+——————————————————————+ //| SpreadMonitorDemo.mq5 |
//| Copyright 2026, Algosphere |
//| https://algosphere-quant.com |
//+——————————————————————+
#property copyright “Copyright 2026, Algosphere”
#property link “https://algosphere-quant.com”
#property version “1.00”
#property description “Demo indicator for SpreadMonitor Library”
#property description “Displays real-time spread statistics and alerts”
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 0
//+——————————————————————+
//| Includes |
//+——————————————————————+
#include “SpreadMonitor.mqh”
//+——————————————————————+
//| Input parameters |
//+——————————————————————+
input group “=== Display Settings ===”
input int InpPanelX=10; // Panel X Position
input int InpPanelY=30; // Panel Y Position
input color InpBackgroundColor=C’32,32,32′; // Background Color
input color InpTextColor=clrWhite; // Text Color
input group “=== Spread Thresholds (Pips) ===”
input double InpLowThreshold=1.0; // Low Spread Threshold
input double InpNormalThreshold=2.0; // Normal Spread Threshold
input double InpHighThreshold=5.0; // High Spread Threshold
input group “=== Filter Settings ===”
input double InpMaxSpread=3.0; // Maximum Acceptable Spread
input int InpHistorySize=500; // History Size (samples)
input group “=== Alert Settings ===”
input bool InpAlertOnHigh=true; // Alert on High Spread
input bool InpAlertOnExtreme=true; // Alert on Extreme Spread
//+——————————————————————+
//| Defines |
//+——————————————————————+
#define PREFIX “SPM_”
#define PANEL_WIDTH 200
#define PANEL_HEIGHT 230
#define ROW_SPACING 18
//+——————————————————————+
//| Global variables |
//+——————————————————————+
CSpreadMonitor g_spread_monitor;
double g_spread_buffer[];
ENUM_SPREAD_CONDITION g_last_condition=SPREAD_LOW;
datetime g_last_alert_time=0;
//+——————————————————————+
//| Custom indicator initialization function |
//+——————————————————————+
int OnInit()
{
//— Initialize spread monitor
if(!g_spread_monitor.Init(_Symbol,InpHistorySize,
InpLowThreshold,InpNormalThreshold,InpHighThreshold))
{
Print(“Error: Failed to initialize SpreadMonitor”);
return(INIT_FAILED);
}
//— Set indicator buffer (not plotted, just for data storage)
SetIndexBuffer(0,g_spread_buffer,INDICATOR_CALCULATIONS);
//— Create panel
CreatePanel();
//— Initial update
g_spread_monitor.Update();
UpdatePanel();
//— Set timer for frequent updates
EventSetMillisecondTimer(250);
return(INIT_SUCCEEDED);
}
//+——————————————————————+
//| Custom indicator deinitialization function |
//+——————————————————————+
void OnDeinit(const int reason)
{
EventKillTimer();
ObjectsDeleteAll(0,PREFIX);
}
//+——————————————————————+
//| Timer function – updates spread display |
//+——————————————————————+
void OnTimer()
{
//— Record spread sample
g_spread_monitor.Update();
//— Update display
UpdatePanel();
//— Check for alerts
CheckAlerts();
}
//+——————————————————————+
//| 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[])
{
//— Store current spread
if(rates_total>0)
g_spread_buffer[rates_total-1]=g_spread_monitor.GetCurrentSpread();
return(rates_total);
}
//+——————————————————————+
//| Check and trigger alerts |
//+——————————————————————+
void CheckAlerts()
{
ENUM_SPREAD_CONDITION current_condition=g_spread_monitor.GetSpreadCondition();
//— Only alert on condition change to avoid spam
if(current_condition==g_last_condition)
return;
//— Check if enough time passed since last alert (30 seconds)
if(TimeCurrent()-g_last_alert_time<30)
return;
//— Alert on high spread
if(InpAlertOnHigh && current_condition==SPREAD_HIGH && g_last_condition<SPREAD_HIGH)
{
Alert(_Symbol,” – HIGH SPREAD: “,DoubleToString(g_spread_monitor.GetCurrentSpread(),1),” pips”);
g_last_alert_time=TimeCurrent();
}
//— Alert on extreme spread
if(InpAlertOnExtreme && current_condition==SPREAD_EXTREME && g_last_condition<SPREAD_EXTREME)
{
Alert(_Symbol,” – EXTREME SPREAD: “,DoubleToString(g_spread_monitor.GetCurrentSpread(),1),” pips!”);
g_last_alert_time=TimeCurrent();
}
g_last_condition=current_condition;
}
//+——————————————————————+
//| Create information panel |
//+——————————————————————+
void CreatePanel()
{
int x=InpPanelX;
int y=InpPanelY;
//— Background
CreateRectangleLabel(PREFIX+”Background”,x,y,PANEL_WIDTH,PANEL_HEIGHT,
InpBackgroundColor,C’60,60,60′);
//— Title
CreateTextLabel(PREFIX+”Title”,x+10,y+8,”SPREAD MONITOR”,
InpTextColor,11,”Arial Bold”);
//— Symbol
CreateTextLabel(PREFIX+”Symbol”,x+PANEL_WIDTH-60,y+10,_Symbol,
clrDodgerBlue,9,”Arial”);
//— Separator
CreateRectangleLabel(PREFIX+”Sep1″,x+10,y+30,PANEL_WIDTH-20,1,
clrDimGray,clrDimGray);
int row_y=y+40;
//— Current spread (large display)
CreateTextLabel(PREFIX+”LblCurrent”,x+15,row_y,”Current:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValCurrent”,x+80,row_y,”0.0″,clrLime,14,”Arial Bold”);
CreateTextLabel(PREFIX+”UnitCurrent”,x+130,row_y,”pips”,clrGray,9,”Arial”);
row_y+=24;
//— Condition
CreateTextLabel(PREFIX+”LblCondition”,x+15,row_y,”Status:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValCondition”,x+80,row_y,”LOW”,clrLime,10,”Arial Bold”);
row_y+=ROW_SPACING+5;
//— Separator
CreateRectangleLabel(PREFIX+”Sep2″,x+10,row_y,PANEL_WIDTH-20,1,
clrDimGray,clrDimGray);
row_y+=10;
//— Statistics section
CreateTextLabel(PREFIX+”LblStats”,x+15,row_y,”Statistics”,clrWhite,9,”Arial Bold”);
row_y+=ROW_SPACING;
CreateTextLabel(PREFIX+”LblAvg”,x+15,row_y,”Average:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValAvg”,x+100,row_y,”0.0 pips”,InpTextColor,9,”Arial”);
row_y+=ROW_SPACING;
CreateTextLabel(PREFIX+”LblMin”,x+15,row_y,”Minimum:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValMin”,x+100,row_y,”0.0 pips”,clrLime,9,”Arial”);
row_y+=ROW_SPACING;
CreateTextLabel(PREFIX+”LblMax”,x+15,row_y,”Maximum:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValMax”,x+100,row_y,”0.0 pips”,clrOrange,9,”Arial”);
row_y+=ROW_SPACING;
//— Separator
CreateRectangleLabel(PREFIX+”Sep3″,x+10,row_y+5,PANEL_WIDTH-20,1,
clrDimGray,clrDimGray);
row_y+=15;
//— Filter status
CreateTextLabel(PREFIX+”LblFilter”,x+15,row_y,”Trade Filter:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValFilter”,x+100,row_y,”ALLOW”,clrLime,9,”Arial Bold”);
row_y+=ROW_SPACING;
//— Samples
CreateTextLabel(PREFIX+”LblSamples”,x+15,row_y,”Samples:”,clrGray,9,”Arial”);
CreateTextLabel(PREFIX+”ValSamples”,x+100,row_y,”0″,clrGray,9,”Arial”);
ChartRedraw();
}
//+——————————————————————+
//| Update panel values |
//+——————————————————————+
void UpdatePanel()
{
//— Get statistics
SSpreadStats stats=g_spread_monitor.GetStatistics();
//— Update current spread
ObjectSetString(0,PREFIX+”ValCurrent”,OBJPROP_TEXT,
DoubleToString(stats.current,1));
ObjectSetInteger(0,PREFIX+”ValCurrent”,OBJPROP_COLOR,
g_spread_monitor.GetConditionColor(stats.condition));
//— Update condition
ObjectSetString(0,PREFIX+”ValCondition”,OBJPROP_TEXT,
g_spread_monitor.GetConditionName(stats.condition));
ObjectSetInteger(0,PREFIX+”ValCondition”,OBJPROP_COLOR,
g_spread_monitor.GetConditionColor(stats.condition));
//— Update statistics
ObjectSetString(0,PREFIX+”ValAvg”,OBJPROP_TEXT,
DoubleToString(stats.average,1)+” pips”);
ObjectSetString(0,PREFIX+”ValMin”,OBJPROP_TEXT,
DoubleToString(stats.minimum,1)+” pips”);
ObjectSetString(0,PREFIX+”ValMax”,OBJPROP_TEXT,
DoubleToString(stats.maximum,1)+” pips”);
//— Update filter status
bool acceptable=g_spread_monitor.IsSpreadAcceptable(InpMaxSpread);
ObjectSetString(0,PREFIX+”ValFilter”,OBJPROP_TEXT,
acceptable ? “ALLOW” : “BLOCK”);
ObjectSetInteger(0,PREFIX+”ValFilter”,OBJPROP_COLOR,
acceptable ? clrLime : clrRed);
//— Update samples count
ObjectSetString(0,PREFIX+”ValSamples”,OBJPROP_TEXT,
IntegerToString(stats.sample_count));
ChartRedraw();
}
//+——————————————————————+
//| Create text label |
//+——————————————————————+
void CreateTextLabel(const string name,
const int x,
const int y,
const string text,
const color clr,
const int font_size,
const string font_name)
{
if(ObjectFind(0,name)>=0)
ObjectDelete(0,name);
if(!ObjectCreate(0,name,OBJ_LABEL,0,0,0))
return;
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size);
ObjectSetString(0,name,OBJPROP_FONT,font_name);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
//+——————————————————————+
//| Create rectangle label |
//+——————————————————————+
void CreateRectangleLabel(const string name,
const int x,
const int y,
const int width,
const int height,
const color bg_color,
const color border_color)
{
if(ObjectFind(0,name)>=0)
ObjectDelete(0,name);
if(!ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,0,0,0))
return;
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_XSIZE,width);
ObjectSetInteger(0,name,OBJPROP_YSIZE,height);
ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bg_color);
ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,border_color);
ObjectSetInteger(0,name,OBJPROP_BORDER_TYPE,BORDER_FLAT);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
//+——————————————————————+