-
Notifications
You must be signed in to change notification settings - Fork 1
/
TD Sequential Ultimate.mq5
729 lines (657 loc) · 27.7 KB
/
TD Sequential Ultimate.mq5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
#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);
}
//+------------------------------------------------------------------+