1134 lines
No EOL
42 KiB
MQL5
1134 lines
No EOL
42 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| supertrendPro.mq5 |
|
|
//| Copyright 2026, MetaQuotes Ltd. Developer: Gloria Diana |
|
|
//| https://www.mql5.com/en/users/gloriadiana |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
|
|
#property link "https://www.mql5.com/en/users/chachaian"
|
|
#property version "1.00"
|
|
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 8
|
|
#property indicator_plots 3
|
|
|
|
#property indicator_type1 DRAW_COLOR_CANDLES
|
|
#property indicator_type2 DRAW_LINE
|
|
#property indicator_type3 DRAW_LINE
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input parameters |
|
|
//+------------------------------------------------------------------+
|
|
input group "Supertrend Settings"
|
|
input int InpATRPeriod = 10; // ATR calculation period
|
|
input double InpATRMultiplier = 1.5; // ATR multiplier used to position the Supertrend bands
|
|
|
|
input group "Notifications"
|
|
input bool InpEnablePushNotifications = true; // Send mobile push notifications on confirmed trend changes
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Trend states exposed through Buffer 7 |
|
|
//+------------------------------------------------------------------+
|
|
const double TREND_BULLISH = 1.0; // Confirmed bullish trend
|
|
const double TREND_BEARISH = 0.0; // Confirmed bearish trend
|
|
const double TREND_NEUTRAL = -1.0; // Initial unresolved trend state
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Indicator theme colors |
|
|
//+------------------------------------------------------------------+
|
|
const color COLOR_BACKGROUND = clrWhiteSmoke; // Main chart background
|
|
const color COLOR_FOREGROUND = clrBlack; // Chart text and scale labels
|
|
const color COLOR_BULLISH = clrSeaGreen; // Bullish candles, bands, and Bid line
|
|
const color COLOR_BEARISH = clrCrimson; // Bearish candles, bands, and Ask line
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Stores the chart appearance before Supertrend Pro changes it |
|
|
//+------------------------------------------------------------------+
|
|
struct SChartAppearance
|
|
{
|
|
long backgroundColor; // Original chart background color
|
|
long foregroundColor; // Original chart text and scale color
|
|
long showGrid; // Original grid visibility
|
|
long chartMode; // Original chart drawing mode
|
|
long chartUpColor; // Original bullish chart color
|
|
long chartDownColor; // Original bearish chart color
|
|
long candleBullColor; // Original bullish candle color
|
|
long candleBearColor; // Original bearish candle color
|
|
long bidColor; // Original Bid line color
|
|
long askColor; // Original Ask line color
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global variables |
|
|
//+------------------------------------------------------------------+
|
|
int g_atrHandle = INVALID_HANDLE; // Handle of the ATR indicator
|
|
double g_atrValues[]; // Working array for copied ATR values
|
|
SChartAppearance g_chartAppearance; // Chart appearance saved before applying the theme
|
|
bool g_chartAppearanceSaved = false; // Confirms that restoration data is available
|
|
datetime g_lastNotifiedBarTime = 0; // Open time of the last bar that generated a push notification
|
|
datetime g_lastObservedLiveBarTime = 0; // Live bar tracked during the previous successful calculation
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Public indicator buffers |
|
|
//+------------------------------------------------------------------+
|
|
double g_candleOpen[]; // Buffer 0: trend-colored candle Open
|
|
double g_candleHigh[]; // Buffer 1: trend-colored candle High
|
|
double g_candleLow[]; // Buffer 2: trend-colored candle Low
|
|
double g_candleClose[]; // Buffer 3: trend-colored candle Close
|
|
double g_candleColor[]; // Buffer 4: candle trend color index
|
|
|
|
double g_upperBand[]; // Buffer 5: upper/bearish Supertrend band
|
|
double g_lowerBand[]; // Buffer 6: lower/bullish Supertrend band
|
|
|
|
double g_trendState[]; // Buffer 7: 1.0 bullish, 0.0 bearish, -1.0 neutral
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- Validate all user-configurable Supertrend parameters before
|
|
//--- allocating or configuring indicator resources.
|
|
if(!ValidateInputs())
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
|
|
//--- Reset notification tracking so every initialization establishes
|
|
//--- a fresh live-bar baseline before any push alert can be generated.
|
|
g_lastNotifiedBarTime=0;
|
|
g_lastObservedLiveBarTime=0;
|
|
|
|
//--- Bind the declared arrays to their permanent indicator buffer
|
|
//--- indexes before configuring their visual plots.
|
|
if(!ConfigureIndicatorBuffers())
|
|
{
|
|
Print("Failed to configure Supertrend Pro indicator buffers.");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//--- Configure the candle and Supertrend plots that present the
|
|
//--- calculated trend state on the chart.
|
|
if(!ConfigureIndicatorPlots())
|
|
{
|
|
Print("Failed to configure Supertrend Pro indicator plots.");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//--- Create the ATR handle used by the Supertrend calculation.
|
|
g_atrHandle=iATR(_Symbol,PERIOD_CURRENT,InpATRPeriod);
|
|
if(g_atrHandle==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("Failed to create ATR handle. Error=%d",GetLastError());
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//--- Preserve the user's current chart appearance before applying
|
|
//--- the mandatory Supertrend Pro visual theme.
|
|
if(!SaveChartAppearance())
|
|
{
|
|
Print("Failed to save the current chart appearance.");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//--- Apply the Supertrend Pro theme after the original appearance
|
|
//--- has been stored successfully.
|
|
if(!ApplyIndicatorTheme())
|
|
{
|
|
Print("Failed to apply the Supertrend Pro chart theme.");
|
|
|
|
//--- Restore any chart properties changed before theme setup failed.
|
|
RestoreChartAppearance();
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
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[])
|
|
{
|
|
//--- Explicitly use chronological indexing throughout the indicator.
|
|
ArraySetAsSeries(time,false);
|
|
ArraySetAsSeries(open,false);
|
|
ArraySetAsSeries(high,false);
|
|
ArraySetAsSeries(low,false);
|
|
ArraySetAsSeries(close,false);
|
|
ArraySetAsSeries(tick_volume,false);
|
|
ArraySetAsSeries(volume,false);
|
|
ArraySetAsSeries(spread,false);
|
|
|
|
//--- Wait until enough price history exists to calculate the ATR.
|
|
if(rates_total<InpATRPeriod)
|
|
return(prev_calculated);
|
|
|
|
//--- Separate the forming bar from the latest confirmed bar.
|
|
int liveIndex=rates_total-1;
|
|
int lastClosedIndex=rates_total-2;
|
|
int firstValidIndex=InpATRPeriod-1;
|
|
int start=0;
|
|
|
|
if(lastClosedIndex<firstValidIndex)
|
|
return(prev_calculated);
|
|
|
|
//--- A notification is eligible only when exactly one new bar has
|
|
//--- opened since the previous successful calculation and the bar that
|
|
//--- has just closed was previously tracked as the live bar.
|
|
bool liveTrendChange=false;
|
|
|
|
if(prev_calculated>0 &&
|
|
rates_total==prev_calculated+1 &&
|
|
g_lastObservedLiveBarTime!=0 &&
|
|
time[lastClosedIndex]==g_lastObservedLiveBarTime)
|
|
liveTrendChange=true;
|
|
|
|
//--- Rebuild the complete Supertrend state when previous calculations
|
|
//--- cannot be reused. Historical reconstruction never generates alerts.
|
|
if(prev_calculated==0)
|
|
{
|
|
//--- Clear all public buffers before reconstructing history.
|
|
ArrayInitialize(g_candleOpen,EMPTY_VALUE);
|
|
ArrayInitialize(g_candleHigh,EMPTY_VALUE);
|
|
ArrayInitialize(g_candleLow,EMPTY_VALUE);
|
|
ArrayInitialize(g_candleClose,EMPTY_VALUE);
|
|
ArrayInitialize(g_candleColor,0.0);
|
|
ArrayInitialize(g_upperBand,EMPTY_VALUE);
|
|
ArrayInitialize(g_lowerBand,EMPTY_VALUE);
|
|
ArrayInitialize(g_trendState,EMPTY_VALUE);
|
|
|
|
//--- ATR data is required from the first valid seed bar onward.
|
|
start=firstValidIndex;
|
|
}
|
|
else
|
|
{
|
|
//--- Resume from the bar that was live during the previous
|
|
//--- calculation and may now have become confirmed.
|
|
start=prev_calculated-1;
|
|
|
|
if(start<firstValidIndex+1)
|
|
start=firstValidIndex+1;
|
|
}
|
|
|
|
//--- Copy ATR values only for confirmed bars that require calculation.
|
|
if(start<=lastClosedIndex)
|
|
{
|
|
if(!GetATRValues(start,lastClosedIndex,rates_total))
|
|
return(prev_calculated);
|
|
}
|
|
|
|
//--- Seed the neutral state only during a complete historical rebuild.
|
|
if(prev_calculated==0)
|
|
{
|
|
double barMidpoint=(high[firstValidIndex]+low[firstValidIndex])/2.0;
|
|
|
|
g_upperBand[firstValidIndex]=barMidpoint+
|
|
g_atrValues[firstValidIndex]*InpATRMultiplier;
|
|
g_lowerBand[firstValidIndex]=barMidpoint-
|
|
g_atrValues[firstValidIndex]*InpATRMultiplier;
|
|
g_trendState[firstValidIndex]=TREND_NEUTRAL;
|
|
|
|
//--- The seed bar is complete, so subsequent bars can enter
|
|
//--- the unified Supertrend state machine.
|
|
start=firstValidIndex+1;
|
|
}
|
|
|
|
//--- Process every newly confirmed bar through the common Supertrend
|
|
//--- state machine, including any bars recovered after inactivity.
|
|
for(int i=start;i<=lastClosedIndex;i++)
|
|
CalculateBar(i,open,high,low,close);
|
|
|
|
//--- Notify only when the newest confirmed bar was genuinely observed
|
|
//--- forming during the preceding successful indicator calculation.
|
|
if(liveTrendChange)
|
|
NotifyConfirmedTrendChange(lastClosedIndex,time);
|
|
|
|
//--- Keep the forming bar outside the confirmed indicator state.
|
|
g_candleOpen[liveIndex]=EMPTY_VALUE;
|
|
g_candleHigh[liveIndex]=EMPTY_VALUE;
|
|
g_candleLow[liveIndex]=EMPTY_VALUE;
|
|
g_candleClose[liveIndex]=EMPTY_VALUE;
|
|
g_candleColor[liveIndex]=0.0;
|
|
g_upperBand[liveIndex]=EMPTY_VALUE;
|
|
g_lowerBand[liveIndex]=EMPTY_VALUE;
|
|
g_trendState[liveIndex]=EMPTY_VALUE;
|
|
|
|
//--- Record the current live bar only after this calculation completes
|
|
//--- successfully. It becomes eligible for notification after it closes.
|
|
g_lastObservedLiveBarTime=time[liveIndex];
|
|
|
|
return(rates_total);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
//--- Restore the user's previous chart appearance before the
|
|
//--- Supertrend Pro instance leaves the chart.
|
|
if(g_chartAppearanceSaved)
|
|
RestoreChartAppearance();
|
|
|
|
//--- Release the ATR resource only when a valid handle was created.
|
|
if(g_atrHandle!=INVALID_HANDLE)
|
|
{
|
|
IndicatorRelease(g_atrHandle);
|
|
g_atrHandle=INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Validates the Supertrend input parameters |
|
|
//+------------------------------------------------------------------+
|
|
bool ValidateInputs()
|
|
{
|
|
//--- The ATR period must contain at least one bar.
|
|
if(InpATRPeriod < 1)
|
|
{
|
|
Print("Invalid ATR period. InpATRPeriod must be greater than or equal to 1.");
|
|
return(false);
|
|
}
|
|
|
|
//--- The ATR multiplier must be positive because zero or negative
|
|
//--- values cannot produce valid Supertrend band distances.
|
|
if(InpATRMultiplier <= 0.0)
|
|
{
|
|
Print("Invalid ATR multiplier. InpATRMultiplier must be greater than 0.");
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Binds all arrays to their permanent indicator buffer indexes |
|
|
//+------------------------------------------------------------------+
|
|
bool ConfigureIndicatorBuffers()
|
|
{
|
|
//--- Buffers 0-3 provide the OHLC values required by the
|
|
//--- DRAW_COLOR_CANDLES plot used for trend-colored candles.
|
|
if(!SetIndexBuffer(0,g_candleOpen,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind candle Open buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!SetIndexBuffer(1,g_candleHigh,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind candle High buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!SetIndexBuffer(2,g_candleLow,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind candle Low buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!SetIndexBuffer(3,g_candleClose,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind candle Close buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Buffer 4 stores the color index used to select the bullish
|
|
//--- or bearish candle color for each calculated bar.
|
|
if(!SetIndexBuffer(4,g_candleColor,INDICATOR_COLOR_INDEX))
|
|
{
|
|
Print("Failed to bind candle color buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Buffers 5 and 6 remain separate so bearish and bullish
|
|
//--- Supertrend segments are never visually joined at reversals.
|
|
if(!SetIndexBuffer(5,g_upperBand,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind upper Supertrend band buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!SetIndexBuffer(6,g_lowerBand,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind lower Supertrend band buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Buffer 7 exposes the numerical trend state for programmatic
|
|
//--- access by Expert Advisors and other MQL5 applications.
|
|
if(!SetIndexBuffer(7,g_trendState,INDICATOR_DATA))
|
|
{
|
|
Print("Failed to bind trend-state buffer. Error: ",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Explicitly keep every indicator buffer in chronological order.
|
|
//--- Index 0 represents the oldest available bar and higher indexes
|
|
//--- progress toward the current bar.
|
|
if(!ArraySetAsSeries(g_candleOpen,false) ||
|
|
!ArraySetAsSeries(g_candleHigh,false) ||
|
|
!ArraySetAsSeries(g_candleLow,false) ||
|
|
!ArraySetAsSeries(g_candleClose,false) ||
|
|
!ArraySetAsSeries(g_candleColor,false) ||
|
|
!ArraySetAsSeries(g_upperBand,false) ||
|
|
!ArraySetAsSeries(g_lowerBand,false) ||
|
|
!ArraySetAsSeries(g_trendState,false))
|
|
{
|
|
Print("Failed to configure chronological indexing for indicator buffers.");
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Applies the Supertrend Pro visual theme to the chart |
|
|
//+------------------------------------------------------------------+
|
|
bool ApplyIndicatorTheme()
|
|
{
|
|
//--- Apply the neutral chart background and foreground colors.
|
|
if(!ChartSetInteger(0,CHART_COLOR_BACKGROUND,COLOR_BACKGROUND))
|
|
{
|
|
PrintFormat("Failed to set chart background color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_FOREGROUND,COLOR_FOREGROUND))
|
|
{
|
|
PrintFormat("Failed to set chart foreground color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Remove the chart grid to keep the trend candles and Supertrend
|
|
//--- bands visually dominant against the WhiteSmoke background.
|
|
if(!ChartSetInteger(0,CHART_SHOW_GRID,false))
|
|
{
|
|
PrintFormat("Failed to configure chart grid visibility. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Use candle-chart mode throughout the Supertrend Pro presentation.
|
|
if(!ChartSetInteger(0,CHART_MODE,CHART_CANDLES))
|
|
{
|
|
PrintFormat("Failed to set candle chart mode. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Apply the bullish theme to native bullish chart elements.
|
|
if(!ChartSetInteger(0,CHART_COLOR_CHART_UP,COLOR_BULLISH))
|
|
{
|
|
PrintFormat("Failed to set bullish chart color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,COLOR_BULLISH))
|
|
{
|
|
PrintFormat("Failed to set bullish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Apply the bearish theme to native bearish chart elements.
|
|
if(!ChartSetInteger(0,CHART_COLOR_CHART_DOWN,COLOR_BEARISH))
|
|
{
|
|
PrintFormat("Failed to set bearish chart color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,COLOR_BEARISH))
|
|
{
|
|
PrintFormat("Failed to set bearish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Match Bid and Ask line colors to the directional theme.
|
|
if(!ChartSetInteger(0,CHART_COLOR_BID,COLOR_BULLISH))
|
|
{
|
|
PrintFormat("Failed to set Bid line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_ASK,COLOR_BEARISH))
|
|
{
|
|
PrintFormat("Failed to set Ask line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Refresh the chart after applying the complete visual theme.
|
|
ChartRedraw(0);
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Saves the chart appearance before applying the indicator theme |
|
|
//+------------------------------------------------------------------+
|
|
bool SaveChartAppearance()
|
|
{
|
|
//--- Read the chart colors that Supertrend Pro will temporarily replace.
|
|
if(!ChartGetInteger(0,CHART_COLOR_BACKGROUND,0,g_chartAppearance.backgroundColor))
|
|
{
|
|
PrintFormat("Failed to read chart background color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_FOREGROUND,0,g_chartAppearance.foregroundColor))
|
|
{
|
|
PrintFormat("Failed to read chart foreground color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_CHART_UP,0,g_chartAppearance.chartUpColor))
|
|
{
|
|
PrintFormat("Failed to read bullish chart color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_CHART_DOWN,0,g_chartAppearance.chartDownColor))
|
|
{
|
|
PrintFormat("Failed to read bearish chart color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_CANDLE_BULL,0,g_chartAppearance.candleBullColor))
|
|
{
|
|
PrintFormat("Failed to read bullish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_CANDLE_BEAR,0,g_chartAppearance.candleBearColor))
|
|
{
|
|
PrintFormat("Failed to read bearish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_BID,0,g_chartAppearance.bidColor))
|
|
{
|
|
PrintFormat("Failed to read Bid line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_COLOR_ASK,0,g_chartAppearance.askColor))
|
|
{
|
|
PrintFormat("Failed to read Ask line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Preserve the non-color chart properties changed by the theme.
|
|
if(!ChartGetInteger(0,CHART_SHOW_GRID,0,g_chartAppearance.showGrid))
|
|
{
|
|
PrintFormat("Failed to read chart grid visibility. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!ChartGetInteger(0,CHART_MODE,0,g_chartAppearance.chartMode))
|
|
{
|
|
PrintFormat("Failed to read chart mode. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- The complete appearance snapshot is now safe to restore later.
|
|
g_chartAppearanceSaved=true;
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Restores the chart appearance saved before theme application |
|
|
//+------------------------------------------------------------------+
|
|
bool RestoreChartAppearance()
|
|
{
|
|
bool restored=true;
|
|
|
|
//--- Restore the original background and foreground colors.
|
|
if(!ChartSetInteger(0,CHART_COLOR_BACKGROUND,g_chartAppearance.backgroundColor))
|
|
{
|
|
PrintFormat("Failed to restore chart background color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_FOREGROUND,g_chartAppearance.foregroundColor))
|
|
{
|
|
PrintFormat("Failed to restore chart foreground color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
//--- Restore the original grid visibility and chart drawing mode.
|
|
if(!ChartSetInteger(0,CHART_SHOW_GRID,g_chartAppearance.showGrid))
|
|
{
|
|
PrintFormat("Failed to restore chart grid visibility. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_MODE,g_chartAppearance.chartMode))
|
|
{
|
|
PrintFormat("Failed to restore chart mode. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
//--- Restore the original bullish chart and candle colors.
|
|
if(!ChartSetInteger(0,CHART_COLOR_CHART_UP,g_chartAppearance.chartUpColor))
|
|
{
|
|
PrintFormat("Failed to restore bullish chart color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,g_chartAppearance.candleBullColor))
|
|
{
|
|
PrintFormat("Failed to restore bullish candle color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
//--- Restore the original bearish chart and candle colors.
|
|
if(!ChartSetInteger(0,CHART_COLOR_CHART_DOWN,g_chartAppearance.chartDownColor))
|
|
{
|
|
PrintFormat("Failed to restore bearish chart color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,g_chartAppearance.candleBearColor))
|
|
{
|
|
PrintFormat("Failed to restore bearish candle color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
//--- Restore the original Bid and Ask line colors.
|
|
if(!ChartSetInteger(0,CHART_COLOR_BID,g_chartAppearance.bidColor))
|
|
{
|
|
PrintFormat("Failed to restore Bid line color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
if(!ChartSetInteger(0,CHART_COLOR_ASK,g_chartAppearance.askColor))
|
|
{
|
|
PrintFormat("Failed to restore Ask line color. Error=%d",GetLastError());
|
|
restored=false;
|
|
}
|
|
|
|
//--- Refresh the chart after restoring all saved appearance properties.
|
|
ChartRedraw(0);
|
|
|
|
//--- Clear the saved-state flag only after a successful restoration.
|
|
if(restored)
|
|
g_chartAppearanceSaved=false;
|
|
|
|
return(restored);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Configures the visual plots used by Supertrend Pro |
|
|
//+------------------------------------------------------------------+
|
|
bool ConfigureIndicatorPlots()
|
|
{
|
|
//--- Configure Plot 0 as two-color candles. Color index 0 represents
|
|
//--- bearish candles and color index 1 represents bullish candles.
|
|
if(!PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_COLOR_CANDLES))
|
|
{
|
|
PrintFormat("Failed to set the candle plot type. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(0,PLOT_COLOR_INDEXES,2))
|
|
{
|
|
PrintFormat("Failed to configure candle color indexes. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,COLOR_BEARISH))
|
|
{
|
|
PrintFormat("Failed to set bearish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,COLOR_BULLISH))
|
|
{
|
|
PrintFormat("Failed to set bullish candle color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE))
|
|
{
|
|
PrintFormat("Failed to set the candle plot empty value. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetString(0,PLOT_LABEL,
|
|
"Supertrend Open;Supertrend High;Supertrend Low;Supertrend Close"))
|
|
{
|
|
PrintFormat("Failed to set the candle plot label. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Configure Plot 1 as the bearish upper Supertrend band.
|
|
if(!PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_LINE))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend plot type. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(1,PLOT_LINE_STYLE,STYLE_SOLID))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend line style. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(1,PLOT_LINE_WIDTH,2))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend line width. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(1,PLOT_LINE_COLOR,COLOR_BEARISH))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend empty value. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetString(1,PLOT_LABEL,"Bearish Supertrend"))
|
|
{
|
|
PrintFormat("Failed to set upper Supertrend plot label. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Configure Plot 2 as the bullish lower Supertrend band.
|
|
if(!PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_LINE))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend plot type. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(2,PLOT_LINE_STYLE,STYLE_SOLID))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend line style. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(2,PLOT_LINE_WIDTH,2))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend line width. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetInteger(2,PLOT_LINE_COLOR,COLOR_BULLISH))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend line color. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,EMPTY_VALUE))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend empty value. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!PlotIndexSetString(2,PLOT_LABEL,"Bullish Supertrend"))
|
|
{
|
|
PrintFormat("Failed to set lower Supertrend plot label. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Prevent plotting before enough bars exist to produce the first
|
|
//--- ATR value required by the Supertrend calculation.
|
|
int drawBegin=InpATRPeriod-1;
|
|
|
|
if(!PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,drawBegin) ||
|
|
!PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,drawBegin) ||
|
|
!PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,drawBegin))
|
|
{
|
|
PrintFormat("Failed to configure the plot starting position. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- Configure the indicator identity and displayed price precision.
|
|
if(!IndicatorSetString(INDICATOR_SHORTNAME,"Supertrend Pro"))
|
|
{
|
|
PrintFormat("Failed to set indicator short name. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
if(!IndicatorSetInteger(INDICATOR_DIGITS,_Digits))
|
|
{
|
|
PrintFormat("Failed to set indicator precision. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Copies ATR values required for the current calculation range |
|
|
//+------------------------------------------------------------------+
|
|
bool GetATRValues(const int startIndex,
|
|
const int lastClosedIndex,
|
|
const int ratesTotal)
|
|
{
|
|
//--- Determine how many confirmed ATR values are required.
|
|
int valuesRequired=lastClosedIndex-startIndex+1;
|
|
|
|
if(valuesRequired<=0)
|
|
return(true);
|
|
|
|
//--- Confirm that the ATR handle has calculated enough recent values
|
|
//--- to cover the requested closed-bar range.
|
|
int calculatedBars=BarsCalculated(g_atrHandle);
|
|
if(calculatedBars<valuesRequired+1)
|
|
return(false);
|
|
|
|
//--- Keep the persistent ATR array aligned with the complete price
|
|
//--- history so each ATR value uses the same chronological bar index.
|
|
if(ArraySize(g_atrValues)!=ratesTotal)
|
|
{
|
|
if(ArrayResize(g_atrValues,ratesTotal)!=ratesTotal)
|
|
{
|
|
PrintFormat("Failed to resize the ATR working array to %d elements.",
|
|
ratesTotal);
|
|
return(false);
|
|
}
|
|
|
|
if(!ArraySetAsSeries(g_atrValues,false))
|
|
{
|
|
Print("Failed to configure chronological indexing for ATR values.");
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
//--- Create a temporary array for only the recent ATR values required
|
|
//--- by this calculation pass.
|
|
double atrValues[];
|
|
|
|
if(ArrayResize(atrValues,valuesRequired)!=valuesRequired)
|
|
{
|
|
PrintFormat("Failed to resize the temporary ATR array to %d elements.",
|
|
valuesRequired);
|
|
return(false);
|
|
}
|
|
|
|
ArraySetAsSeries(atrValues,false);
|
|
|
|
//--- Start from shift 1 because the current live bar is intentionally
|
|
//--- excluded from confirmed Supertrend calculations.
|
|
ResetLastError();
|
|
|
|
int copiedValues=CopyBuffer(g_atrHandle,0,1,valuesRequired,atrValues);
|
|
if(copiedValues<0)
|
|
{
|
|
PrintFormat("Failed to copy ATR values. Error=%d",GetLastError());
|
|
return(false);
|
|
}
|
|
|
|
//--- A partial copy means the requested ATR range is not fully ready.
|
|
if(copiedValues!=valuesRequired)
|
|
return(false);
|
|
|
|
//--- Transfer the copied ATR segment into its matching chronological
|
|
//--- positions in the persistent ATR working array.
|
|
for(int i=0;i<valuesRequired;i++)
|
|
g_atrValues[startIndex+i]=atrValues[i];
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates one bar according to the previous Supertrend state |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateBar(const int index,
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
int previousIndex=index-1;
|
|
|
|
//--- Continue resolving the initial neutral state until price
|
|
//--- establishes the first confirmed directional trend.
|
|
if(g_trendState[previousIndex]==TREND_NEUTRAL)
|
|
{
|
|
CalculateNeutralState(index,high,low,close);
|
|
|
|
//--- Color the bar only after the neutral state resolves into
|
|
//--- a confirmed bullish or bearish trend.
|
|
if(g_trendState[index]==TREND_BULLISH)
|
|
SetBullishCandle(index,open,high,low,close);
|
|
else
|
|
if(g_trendState[index]==TREND_BEARISH)
|
|
SetBearishCandle(index,open,high,low,close);
|
|
|
|
return;
|
|
}
|
|
|
|
//--- Apply bullish continuation or bearish reversal logic when the
|
|
//--- preceding confirmed bar belongs to an established bullish trend.
|
|
if(g_trendState[previousIndex]==TREND_BULLISH)
|
|
{
|
|
CalculateBullishState(index,open,high,low,close);
|
|
return;
|
|
}
|
|
|
|
//--- Apply bearish continuation or bullish reversal logic when the
|
|
//--- preceding confirmed bar belongs to an established bearish trend.
|
|
if(g_trendState[previousIndex]==TREND_BEARISH)
|
|
CalculateBearishState(index,open,high,low,close);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Processes one bar while the Supertrend state remains neutral |
|
|
//+------------------------------------------------------------------+
|
|
bool CalculateNeutralState(const int index,
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
//--- Calculate the raw ATR-based bands for the current closed bar.
|
|
double barMidpoint=(high[index]+low[index])/2.0;
|
|
double upperBandValue=barMidpoint+
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
double lowerBandValue=barMidpoint-
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
|
|
int previousIndex=index-1;
|
|
|
|
//--- Establish the first bullish trend when price closes above the
|
|
//--- previous neutral upper band.
|
|
if(close[index]>g_upperBand[previousIndex])
|
|
{
|
|
g_trendState[index]=TREND_BULLISH;
|
|
g_upperBand[index]=EMPTY_VALUE;
|
|
g_lowerBand[index]=lowerBandValue;
|
|
|
|
return(true);
|
|
}
|
|
|
|
//--- Establish the first bearish trend when price closes below the
|
|
//--- previous neutral lower band.
|
|
if(close[index]<g_lowerBand[previousIndex])
|
|
{
|
|
g_trendState[index]=TREND_BEARISH;
|
|
g_lowerBand[index]=EMPTY_VALUE;
|
|
g_upperBand[index]=upperBandValue;
|
|
|
|
return(true);
|
|
}
|
|
|
|
//--- Keep the state neutral while price remains between both bands.
|
|
//--- The upper band may only move downward during this stage.
|
|
if(upperBandValue<g_upperBand[previousIndex])
|
|
g_upperBand[index]=upperBandValue;
|
|
else
|
|
g_upperBand[index]=g_upperBand[previousIndex];
|
|
|
|
//--- The lower band may only move upward while the initial trend
|
|
//--- remains unresolved.
|
|
if(lowerBandValue>g_lowerBand[previousIndex])
|
|
g_lowerBand[index]=lowerBandValue;
|
|
else
|
|
g_lowerBand[index]=g_lowerBand[previousIndex];
|
|
|
|
//--- Preserve the neutral state until either boundary is breached.
|
|
g_trendState[index]=TREND_NEUTRAL;
|
|
|
|
return(false);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Processes one bar when the previous trend state is bullish |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateBullishState(const int index,
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
//--- Calculate the raw ATR-based bands for the current confirmed bar.
|
|
double barMidpoint=(high[index]+low[index])/2.0;
|
|
double upperBandValue=barMidpoint+
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
double lowerBandValue=barMidpoint-
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
|
|
int previousIndex=index-1;
|
|
|
|
//--- Reverse to bearish when price closes below the previous active
|
|
//--- lower band. The upper band becomes active from this bar onward.
|
|
if(close[index]<g_lowerBand[previousIndex])
|
|
{
|
|
g_trendState[index]=TREND_BEARISH;
|
|
g_lowerBand[index]=EMPTY_VALUE;
|
|
g_upperBand[index]=upperBandValue;
|
|
|
|
//--- Recolor the reversal bar immediately as part of the new
|
|
//--- confirmed bearish trend.
|
|
SetBearishCandle(index,open,high,low,close);
|
|
return;
|
|
}
|
|
|
|
//--- Preserve the bullish trend while allowing the lower band to
|
|
//--- tighten upward without moving back down.
|
|
if(lowerBandValue>g_lowerBand[previousIndex])
|
|
g_lowerBand[index]=lowerBandValue;
|
|
else
|
|
g_lowerBand[index]=g_lowerBand[previousIndex];
|
|
|
|
//--- Keep the bearish upper band inactive while bullish control remains.
|
|
g_upperBand[index]=EMPTY_VALUE;
|
|
g_trendState[index]=TREND_BULLISH;
|
|
|
|
//--- Draw the confirmed bar using the bullish candle color.
|
|
SetBullishCandle(index,open,high,low,close);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Processes one bar when the previous trend state is bearish |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateBearishState(const int index,
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
//--- Calculate the raw ATR-based bands for the current confirmed bar.
|
|
double barMidpoint=(high[index]+low[index])/2.0;
|
|
double upperBandValue=barMidpoint+
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
double lowerBandValue=barMidpoint-
|
|
g_atrValues[index]*InpATRMultiplier;
|
|
|
|
int previousIndex=index-1;
|
|
|
|
//--- Reverse to bullish when price closes above the previous active
|
|
//--- upper band. The lower band becomes active from this bar onward.
|
|
if(close[index]>g_upperBand[previousIndex])
|
|
{
|
|
g_trendState[index]=TREND_BULLISH;
|
|
g_upperBand[index]=EMPTY_VALUE;
|
|
g_lowerBand[index]=lowerBandValue;
|
|
|
|
//--- Recolor the reversal bar immediately as part of the new
|
|
//--- confirmed bullish trend.
|
|
SetBullishCandle(index,open,high,low,close);
|
|
return;
|
|
}
|
|
|
|
//--- Preserve the bearish trend while allowing the upper band to
|
|
//--- tighten downward without moving back upward.
|
|
if(upperBandValue<g_upperBand[previousIndex])
|
|
g_upperBand[index]=upperBandValue;
|
|
else
|
|
g_upperBand[index]=g_upperBand[previousIndex];
|
|
|
|
//--- Keep the bullish lower band inactive while bearish control remains.
|
|
g_lowerBand[index]=EMPTY_VALUE;
|
|
g_trendState[index]=TREND_BEARISH;
|
|
|
|
//--- Draw the confirmed bar using the bearish candle color.
|
|
SetBearishCandle(index,open,high,low,close);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Populates the candle buffers for a confirmed bullish bar |
|
|
//+------------------------------------------------------------------+
|
|
void SetBullishCandle(const int index,
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
//--- Copy the original OHLC values so the indicator can redraw the
|
|
//--- completed candle using the bullish color index.
|
|
g_candleOpen[index]=open[index];
|
|
g_candleHigh[index]=high[index];
|
|
g_candleLow[index]=low[index];
|
|
g_candleClose[index]=close[index];
|
|
g_candleColor[index]=1.0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Populates the candle buffers for a confirmed bearish bar |
|
|
//+------------------------------------------------------------------+
|
|
void SetBearishCandle(const int index,
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[])
|
|
{
|
|
//--- Copy the original OHLC values so the indicator can redraw the
|
|
//--- completed candle using the bearish color index.
|
|
g_candleOpen[index]=open[index];
|
|
g_candleHigh[index]=high[index];
|
|
g_candleLow[index]=low[index];
|
|
g_candleClose[index]=close[index];
|
|
g_candleColor[index]=0.0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Sends a push notification for a confirmed directional reversal |
|
|
//+------------------------------------------------------------------+
|
|
void NotifyConfirmedTrendChange(const int index,
|
|
const datetime &time[])
|
|
{
|
|
if(!InpEnablePushNotifications)
|
|
return;
|
|
|
|
//--- A previous confirmed bar is required to determine whether the
|
|
//--- current state represents an actual trend change.
|
|
if(index<1)
|
|
return;
|
|
|
|
int previousIndex=index-1;
|
|
|
|
//--- Ignore the first directional trend established from the neutral
|
|
//--- startup state because it is not a bullish-to-bearish reversal.
|
|
if(g_trendState[previousIndex]==TREND_NEUTRAL)
|
|
return;
|
|
|
|
//--- Ignore bars whose directional state did not change.
|
|
if(g_trendState[index]==g_trendState[previousIndex])
|
|
return;
|
|
|
|
//--- Only confirmed bullish and bearish states are valid notification
|
|
//--- targets.
|
|
if(g_trendState[index]!=TREND_BULLISH &&
|
|
g_trendState[index]!=TREND_BEARISH)
|
|
return;
|
|
|
|
//--- Prevent recalculation of the same confirmed bar from generating
|
|
//--- another push notification.
|
|
if(time[index]==g_lastNotifiedBarTime)
|
|
return;
|
|
|
|
//--- Build a concise message containing the symbol, timeframe, and
|
|
//--- newly confirmed Supertrend direction.
|
|
string trendText=(g_trendState[index]==TREND_BULLISH) ?
|
|
"BULLISH" :
|
|
"BEARISH";
|
|
|
|
string message=StringFormat("Supertrend | %s %s | Trend changed to %s",
|
|
_Symbol,
|
|
EnumToString((ENUM_TIMEFRAMES)_Period),
|
|
trendText);
|
|
|
|
//--- Send the notification and record the bar only after MetaTrader
|
|
//--- confirms that the message was accepted for delivery.
|
|
ResetLastError();
|
|
|
|
if(!SendNotification(message))
|
|
{
|
|
PrintFormat("Failed to send Supertrend push notification. Error=%d",
|
|
GetLastError());
|
|
return;
|
|
}
|
|
|
|
g_lastNotifiedBarTime=time[index];
|
|
}
|
|
//+------------------------------------------------------------------+ |