#property copyright "EarnForex.com" #property link "https://www.earnforex.com/metatrader-indicators/TD-Sequential-Ultimate/" #property version "1.03" #property strict #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 #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_color3 clrNONE #property indicator_type3 DRAW_NONE #property indicator_color4 clrNONE #property indicator_type4 DRAW_NONE #property indicator_color5 clrNONE #property indicator_type5 DRAW_NONE //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; //Forex-Station button template start41; copy and paste // https://forex-station.com/viewtopic.php?p=1295478806#p1295478806 // https://forex-station.com/viewtopic.php?p=1295478883#p1295478883 input string button_note1_ = "------------------------------"; input int btn_Subwindow = 0; // What window to put the button on. If <0, the button will use the same sub-window as the indicator. input ENUM_BASE_CORNER btn_corner = CORNER_LEFT_UPPER; // button corner on chart for anchoring input string btn_text = "SEQUENTIAL"; // a button name input string btn_Font = "Arial"; // button font name input int btn_FontSize = 8; // button font size input color btn_text_ON_color = clrLime; // ON color when the button is turned on input color btn_text_OFF_color = clrRed; // OFF color when the button is turned off input color btn_background_color = clrDimGray; // background color of the button input color btn_border_color = clrBlack; // border color the button input int button_x = 20; // x coordinate of the button input int button_y = 25; // y coordinate of the button input int btn_Width = 80; // button width input int btn_Height = 20; // button height input string button_note2 = "------------------------------"; bool show_data, recalc=false; int swin, global_prev_calculated, global_rates_total; string IndicatorObjPrefix, buttonId; //Forex-Station button template end41; copy and paste // 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() { IndicatorDigits(Digits); IndicatorObjPrefix = "__" + btn_text + "__"; // The leading "_" gives buttonId a *unique* prefix. Furthermore, prepending the swin is usually unique unless >2+ of THIS indy are displayed in the SAME sub-window. (But, if >2 used, be sure to shift the buttonId position) buttonId = "_" + IndicatorObjPrefix + "_BT_"; if (ObjectFind(buttonId)<0) createButton(buttonId, btn_text, btn_Width, btn_Height, btn_Font, btn_FontSize, btn_background_color, btn_border_color, btn_text_ON_color); ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE, button_y); ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE, button_x); OnInit2(); show_data = ObjectGetInteger(0, buttonId, OBJPROP_STATE); if (show_data) ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_ON_color); else ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_OFF_color); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------------------------------------------------------+ void createButton(string buttonID,string buttonText,int width2,int height,string font,int fontSize,color bgColor,color borderColor,color txtColor) { ObjectDelete (0,buttonID); ObjectCreate (0,buttonID,OBJ_BUTTON,btn_Subwindow,0,0); ObjectSetInteger(0,buttonID,OBJPROP_COLOR,txtColor); ObjectSetInteger(0,buttonID,OBJPROP_BGCOLOR,bgColor); ObjectSetInteger(0,buttonID,OBJPROP_BORDER_COLOR,borderColor); ObjectSetInteger(0,buttonID,OBJPROP_BORDER_TYPE,BORDER_RAISED); ObjectSetInteger(0,buttonID,OBJPROP_XSIZE,width2); ObjectSetInteger(0,buttonID,OBJPROP_YSIZE,height); ObjectSetString (0,buttonID,OBJPROP_FONT,font); ObjectSetString (0,buttonID,OBJPROP_TEXT,buttonText); ObjectSetInteger(0,buttonID,OBJPROP_FONTSIZE,fontSize); ObjectSetInteger(0,buttonID,OBJPROP_SELECTABLE,0); ObjectSetInteger(0,buttonID,OBJPROP_CORNER,btn_corner); ObjectSetInteger(0,buttonID,OBJPROP_HIDDEN,1); ObjectSetInteger(0,buttonID,OBJPROP_XDISTANCE,9999); ObjectSetInteger(0,buttonID,OBJPROP_YDISTANCE,9999); // Upon creation, set the initial state to "true" which is "on", so one will see the indicator by default ObjectSetInteger(0, buttonId, OBJPROP_STATE, true); } //+------------------------------------------------------------------------------------------------------------------+ void OnDeinit(const int reason) { // This 'ObjectsDeleteAll' is only needed when any objects *besides* the button are created, but this indicator does not, hence, not needed. //ObjectsDeleteAll(0, IndicatorObjPrefix); OnDeinit2(); // If just changing a TF', the button need not be deleted, therefore the 'OBJPROP_STATE' is also preserved. if(reason != REASON_CHARTCHANGE) ObjectDelete(buttonId); } //+------------------------------------------------------------------------------------------------------------------+ void OnChartEvent(const int id, //don't change anything here const long &lparam, const double &dparam, const string &sparam) { // If another indy on the same chart has enabled events for create/delete/mouse-move, just skip this events up front because they aren't // needed, AND in the worst case, this indy might cause MT4 to hang!! Skipping the events seems to help, along with other (major) changes to the code below. if(id==CHARTEVENT_OBJECT_CREATE || id==CHARTEVENT_OBJECT_DELETE) return; // This appears to make this indy compatible with other programs that enabled CHART_EVENT_OBJECT_CREATE and/or CHART_EVENT_OBJECT_DELETE if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_MOUSE_WHEEL) return; // If this, or another program, enabled mouse-events, these are not needed below, so skip it unless actually needed. if ((id == CHARTEVENT_CHART_CHANGE) || (id == CHARTEVENT_CLICK)) RedrawVisibleLabels(); if (id==CHARTEVENT_OBJECT_CLICK && sparam == buttonId) { show_data = ObjectGetInteger(0, buttonId, OBJPROP_STATE); if (show_data) { ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_ON_color); SetIndexStyle(0,DRAW_ARROW); SetIndexStyle(1,DRAW_ARROW); // Is it a problem to call 'start()' ?? Possibly it makes no difference, but now calling "mystart()" instead of "start()"; and "start()" simply runs "mystart()", so should be same as before. recalc=true; mystart(); } else { ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_OFF_color); OnDeinit2(); for (int ForexStation=0; ForexStation 0 ? global_prev_calculated - 1 : 0; if (counted_bars < 0) return(-1); if(recalc) { // If a button goes from off-to-on, everything must be recalculated. The 'recalc' variable is used as a trigger to do this. counted_bars = 0; recalc=false; } int limit = MathMin(global_rates_total - 1 - counted_bars, global_rates_total - 6); // "- 6" because "+ 5" is used in the cycle. if (limit < 1) return(global_prev_calculated); // Data not yet ready. // Need to reset everything because global_prev_calculated is set to 0 when big chunks of new data are synced. if (global_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 < global_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]; // 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]); } 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, 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]); 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]); } 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, 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]); 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); } } } } //if (show_data) return(0); //mystart () } //+------------------------------------------------------------------------------------------------------------------+ 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; 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); 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 = Time[i]; double low = Low[i]; double high = High[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); } } // Remove number objects for a given setup or countdown. void RemoveCount(const ENUM_COUNT_TYPE count_type, const datetime begin, 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; 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(Time[i]) + " candle."; email_subject += " Buy Setup"; break; case ALERT_TYPE_SETUP_SELL: main_text += " Sell Setup completed by " + TimeToString(Time[i]) + " candle."; email_subject += " Sell Setup"; break; case ALERT_TYPE_PERFECTING_BUY: main_text += " Buy Setup Perfected by " + TimeToString(Time[i]) + " candle."; email_subject += " Buy Perfecting"; break; case ALERT_TYPE_PERFECTING_SELL: main_text += " Sell Setup Perfected by " + TimeToString(Time[i]) + " candle."; email_subject += " Sell Perfecting"; break; case ALERT_TYPE_COUNT13_BUY: main_text += " Buy Countdown completed by " + TimeToString(Time[i]) + " candle."; email_subject += " Buy Countdown"; break; case ALERT_TYPE_COUNT13_SELL: main_text += " Sell Countdown completed by " + TimeToString(Time[i]) + " candle."; email_subject += " Sell Countdown"; break; case ALERT_TYPE_RESISTANCE: main_text += " TDST Resistance broken by " + TimeToString(Time[i]) + " candle."; email_subject += " Buy TDST Resistance"; break; case ALERT_TYPE_SUPPORT: main_text += " TDST Support broken by " + TimeToString(Time[i]) + " candle."; email_subject += " TDST Support"; break; } if (AlertNative) Alert(main_text); if (AlertEmail) SendMail(email_subject, main_text); if (AlertNotification) SendNotification(main_text); } //+------------------------------------------------------------------+