733 lines
No EOL
57 KiB
MQL5
733 lines
No EOL
57 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TD_Sequenctial_Ultimate.mq5 |
|
|
//| Rodrigo César Pagani |
|
|
//| rodrigo@pagani.com.br |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "EarnForex.com"
|
|
#property link "https://www.earnforex.com/metatrader-indicators/TD-Sequential-Ultimate/"
|
|
#property version "1.03"
|
|
|
|
#property description "Shows setups and countdowns based on Tom DeMark's Sequential method."
|
|
#property description "TDST support and resistance levels are marked too."
|
|
#property description "Optional alerts available."
|
|
|
|
/*
|
|
For iCustom():
|
|
Buffer #0 - TDST Resistance: actual price level. EMPTY_VALUE on empty value.
|
|
Buffer #1 - TDST Support: actual price level. EMPTY_VALUE on empty value.
|
|
Buffer #2 - Setup: candle number - positive for Buy Setup, negative for Sell Setup. 0 on empty value.
|
|
Buffer #3 - Countdown: candle number - positive for Buy Countdown, negative for Sell Countdown. 0 on empty value.
|
|
Buffer #4 - Setup Perfection: 1.0 for Buy Setup Perfection, -1.0 for Sell Setup Perfection. 0 on empty value.
|
|
*/
|
|
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 5 // +3 buffers for usage with iCustom(), they won't be displayed by the indicator.
|
|
#property indicator_plots 5 // Only two are actually displayed. Three others are necessary for iCustom().
|
|
#property indicator_color1 clrRed
|
|
#property indicator_type1 DRAW_ARROW // Arrows are better because line support/resistance have vertical connections.
|
|
#property indicator_width1 1
|
|
#property indicator_label1 "TDST Resistance"
|
|
#property indicator_color2 clrGreen
|
|
#property indicator_type2 DRAW_ARROW
|
|
#property indicator_width2 1
|
|
#property indicator_label2 "TDST Support"
|
|
#property indicator_type3 DRAW_NONE
|
|
#property indicator_color3 clrNONE
|
|
#property indicator_label3 "Setup"
|
|
#property indicator_type4 DRAW_NONE
|
|
#property indicator_label4 "Countdown"
|
|
#property indicator_color4 clrNONE
|
|
#property indicator_type5 DRAW_NONE
|
|
#property indicator_label5 "Perfection"
|
|
#property indicator_color5 clrNONE
|
|
|
|
input group "Calculation"
|
|
input int MaxBars = 1000; // MaxBars: how many bars to calculate; 0 = all bars.
|
|
input group "Display"
|
|
input color BuySetupColor = clrLime;
|
|
input color SellSetupColor = clrRed;
|
|
input color CountdownColor = clrOrange;
|
|
input string FontFace = "Verdana";
|
|
input int FontSize = 12;
|
|
input int ArrowWidth = 2;
|
|
input int PixelDistance = 3;
|
|
input string Prefix = "TDS_";
|
|
input group "Alerts"
|
|
input bool AlertOnSetup = false;
|
|
input bool AlertOnPerfecting = false;
|
|
input bool AlertOnCountdown13 = false;
|
|
input bool AlertOnSupportResistance = false;
|
|
input bool AlertNative = false;
|
|
input bool AlertEmail = false;
|
|
input bool AlertNotification = false;
|
|
|
|
// Support and resistance buffers - shown on the chart.
|
|
double Resistance[], Support[];
|
|
// These buffers will be used only by other indicators or EAs calling this one via iCustom().
|
|
double Setup[], Countdown[], Perfection[];
|
|
|
|
uint FontPixelHeight;
|
|
|
|
enum ENUM_COUNT_TYPE
|
|
{
|
|
COUNT_TYPE_BUY_SETUP,
|
|
COUNT_TYPE_SELL_SETUP,
|
|
COUNT_TYPE_BUY_COUNTDOWN,
|
|
COUNT_TYPE_SELL_COUNTDOWN,
|
|
COUNT_TYPE_BUY_PERFECTION,
|
|
COUNT_TYPE_SELL_PERFECTION
|
|
};
|
|
|
|
enum ENUM_ALERT_TYPE
|
|
{
|
|
ALERT_TYPE_SETUP_BUY,
|
|
ALERT_TYPE_SETUP_SELL,
|
|
ALERT_TYPE_PERFECTING_BUY,
|
|
ALERT_TYPE_PERFECTING_SELL,
|
|
ALERT_TYPE_COUNT13_BUY,
|
|
ALERT_TYPE_COUNT13_SELL,
|
|
ALERT_TYPE_SUPPORT,
|
|
ALERT_TYPE_RESISTANCE
|
|
};
|
|
|
|
int OnInit()
|
|
{
|
|
SetIndexBuffer(0, Resistance, INDICATOR_DATA);
|
|
SetIndexBuffer(1, Support, INDICATOR_DATA);
|
|
SetIndexBuffer(2, Setup, INDICATOR_DATA);
|
|
SetIndexBuffer(3, Countdown, INDICATOR_DATA);
|
|
SetIndexBuffer(4, Perfection, INDICATOR_DATA);
|
|
|
|
ArraySetAsSeries(Resistance, true);
|
|
ArraySetAsSeries(Support, true);
|
|
ArraySetAsSeries(Setup, true);
|
|
ArraySetAsSeries(Countdown, true);
|
|
ArraySetAsSeries(Perfection, true);
|
|
|
|
PlotIndexSetInteger(0, PLOT_ARROW, 158);
|
|
PlotIndexSetInteger(1, PLOT_ARROW, 158);
|
|
|
|
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
|
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
|
|
|
// Calculate the height of the font in pixels - will be needed later to space counts and arrows.
|
|
string text = "0"; // Any dummy digit text to get its height.
|
|
// Get the height of the text based on font and its size. Negative because OS-dependent, *10 because set in 1/10 of pt.
|
|
TextSetFont(FontFace, FontSize * -10);
|
|
uint w; // Dummy variable, not used anywhere except this call.
|
|
TextGetSize(text, w, FontPixelHeight);
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
void OnDeinit(const int reason)
|
|
{
|
|
ObjectsDeleteAll(0, Prefix);
|
|
}
|
|
|
|
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[])
|
|
{
|
|
ArraySetAsSeries(Time, true);
|
|
ArraySetAsSeries(High, true);
|
|
ArraySetAsSeries(Low, true);
|
|
ArraySetAsSeries(Close, true);
|
|
|
|
static double Preceding_Close = 0;
|
|
static bool Use_Preceding_Close = false;
|
|
|
|
static int Setup_Buy = 0;
|
|
static datetime Setup_Buy_First_Candle = 0;
|
|
static double Setup_Buy_Highest_High = 0;
|
|
static double Setup_Buy_Highest_High_Candidate = 0;
|
|
static double Setup_Buy_6_Low = 0;
|
|
static double Setup_Buy_7_Low = 0;
|
|
|
|
static bool No_More_Countdown_Buy_Until_Next_Buy_Setup = false;
|
|
static bool Setup_Buy_Perfected = false;
|
|
static bool Setup_Buy_Needs_Perfecting = false;
|
|
|
|
static int Countdown_Buy = 0;
|
|
static double Countdown_Buy_8_Close = 0;
|
|
|
|
static int Setup_Sell = 0;
|
|
static datetime Setup_Sell_First_Candle = 0;
|
|
static double Setup_Sell_Lowest_Low = 0;
|
|
static double Setup_Sell_Lowest_Low_Candidate = 0;
|
|
static double Setup_Sell_6_High = 0;
|
|
static double Setup_Sell_7_High = 0;
|
|
|
|
static bool No_More_Countdown_Sell_Until_Next_Sell_Setup = false;
|
|
static bool Setup_Sell_Perfected = false;
|
|
static bool Setup_Sell_Needs_Perfecting = false;
|
|
|
|
static int Countdown_Sell = 0;
|
|
static double Countdown_Sell_8_Close = 0;
|
|
|
|
if (rates_total < 7) return(0); // Not enough bars to start anything.
|
|
int counted_bars = prev_calculated > 0 ? prev_calculated - 1 : 0;
|
|
if (counted_bars < 0) return(-1);
|
|
int limit = MathMin(rates_total - 1 - counted_bars, rates_total - 6); // "- 6" because "+ 5" is used in the cycle.
|
|
if (limit < 1) return(prev_calculated); // Data not yet ready.
|
|
|
|
// Need to reset everything because prev_calculated is set to 0 when big chunks of new data are synced.
|
|
if (prev_calculated == 0)
|
|
{
|
|
Setup_Buy = 0;
|
|
Setup_Buy_First_Candle = 0;
|
|
Setup_Buy_Highest_High = 0;
|
|
Setup_Buy_6_Low = 0;
|
|
Setup_Buy_7_Low = 0;
|
|
|
|
No_More_Countdown_Buy_Until_Next_Buy_Setup = false;
|
|
Setup_Buy_Perfected = false;
|
|
Setup_Buy_Needs_Perfecting = false;
|
|
|
|
Countdown_Buy = 0;
|
|
Countdown_Buy_8_Close = 0;
|
|
|
|
Setup_Sell = 0;
|
|
Setup_Sell_First_Candle = 0;
|
|
Setup_Sell_Lowest_Low = 0;
|
|
Setup_Sell_6_High = 0;
|
|
Setup_Sell_7_High = 0;
|
|
|
|
No_More_Countdown_Sell_Until_Next_Sell_Setup = false;
|
|
Setup_Sell_Perfected = false;
|
|
Setup_Sell_Needs_Perfecting = false;
|
|
|
|
Countdown_Sell = 0;
|
|
Countdown_Sell_8_Close = 0;
|
|
|
|
ObjectsDeleteAll(0, Prefix);
|
|
|
|
for (int i = 0; i < rates_total; i++)
|
|
{
|
|
Setup[i] = 0;
|
|
Countdown[i] = 0;
|
|
Perfection[i] = 0;
|
|
Support[i] = EMPTY_VALUE;
|
|
Resistance[i] = EMPTY_VALUE;
|
|
}
|
|
}
|
|
|
|
limit = MathMin(limit, MaxBars);
|
|
|
|
for (int i = limit; i > 0; i--)
|
|
{
|
|
Setup[i] = 0;
|
|
Countdown[i] = 0;
|
|
Perfection[i] = 0;
|
|
// Cancel S/R or propagate them:
|
|
if ((Resistance[i + 1] != EMPTY_VALUE) && (Close[i] > Resistance[i + 1]))
|
|
{
|
|
Resistance[i] = EMPTY_VALUE;
|
|
if (AlertOnSupportResistance) DoAlert(i, ALERT_TYPE_RESISTANCE);
|
|
}
|
|
else Resistance[i] = Resistance[i + 1];
|
|
if ((Support[i + 1] != EMPTY_VALUE) && (Close[i] < Support[i + 1]))
|
|
{
|
|
Support[i] = EMPTY_VALUE;
|
|
if (AlertOnSupportResistance) DoAlert(i, ALERT_TYPE_SUPPORT);
|
|
}
|
|
else Support[i] = Support[i + 1];
|
|
Resistance[0] = EMPTY_VALUE; Support[0] = EMPTY_VALUE; // Always empty for current candle.
|
|
|
|
// Buy Setup.
|
|
|
|
// Price Flip check +
|
|
// First Buy Setup candle +
|
|
// Setup_Buy == 0 for when no previous Buy Setup present. Setup_Buy == 9 for when one Buy Setup follows another:
|
|
if ((Close[i + 1] >= Close[i + 5]) && (Close[i] < Close[i + 4]) && ((Setup_Buy == 0) || (Setup_Buy == 9)))
|
|
{
|
|
// Remember Close before the Setup's first bar (needed further for TDST Resistance).
|
|
Preceding_Close = Close[i + 1];
|
|
Use_Preceding_Close = false;
|
|
// Set initial values.
|
|
Setup_Buy = 1;
|
|
PutCount(COUNT_TYPE_BUY_SETUP, IntegerToString(Setup_Buy), Time[i], Low[i]);
|
|
Setup_Buy_First_Candle = Time[i]; // Remember the first candle to wipe the Setup if it fails before completing.
|
|
Setup_Buy_Highest_High_Candidate = High[i];
|
|
Setup[i] = Setup_Buy;
|
|
}
|
|
// Buy Setup candles - second through nine:
|
|
else if ((Close[i] < Close[i + 4]) && (Setup_Buy > 0) && (Setup_Buy < 9))
|
|
{
|
|
Setup_Buy++;
|
|
Setup[i] = Setup_Buy;
|
|
PutCount(COUNT_TYPE_BUY_SETUP, IntegerToString(Setup_Buy), Time[i], Low[i]);
|
|
if (Setup_Buy_Highest_High_Candidate < High[i]) Setup_Buy_Highest_High_Candidate = High[i];
|
|
|
|
// These are needed for perfection checks.
|
|
if (Setup_Buy == 6) Setup_Buy_6_Low = Low[i];
|
|
if (Setup_Buy == 7) Setup_Buy_7_Low = Low[i];
|
|
|
|
if (Setup_Buy == 9)
|
|
{
|
|
if (Countdown_Sell > 0)
|
|
{
|
|
Countdown_Sell = 0; // Reset Sell Countdown.
|
|
RemoveCount(COUNT_TYPE_SELL_COUNTDOWN, Time[i], Time);
|
|
}
|
|
No_More_Countdown_Sell_Until_Next_Sell_Setup = true;
|
|
if (AlertOnSetup) DoAlert(i, ALERT_TYPE_SETUP_BUY);
|
|
Setup_Buy_Perfected = false; // Will be checked farther.
|
|
Setup_Buy_Needs_Perfecting = true;
|
|
No_More_Countdown_Buy_Until_Next_Buy_Setup = false;
|
|
Setup_Sell = 0;
|
|
if (Preceding_Close > Setup_Buy_Highest_High_Candidate)
|
|
{
|
|
Setup_Buy_Highest_High = Preceding_Close;
|
|
Use_Preceding_Close = true;
|
|
}
|
|
else Setup_Buy_Highest_High = Setup_Buy_Highest_High_Candidate;
|
|
int res_count = 9;
|
|
if (Use_Preceding_Close) res_count = 10; // If preceding bar's Close is used to draw the Resistance line, it should be started at that bar's Close.
|
|
for (int j = i; j < i + res_count; j++)
|
|
{
|
|
Resistance[j] = Setup_Buy_Highest_High;
|
|
if (High[j] == Setup_Buy_Highest_High) break;
|
|
}
|
|
// Check perfecting:
|
|
// Setup candle #8:
|
|
if ((Low[i + 1] < Setup_Buy_6_Low) && (Low[i + 1] < Setup_Buy_7_Low))
|
|
{
|
|
Setup_Buy_Perfected = true;
|
|
Setup_Buy_Needs_Perfecting = false;
|
|
Perfection[i + 1] = 1;
|
|
PutCount(COUNT_TYPE_BUY_PERFECTION, "233", Time[i + 1], High[i + 1]); // Arrow up.
|
|
if (AlertOnPerfecting) DoAlert(i + 1, ALERT_TYPE_PERFECTING_BUY);
|
|
}
|
|
// Countdown can also start on this candle but farther in the code.
|
|
}
|
|
}
|
|
// Buy Setup broken:
|
|
else if ((Close[i] >= Close[i + 4]) && (Setup_Buy != 9) && (Setup_Buy != 0))
|
|
{
|
|
RemoveCount(COUNT_TYPE_BUY_SETUP, Setup_Buy_First_Candle, Time, Setup_Buy); // Remove number objects for this Buy Setup.
|
|
Setup_Buy = 0;
|
|
Setup_Buy_First_Candle = 0;
|
|
Setup_Buy_Highest_High_Candidate = 0;
|
|
Setup_Buy_Needs_Perfecting = false;
|
|
Setup_Buy_Perfected = false;
|
|
}
|
|
|
|
// Buy Countdown check.
|
|
if ((!No_More_Countdown_Buy_Until_Next_Buy_Setup) && ((Setup_Buy == 9) || (Countdown_Buy > 0))) // Have a completed Buy Setup or a started Countdown.
|
|
{
|
|
if (Countdown_Buy < 13)
|
|
{
|
|
if (Close[i] <= Low[i + 2])
|
|
{
|
|
if (Countdown_Buy < 12) // Normal condition for Countdown candles from 1 through 12.
|
|
{
|
|
Countdown_Buy++;
|
|
if (Countdown_Buy == 8) Countdown_Buy_8_Close = Close[i]; // Remember 8th Countdown close for checking the 13th.
|
|
Countdown[i] = Countdown_Buy;
|
|
PutCount(COUNT_TYPE_BUY_COUNTDOWN, IntegerToString(Countdown_Buy), Time[i], Low[i]);
|
|
}
|
|
else if (Low[i] < Countdown_Buy_8_Close) // Special condition for Countdown candle 13.
|
|
{
|
|
Countdown_Buy++;
|
|
Countdown[i] = Countdown_Buy;
|
|
PutCount(COUNT_TYPE_BUY_COUNTDOWN, IntegerToString(Countdown_Buy), Time[i], Low[i]);
|
|
if (AlertOnCountdown13) DoAlert(i, ALERT_TYPE_COUNT13_BUY);
|
|
Countdown_Buy = 0; // Reset Countdown as we have already reached count 13.
|
|
No_More_Countdown_Buy_Until_Next_Buy_Setup = true;
|
|
}
|
|
else // Failed Countdown candles 13 are marked with +.
|
|
{
|
|
Countdown[i] = 14;
|
|
PutCount(COUNT_TYPE_BUY_COUNTDOWN, "+", Time[i], Low[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if Countdown is broken.
|
|
if (Countdown_Buy > 0) // Have a Buy Countdown that can be interrupted.
|
|
{
|
|
if ((Low[i] > Setup_Buy_Highest_High) && (Close[i + 1] > Setup_Buy_Highest_High))
|
|
{
|
|
Countdown_Buy = 0;
|
|
RemoveCount(COUNT_TYPE_BUY_COUNTDOWN, Time[i], Time);
|
|
Setup_Buy = 0;
|
|
}
|
|
}
|
|
// Check if Setup is perfected.
|
|
if ((!Setup_Buy_Perfected) && (Setup_Buy_Needs_Perfecting)) // Setup Buy candles #9+.
|
|
{
|
|
if ((Low[i] < Setup_Buy_6_Low) && (Low[i] < Setup_Buy_7_Low))
|
|
{
|
|
Setup_Buy_Perfected = true;
|
|
Setup_Buy_Needs_Perfecting = false;
|
|
Perfection[i] = 1;
|
|
PutCount(COUNT_TYPE_BUY_PERFECTION, "233", Time[i], High[i]); // Arrow up.
|
|
if (AlertOnPerfecting) DoAlert(i, ALERT_TYPE_PERFECTING_BUY);
|
|
}
|
|
}
|
|
|
|
// Sell Setup.
|
|
|
|
// Price Flip check +
|
|
// First Sell Setup candle +
|
|
// Setup_Sell == 0 for when no previous Sell Setup present. Setup_Sell == 9 for when one Sell Setup follows another:
|
|
if ((Close[i + 1] <= Close[i + 5]) && (Close[i] > Close[i + 4]) && ((Setup_Sell == 0) || (Setup_Sell == 9)))
|
|
{
|
|
// Remember Close before the Setup's first bar (needed further for TDST Support).
|
|
Preceding_Close = Close[i + 1];
|
|
Use_Preceding_Close = false;
|
|
// Set initial values.
|
|
Setup_Sell = 1;
|
|
PutCount(COUNT_TYPE_SELL_SETUP, IntegerToString(Setup_Sell), Time[i], High[i]);
|
|
Setup_Sell_First_Candle = Time[i]; // Remember the first candle to wipe the Setup if it fails before completing.
|
|
Setup_Sell_Lowest_Low_Candidate = Low[i];
|
|
Setup[i] = -Setup_Sell;
|
|
}
|
|
// Sell Setup candles - second through nine:
|
|
else if ((Close[i] > Close[i + 4]) && (Setup_Sell > 0) && (Setup_Sell < 9))
|
|
{
|
|
Setup_Sell++;
|
|
Setup[i] = -Setup_Sell;
|
|
PutCount(COUNT_TYPE_SELL_SETUP, IntegerToString(Setup_Sell), Time[i], High[i]);
|
|
if (Setup_Sell_Lowest_Low_Candidate > Low[i]) Setup_Sell_Lowest_Low_Candidate = Low[i];
|
|
|
|
// These are needed for perfection checks.
|
|
if (Setup_Sell == 6) Setup_Sell_6_High = High[i];
|
|
if (Setup_Sell == 7) Setup_Sell_7_High = High[i];
|
|
|
|
if (Setup_Sell == 9)
|
|
{
|
|
if (Countdown_Buy > 0)
|
|
{
|
|
Countdown_Buy = 0; // Reset Buy Countdown.
|
|
RemoveCount(COUNT_TYPE_BUY_COUNTDOWN, Time[i], Time);
|
|
}
|
|
No_More_Countdown_Buy_Until_Next_Buy_Setup = true;
|
|
if (AlertOnSetup) DoAlert(i, ALERT_TYPE_SETUP_SELL);
|
|
Setup_Sell_Perfected = false; // Will be checked farther.
|
|
Setup_Sell_Needs_Perfecting = true;
|
|
No_More_Countdown_Sell_Until_Next_Sell_Setup = false;
|
|
Setup_Buy = 0;
|
|
if (Preceding_Close < Setup_Sell_Lowest_Low_Candidate)
|
|
|
|
{
|
|
Setup_Sell_Lowest_Low = Preceding_Close;
|
|
Use_Preceding_Close = true;
|
|
}
|
|
else Setup_Sell_Lowest_Low = Setup_Sell_Lowest_Low_Candidate;
|
|
int supp_count = 9;
|
|
if (Use_Preceding_Close) supp_count = 10; // If preceding bar's Close is used to draw the Support line, it should be started at that bar's Close.
|
|
for (int j = i; j < i + supp_count; j++)
|
|
{
|
|
Support[j] = Setup_Sell_Lowest_Low;
|
|
if (Low[j] == Setup_Sell_Lowest_Low) break;
|
|
}
|
|
// Check perfecting:
|
|
// Setup candle #8:
|
|
if ((High[i + 1] > Setup_Sell_6_High) && (High[i + 1] > Setup_Sell_7_High))
|
|
{
|
|
Setup_Sell_Perfected = true;
|
|
Setup_Sell_Needs_Perfecting = false;
|
|
Perfection[i + 1] = -1;
|
|
PutCount(COUNT_TYPE_SELL_PERFECTION, "234", Time[i + 1], Low[i + 1]); // Arrow down.
|
|
if (AlertOnPerfecting) DoAlert(i + 1, ALERT_TYPE_PERFECTING_SELL);
|
|
}
|
|
// Countdown can also start on this candle but farther in the code.
|
|
}
|
|
}
|
|
// Sell Setup broken:
|
|
else if ((Close[i] <= Close[i + 4]) && (Setup_Sell != 9) && (Setup_Sell != 0))
|
|
{
|
|
RemoveCount(COUNT_TYPE_SELL_SETUP, Setup_Sell_First_Candle, Time, Setup_Sell); // Remove number objects for this Sell Setup.
|
|
Setup_Sell = 0;
|
|
Setup_Sell_First_Candle = 0;
|
|
Setup_Sell_Lowest_Low_Candidate = 0;
|
|
Setup_Sell_Needs_Perfecting = false;
|
|
Setup_Sell_Perfected = false;
|
|
}
|
|
|
|
// Sell Countdown check.
|
|
if ((!No_More_Countdown_Sell_Until_Next_Sell_Setup) && ((Setup_Sell == 9) || (Countdown_Sell > 0))) // Have a completed Sell Setup or a started Countdown.
|
|
{
|
|
if (Countdown_Sell < 13)
|
|
{
|
|
if (Close[i] >= High[i + 2])
|
|
{
|
|
if (Countdown_Sell < 12) // Normal condition for Countdown candles from 1 through 12.
|
|
{
|
|
Countdown_Sell++;
|
|
if (Countdown_Sell == 8) Countdown_Sell_8_Close = Close[i]; // Remember 8th Countdown close for checking the 13th.
|
|
Countdown[i] = -Countdown_Sell;
|
|
PutCount(COUNT_TYPE_SELL_COUNTDOWN, IntegerToString(Countdown_Sell), Time[i], High[i]);
|
|
}
|
|
else if (High[i] > Countdown_Sell_8_Close) // Special condition for Countdown candle 13.
|
|
{
|
|
Countdown_Sell++;
|
|
Countdown[i] = -Countdown_Sell;
|
|
PutCount(COUNT_TYPE_SELL_COUNTDOWN, IntegerToString(Countdown_Sell), Time[i], High[i]);
|
|
if (AlertOnCountdown13) DoAlert(i, ALERT_TYPE_COUNT13_SELL);
|
|
Countdown_Sell = 0; // Reset Countdown as we have already reached count 13.
|
|
No_More_Countdown_Sell_Until_Next_Sell_Setup = true;
|
|
}
|
|
else // Failed Countdown candles 13 are marked with +.
|
|
{
|
|
Countdown[i] = -14;
|
|
PutCount(COUNT_TYPE_SELL_COUNTDOWN, "+", Time[i], High[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if Countdown is broken.
|
|
if (Countdown_Sell > 0) // Have a Sell Countdown that can be interrupted.
|
|
{
|
|
if ((High[i] < Setup_Sell_Lowest_Low) && (Close[i + 1] < Setup_Sell_Lowest_Low))
|
|
{
|
|
Countdown_Sell = 0;
|
|
RemoveCount(COUNT_TYPE_SELL_COUNTDOWN, Time[i], Time);
|
|
Setup_Sell = 0;
|
|
}
|
|
}
|
|
// Check if Setup is perfected.
|
|
if ((!Setup_Sell_Perfected) && (Setup_Sell_Needs_Perfecting)) // Setup Sell candles #9+.
|
|
{
|
|
if ((High[i] > Setup_Sell_6_High) && (High[i] > Setup_Sell_7_High))
|
|
{
|
|
Setup_Sell_Perfected = true;
|
|
Setup_Sell_Needs_Perfecting = false;
|
|
Perfection[i] = -1;
|
|
PutCount(COUNT_TYPE_SELL_PERFECTION, "234", Time[i], Low[i]); // Arrow down.
|
|
if (AlertOnPerfecting) DoAlert(i, ALERT_TYPE_PERFECTING_SELL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(rates_total);
|
|
}
|
|
|
|
void OnChartEvent(const int id,
|
|
const long &lparam,
|
|
const double &dparam,
|
|
const string &sparam)
|
|
{
|
|
if (id == CHARTEVENT_CHART_CHANGE) RedrawVisibleLabels();
|
|
}
|
|
|
|
void PutCount(const ENUM_COUNT_TYPE count_type, const string s, const datetime time, const double price)
|
|
{
|
|
string name = Prefix;
|
|
color colour = clrNONE;
|
|
int level = 0;
|
|
ENUM_OBJECT object_type = OBJ_TEXT;
|
|
bool adjust_for_height = false;
|
|
|
|
switch(count_type)
|
|
{
|
|
case COUNT_TYPE_BUY_SETUP:
|
|
name += "BS";
|
|
colour = BuySetupColor;
|
|
level = 1;
|
|
break;
|
|
case COUNT_TYPE_SELL_SETUP:
|
|
name += "SS";
|
|
colour = SellSetupColor;
|
|
level = -1;
|
|
adjust_for_height = true;
|
|
break;
|
|
case COUNT_TYPE_BUY_COUNTDOWN:
|
|
name += "BC";
|
|
colour = CountdownColor;
|
|
level = 2;
|
|
break;
|
|
case COUNT_TYPE_SELL_COUNTDOWN:
|
|
name += "SC";
|
|
colour = CountdownColor;
|
|
level = -2;
|
|
adjust_for_height = true;
|
|
break;
|
|
case COUNT_TYPE_BUY_PERFECTION:
|
|
name += "BP";
|
|
colour = BuySetupColor;
|
|
level = 3;
|
|
object_type = OBJ_ARROW;
|
|
break;
|
|
case COUNT_TYPE_SELL_PERFECTION:
|
|
name += "SP";
|
|
colour = SellSetupColor;
|
|
level = -3;
|
|
object_type = OBJ_ARROW;
|
|
adjust_for_height = true;
|
|
break;
|
|
}
|
|
|
|
name += IntegerToString((int)time);
|
|
|
|
ObjectCreate(0, name, object_type, 0, time, price);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, colour);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
|
|
if ((count_type == COUNT_TYPE_BUY_PERFECTION) || (count_type == COUNT_TYPE_SELL_PERFECTION))
|
|
{
|
|
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, StringToInteger(s));
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, ArrowWidth);
|
|
}
|
|
else
|
|
{
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize);
|
|
ObjectSetString(0, name, OBJPROP_FONT, FontFace);
|
|
ObjectSetString(0, name, OBJPROP_TEXT, s);
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_UPPER);
|
|
}
|
|
|
|
RedrawOneWickLabel(name, time, price, level);
|
|
}
|
|
|
|
// level - positional level above (-) and below the candles (+).
|
|
void RedrawOneWickLabel(const string name, const datetime time, const double price, const int level)
|
|
{
|
|
int x, y, cw;
|
|
datetime t;
|
|
double p, p_0;
|
|
|
|
if (ObjectFind(0, name) == -1) return;
|
|
|
|
int distance = 0;
|
|
if (level < 0) distance = level * (int)FontPixelHeight + level * PixelDistance; // Objects above the candle should be additionally adjusted for their own height.
|
|
else distance = (level - 1) * (int)FontPixelHeight + level * PixelDistance;
|
|
|
|
// Needed only for y; x is used as a dummy.
|
|
ChartTimePriceToXY(0, 0, time, price, x, y);
|
|
|
|
ChartXYToTimePrice(0, x, y + distance, cw, t, p);
|
|
|
|
// Went above the screen - MT5 ChartXYToTimePrice will invert the offset, so we need to adjust it.
|
|
if (y + distance < 0)
|
|
{
|
|
// Get the price of Y = 0.
|
|
ChartXYToTimePrice(0, x, 0, cw, t, p_0);
|
|
p = p_0 + (p_0 - p); // Return the price to its "above the screen edge" position.
|
|
}
|
|
|
|
ObjectSetDouble(0, name, OBJPROP_PRICE, p);
|
|
}
|
|
|
|
void RedrawVisibleLabels()
|
|
{
|
|
int visible_bars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
|
|
int first_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
|
|
int last_bar = first_bar - visible_bars + 1;
|
|
|
|
// Process all bars on the current screen.
|
|
// Cycle through all possible object names based on prefix + count_type_name + timestamp.
|
|
for (int i = first_bar; i >= last_bar; i--)
|
|
{
|
|
datetime time = iTime(Symbol(), Period(), i);
|
|
double low = iLow(Symbol(), Period(), i);
|
|
double high = iHigh(Symbol(), Period(), i);
|
|
string time_string = IntegerToString((int)time);
|
|
RedrawOneWickLabel(Prefix + "BS" + time_string, time, low, 1);
|
|
RedrawOneWickLabel(Prefix + "SS" + time_string, time, high, -1);
|
|
RedrawOneWickLabel(Prefix + "BC" + time_string, time, low, 2);
|
|
RedrawOneWickLabel(Prefix + "SC" + time_string, time, high, -2);
|
|
RedrawOneWickLabel(Prefix + "BP" + time_string, time, low, 3);
|
|
RedrawOneWickLabel(Prefix + "SP" + time_string, time, high, -3);
|
|
}
|
|
|
|
ChartRedraw();
|
|
}
|
|
|
|
void RemoveCount(const ENUM_COUNT_TYPE count_type, const datetime begin, const datetime &Time[], const int n = 0)
|
|
{
|
|
string name_start = Prefix;
|
|
if (count_type == COUNT_TYPE_BUY_SETUP) name_start += "BS";
|
|
else if (count_type == COUNT_TYPE_SELL_SETUP) name_start += "SS";
|
|
else if (count_type == COUNT_TYPE_BUY_COUNTDOWN) name_start += "BC";
|
|
else if (count_type == COUNT_TYPE_SELL_COUNTDOWN) name_start += "SC";
|
|
|
|
int begin_candle = iBarShift(Symbol(), Period(), begin, true);
|
|
|
|
if (begin_candle == -1) return; // Some data error.
|
|
|
|
// Setup
|
|
if ((count_type == COUNT_TYPE_BUY_SETUP) || (count_type == COUNT_TYPE_SELL_SETUP))
|
|
{
|
|
for (int i = begin_candle; i > begin_candle - n; i--)
|
|
{
|
|
string name = name_start + IntegerToString(Time[i]);
|
|
ObjectDelete(0, name);
|
|
}
|
|
}
|
|
// Countdown
|
|
else if ((count_type == COUNT_TYPE_BUY_COUNTDOWN) || (count_type == COUNT_TYPE_SELL_COUNTDOWN))
|
|
{
|
|
// The number of countdown objects is unknown.
|
|
for (int i = begin_candle; i < Bars(Symbol(), Period()); i++)
|
|
{
|
|
string name = name_start + IntegerToString(Time[i]);
|
|
if (ObjectGetString(ChartID(), name, OBJPROP_TEXT) == "1") // The last one.
|
|
{
|
|
ObjectDelete(0, name);
|
|
return;
|
|
}
|
|
ObjectDelete(0, name); // Attempt to delete even if it doesn't exist.
|
|
}
|
|
}
|
|
}
|
|
|
|
void DoAlert(int i, ENUM_ALERT_TYPE alert_type)
|
|
{
|
|
if (i > 2) return; // i == 2 the earliest possible alert event that should be reacted on.
|
|
|
|
string main_text = "Sequential: " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period());
|
|
string email_subject = main_text;
|
|
//string
|
|
switch(alert_type)
|
|
{
|
|
case ALERT_TYPE_SETUP_BUY:
|
|
main_text += " Buy Setup completed by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Buy Setup";
|
|
break;
|
|
case ALERT_TYPE_SETUP_SELL:
|
|
main_text += " Sell Setup completed by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Sell Setup";
|
|
break;
|
|
case ALERT_TYPE_PERFECTING_BUY:
|
|
main_text += " Buy Setup Perfected by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Buy Perfecting";
|
|
break;
|
|
case ALERT_TYPE_PERFECTING_SELL:
|
|
main_text += " Sell Setup Perfected by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Sell Perfecting";
|
|
break;
|
|
case ALERT_TYPE_COUNT13_BUY:
|
|
main_text += " Buy Countdown completed by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Buy Countdown";
|
|
break;
|
|
case ALERT_TYPE_COUNT13_SELL:
|
|
main_text += " Sell Countdown completed by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Sell Countdown";
|
|
break;
|
|
case ALERT_TYPE_RESISTANCE:
|
|
main_text += " TDST Resistance broken by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " Buy TDST Resistance";
|
|
break;
|
|
case ALERT_TYPE_SUPPORT:
|
|
main_text += " TDST Support broken by " + TimeToString(iTime(Symbol(), Period(), i)) + " candle.";
|
|
email_subject += " TDST Support";
|
|
break;
|
|
}
|
|
|
|
if (AlertNative) Alert(main_text);
|
|
if (AlertEmail) SendMail(email_subject, main_text);
|
|
if (AlertNotification) SendNotification(main_text);
|
|
}
|
|
//+------------------------------------------------------------------+ |