Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
778 blopes 1
package br.com.kronus.core;
2
 
3
import br.com.ec.core.util.VerificadorUtil;
4
import br.com.kronus.core.Candle;
5
 
6
import java.util.ArrayList;
7
import java.util.List;
8
 
9
/**
10
 * Detector + Monitor de Gatilhos (Referência, G1, G2, G3, G4)
11
 *
12
 * REGRAS IMPLEMENTADAS (resumo):
13
 *
14
 * Candle Referência:
15
 *  - Candle que demarca uma região (topo ou fundo) e que posteriormente
16
 *    será rompido contrariamente à sua tendência pelo Gatilho 1.
17
 *  - Ex.: candle comprador -> se um candle posterior romper seu FUNDO,
18
 *        esse posterior é G1 e o rompido é a referência.
19
 *    Ex.: candle vendedor -> se um candle posterior romper seu TOPO,
20
 *        esse posterior é G1 e o rompido é a referência.
21
 *
22
 * Gatilho 1:
23
 *  - Primeiro candle que rompe a tendência contrária a algum candle anterior
24
 *    (sem insiders).
25
 *  - Se G1 for COMPRADOR: deve romper o TOPO do candle referência.
26
 *  - Se G1 for VENDEDOR: deve romper o FUNDO do candle referência.
27
 *
28
 * Gatilho 2:
29
 *  - Candle que retorna à região do candle referência buscando liquidez.
30
 *  - Último candle antes da nova quebra de tendência.
31
 *  - Fechamento deve estar dentro da região total (incluindo pavio)
32
 *    do candle referência.
33
 *  - Se ultrapassar a máxima (ou mínima) do candle referência, deve-se procurar
34
 *    por um novo candle referência a partir desse candle.
35
 *  - EXCEÇÃO: se o candle for da mesma tendência de G1 e tiver rompido
36
 *    sua tendência (novo topo na compra / novo fundo na venda), também é G2.
37
 *
38
 * Gatilho 3:
39
 *  - Candle posterior ao G2.
40
 *  - Mesma tendência de G1.
41
 *  - Se referência for COMPRADORA:
42
 *      * topo de G3 < topo da referência
43
 *      * G3 rompe o FUNDO do G2
44
 *      * se romper o topo da referência após o G2 → invalida padrão.
45
 *  - Se referência for VENDEDORA:
46
 *      * topo de G3 > topo da referência
47
 *      * G3 rompe o TOPO do G2
48
 *      * se romper o fundo da referência após o G2 → invalida padrão.
49
 *
50
 * Gatilho 4:
51
 *  - Desarma a operação.
52
 *  - Se referência COMPRADORA: seu topo será rompido pelo candle do G4.
53
 *  - Se referência VENDEDORA: seu fundo será rompido.
54
 *  - Após G4 a análise recomeça.
55
 */
56
public class DetectorGatilhos_ {
57
 
58
    private Candle referencia;
59
    private Candle gatilho1;
60
    private Candle gatilho2;
61
    private Candle gatilho3;
62
    private Candle gatilho4;
63
    private int idxRef  = -1;
64
    private int idxG1   = -1;
65
    private int idxG2   = -1;
66
    private int idxG3   = -1;
67
    private int idxG4   = -1;
68
 
69
    private boolean candidatoG4Avaliado = false;
70
 
71
 
72
    private PadraoGatilho padraoAtual;
73
 
74
    private boolean logAtivo;
75
 
76
    public DetectorGatilhos_() {
77
        this(false);
78
    }
79
 
80
    public DetectorGatilhos_(boolean logAtivo) {
81
        this.logAtivo = logAtivo;
82
    }
83
 
84
    public boolean isLogAtivo() {
85
        return logAtivo;
86
    }
87
 
88
    public void setLogAtivo(boolean logAtivo) {
89
        this.logAtivo = logAtivo;
90
    }
91
 
92
    private void log(String msg) {
93
        if (logAtivo) {
94
            System.out.println(msg);
95
        }
96
    }
97
 
98
    private void reset() {
99
        referencia = null;
100
        gatilho1 = null;
101
        gatilho2 = null;
102
        gatilho3 = null;
103
        gatilho4 = null;
104
        padraoAtual = null;
105
 
106
        idxRef = -1;
107
        idxG1  = -1;
108
        idxG2  = -1;
109
        idxG3  = -1;
110
        idxG4  = -1;
111
 
112
        candidatoG4Avaliado = false;
113
    }
114
 
115
    // ============================================================
116
    //  A) MODO SIMULADOR / BACKTEST
117
    //     -> continua: List<PadraoGatilho> padroes = detector.identificarPadroes(candles);
118
    // ============================================================
119
 
120
    public List<PadraoGatilho> identificarPadroes(List<Candle> candles) {
121
        reset();
122
        List<PadraoGatilho> padroes = new ArrayList<>();
123
 
124
        for (int i = 0; i < candles.size(); i++) {
125
            processarCandleInterno(candles, i, null, padroes);
126
        }
127
 
128
        return padroes;
129
    }
130
 
131
    // ============================================================
132
    //  B) MODO MONITOR (tempo real / replay)
133
    // ============================================================
134
 
135
    /**
136
     * Processa o candle idx e retorna os logs deste passo.
137
     * Os logs também são enviados para System.out.println se logAtivo = true.
138
     */
139
    public List<String> processarCandle(List<Candle> candles, int idx) {
140
        List<String> logs = new ArrayList<>();
141
        processarCandleInterno(candles, idx, logs, null);
142
        return logs;
143
    }
144
 
145
    // ============================================================
146
    //  C) LÓGICA INTERNA (compartilhada entre simulador e monitor)
147
    // ============================================================
148
 
149
    private void processarCandleInterno(List<Candle> candles,
150
                                        int idx,
151
                                        List<String> logs,
152
                                        List<PadraoGatilho> padroes) {
153
 
154
        if (idx < 0 || idx >= candles.size()) {
155
            return;
156
        }
157
 
158
        Candle atual = candles.get(idx);
159
        if (VerificadorUtil.naoEstaNulo(atual)) {
160
                if (VerificadorUtil.estaNulo(atual.getContador())) {
161
                        System.out.println("TESTE");
162
                } else {
163
                        if (atual.getContador() == 11) {
164
                                System.out.println("TESTE");
165
                        }
166
                        if (atual.getContador() == 57) {
167
                                System.out.println("TESTE");
168
                        }
169
                }
170
        }
171
 
172
        // --------------------------------------------------------------------
173
        // 1) Ainda não temos Ref + G1 -> tentar formar com o candle atual
174
        // --------------------------------------------------------------------
175
        if (referencia == null || gatilho1 == null) {
176
            ParRefG1 par = encontrarReferenciaEGatilho1(candles, idx);
177
 
178
            if (par != null) {
179
                referencia = par.referencia;
180
                gatilho1   = par.g1;
181
 
182
                idxRef = candles.indexOf(referencia);
183
                idxG1  = candles.indexOf(gatilho1);
184
 
185
                // Segurança extra: não aceitar se por algum bug der o mesmo índice ou inverso
186
                if (idxRef < 0 || idxG1 < 0 || idxRef >= idxG1) {
187
                    // Inconsistente, descarta e segue
188
                    referencia = null;
189
                    gatilho1   = null;
190
                    idxRef = -1;
191
                    idxG1  = -1;
192
                    String msg = String.format(
193
                            "[%d] Par (Ref, G1) inconsistente encontrado. Descartando.",
194
                            idx
195
                    );
196
                    if (logs != null) logs.add(msg);
197
                    log(msg);
198
                    return;
199
                }
200
 
201
                gatilho2 = null;
202
                gatilho3 = null;
203
                gatilho4 = null;
204
                idxG2 = idxG3 = idxG4 = -1;
205
 
206
                padraoAtual = new PadraoGatilho();
207
                padraoAtual.setReferencia(referencia);
208
                padraoAtual.setGatilho1(gatilho1);
209
 
210
                String direcao = gatilho1.isCandleComprador() ? "COMPRA" : "VENDA";
211
                int idxRef = candles.indexOf(referencia);
212
                int idxG1  = candles.indexOf(gatilho1);
213
 
214
                String msg = String.format(
215
                        "[%d] Gatilho 1 (%s) identificado. Referência = candle [%d], G1 = candle [%d].",
216
                        idx, direcao, idxRef, idxG1
217
                );
218
                if (logs != null) logs.add(msg);
219
                log(msg);
220
            } else {
221
                String msg = "[" + idx + "] Sem referência/G1 formados ainda.";
222
                if (logs != null) logs.add(msg);
223
                log(msg);
224
            }
225
 
226
            return;
227
        }
228
 
229
        // --------------------------------------------------------------------
230
        // 2) Temos Ref + G1, mas ainda não temos G2
231
        // --------------------------------------------------------------------
232
        if (gatilho2 == null) {
233
 
234
            boolean cEhG2 = isGatilho2(referencia, gatilho1, atual);
235
 
236
            if (cEhG2) {
237
                // G2 deve ser posterior a G1 e candle diferente
238
                if (idx > idxG1) {
239
                    gatilho2 = atual;
240
                    idxG2    = idx;
241
 
242
                    if (padraoAtual != null) {
243
                        padraoAtual.setGatilho2(gatilho2);
244
                    }
245
 
246
                    String msg = String.format(
247
                            "[%d] Gatilho 2 identificado.",
248
                            idx
249
                    );
250
                    if (logs != null) logs.add(msg);
251
                    log(msg);
252
                } else {
253
                    // Se por alguma razão o método estiver sendo chamado de forma estranha
254
                    String msg = String.format(
255
                            "[%d] Candle candidato a G2, mas índice %d <= idxG1 %d. Ignorando para manter ordem cronológica.",
256
                            idx, idx, idxG1
257
                    );
258
                    if (logs != null) logs.add(msg);
259
                    log(msg);
260
                }
261
                return;
262
            }
263
 
264
            // Se NÃO é G2, verificamos se:
265
            // 1) ultrapassou a região da referência (topo/fundo)
266
            // 2) houve nova quebra de tendência sem atingir a região da referência
267
            boolean ultrapassaRef = ultrapassaRegiaoReferencia(referencia, atual);
268
            boolean novaQuebraSemRegiao = novaQuebraDeTendenciaSemAtingirRegiao(referencia, gatilho1, atual);
269
 
270
            if (ultrapassaRef || novaQuebraSemRegiao) {
271
                String motivo = ultrapassaRef
272
                        ? "ultrapassou a região da referência"
273
                        : "nova quebra de tendência sem atingir a região da referência";
274
 
275
                String msg = String.format(
276
                        "[%d] Candle %s. Procurando novo candle referência a partir deste candle.",
277
                        idx, motivo
278
                );
279
                if (logs != null) logs.add(msg);
280
                log(msg);
281
 
282
                // Reset e tentamos nova Ref+G1 com o candle atual
283
                reset();
284
                ParRefG1 novo = encontrarReferenciaEGatilho1(candles, idx);
285
                if (novo != null) {
286
                    referencia = novo.referencia;
287
                    gatilho1 = novo.g1;
288
                    padraoAtual = new PadraoGatilho();
289
                    padraoAtual.setReferencia(referencia);
290
                    padraoAtual.setGatilho1(gatilho1);
291
 
292
                    String direcao = gatilho1.isCandleComprador() ? "COMPRA" : "VENDA";
293
                    int idxRef = candles.indexOf(referencia);
294
                    int idxG1  = candles.indexOf(gatilho1);
295
 
296
                    String msg2 = String.format(
297
                            "[%d] Novo Gatilho 1 (%s) identificado após reset. Ref = [%d], G1 = [%d].",
298
                            idx, direcao, idxRef, idxG1
299
                    );
300
                    if (logs != null) logs.add(msg2);
301
                    log(msg2);
302
                }
303
                return;
304
            }
305
 
306
            // Nem é G2, nem invalida a referência → seguimos aguardando G2
307
            String msg = "[" + idx + "] Aguardando Gatilho 2.";
308
            if (logs != null) logs.add(msg);
309
            log(msg);
310
 
311
            return;
312
        }
313
 
314
        // --------------------------------------------------------------------
315
        // 3) Temos Ref + G1 + G2, mas ainda não G3
316
        // --------------------------------------------------------------------
317
        if (gatilho3 == null) {
318
 
319
            // Invalidação após G2:
320
            if (rompeReferenciaAposG2(referencia, atual)) {
321
                String msg = String.format(
322
                        "[%d] Rompimento da referência após o G2. Padrão invalidado. Resetando.",
323
                        idx
324
                );
325
                if (logs != null) logs.add(msg);
326
                log(msg);
327
                reset();
328
                return;
329
            }
330
 
331
            if (isGatilho3(referencia, gatilho1, gatilho2, atual)) {
332
 
333
                if (idx > idxG2) {
334
                    gatilho3 = atual;
335
                    idxG3    = idx;
336
                    candidatoG4Avaliado = false;
337
 
338
                    if (padraoAtual != null) {
339
                        padraoAtual.setGatilho3(gatilho3);
340
                    }
341
 
342
                    String msg = String.format(
343
                            "[%d] Gatilho 3 identificado.",
344
                            idx
345
                    );
346
                    if (logs != null) logs.add(msg);
347
                    log(msg);
348
 
349
                    if (padroes != null && padraoAtual != null && !padroes.contains(padraoAtual)) {
350
                        padroes.add(padraoAtual);
351
                    }
352
                } else {
353
                    String msg = String.format(
354
                            "[%d] Candle candidato a G3, mas índice %d <= idxG2 %d. Ignorando.",
355
                            idx, idx, idxG2
356
                    );
357
                    if (logs != null) logs.add(msg);
358
                    log(msg);
359
                }
360
 
361
                return;
362
            }
363
 
364
            return;
365
        }
366
 
367
        // --------------------------------------------------------------------
368
        // 4) Já temos Ref + G1 + G2 + G3 -> monitorar G4 (desarme) monitorar APENAS o primeiro candle pós G3 para possível G4
369
        // --------------------------------------------------------------------
370
        if (gatilho3 != null) {
371
            Candle g3 = gatilho3;
372
 
373
            // Se ainda não avaliamos o candidato a G4
374
            if (!candidatoG4Avaliado) {
375
 
376
                // 4.1) Descartar insiders: candles completamente dentro do range do G3
377
                if (isInsiderEmRelacaoAoG3(g3, atual)) {
378
                    String msg = String.format(
379
                            "[%d] Candle insider em relação ao G3. Aguardando próximo candle para possível G4.",
380
                            idx
381
                    );
382
                    if (logs != null) logs.add(msg);
383
                    log(msg);
384
                    return;
385
                }
386
 
387
                // 4.2) Este é o PRIMEIRO candle "útil" pós G3 (não insider)
388
                candidatoG4Avaliado = true;
389
 
390
                // G4 só pode ser depois de G3 e em candle diferente
391
                if (idx > idxG3 && isGatilho4(referencia, atual)) {
392
                    gatilho4 = atual;
393
                    idxG4    = idx;
394
 
395
                    if (padraoAtual != null) {
396
                        padraoAtual.setGatilho4(gatilho4);
397
                    }
398
 
399
                    String msg = String.format(
400
                            "[%d] Gatilho 4 identificado. Operação desarmada. Padrão encerrado.",
401
                            idx
402
                    );
403
                    if (logs != null) logs.add(msg);
404
                    log(msg);
405
 
406
                    reset();
407
                } else {
408
                    String msg = String.format(
409
                            "[%d] Primeiro candle pós G3 não configurou Gatilho 4. Padrão encerrado sem G4.",
410
                            idx
411
                    );
412
                    if (logs != null) logs.add(msg);
413
                    log(msg);
414
                    reset();
415
                }
416
 
417
                return;
418
            }
419
 
420
            // Se já avaliamos o candidato a G4, o padrão já foi encerrado (com ou sem G4),
421
            // então não deveríamos mais cair aqui em condições normais.
422
            return;
423
        }
424
 
425
 
426
    }
427
 
428
    /**
429
     * Um candle é considerado insider em relação ao G3 se estiver completamente
430
     * contido dentro da máxima e mínima do G3.
431
     */
432
    private boolean isInsiderEmRelacaoAoG3(Candle g3, Candle c) {
433
        return c.getMaxima() <= g3.getMaxima()
434
                && c.getMinima() >= g3.getMinima();
435
    }
436
 
437
 
438
    // ============================================================
439
    //  BLOCO: REFERÊNCIA + G1
440
    // ============================================================
441
 
442
    private static class ParRefG1 {
443
        Candle referencia;
444
        Candle g1;
445
        ParRefG1(Candle referencia, Candle g1) {
446
            this.referencia = referencia;
447
            this.g1 = g1;
448
        }
449
    }
450
 
451
    /**
452
     * Encontra par (Referência, G1) partindo de um possível candle referência,
453
     * analisando os candles posteriores.
454
     *
455
     * Regras:
456
     *
457
     * Candle Referência:
458
     *  - Candle que demarca topo/fundo e que será rompido, posteriormente,
459
     *    contrariamente à sua tendência pelo Gatilho 1.
460
     *
461
     * Exemplo:
462
     *  - Candle COMPRADOR (referência):
463
     *      Se ALGUM candle VENDEDOR subsequente romper o FUNDO dele:
464
     *          -> esse VENDEDOR é o Gatilho 1
465
     *          -> o COMPRADOR rompido é a Referência.
466
     *
467
     *  - Candle VENDEDOR (referência):
468
     *      Se ALGUM candle COMPRADOR subsequente romper o TOPO dele:
469
     *          -> esse COMPRADOR é o Gatilho 1
470
     *          -> o VENDEDOR rompido é a Referência.
471
     *
472
     * A função procura o PRIMEIRO G1 que respeita essas regras.
473
     */
474
    private ParRefG1 encontrarReferenciaEGatilho1(List<Candle> candles, int idxRef) {
475
        Candle ref = candles.get(idxRef);
476
 
477
        boolean refComprador = ref.isCandleComprador();
478
        boolean refVendedor = ref.isCandleVendedor();
479
 
480
        // Se a referência não tiver tendência clara, ignoramos (doji/indeciso)
481
        if (!refComprador && !refVendedor) {
482
            return null;
483
        }
484
 
485
        // Percorre os candles POSTERIORES ao possível candle referência
486
        for (int i = idxRef + 1; i < candles.size(); i++) {
487
            Candle c = candles.get(i);
488
 
489
            // também ignoramos candles sem tendência clara como candidatos a G1
490
            boolean cComprador = c.isCandleComprador();
491
            boolean cVendedor = c.isCandleVendedor();
492
            if (!cComprador && !cVendedor) {
493
                continue;
494
            }
495
 
496
            if (refComprador && cVendedor) {
497
                // Referência COMPRADORA -> G1 VENDEDOR deve romper o FUNDO da referência
498
                if (c.getMinima() < ref.getMinima()) {
499
                    // Achamos o primeiro G1 que rompeu o fundo da referência
500
                    return new ParRefG1(ref, c);
501
                }
502
 
503
            } else if (refVendedor && cComprador) {
504
                // Referência VENDEDORA -> G1 COMPRADOR deve romper o TOPO da referência
505
                if (c.getMaxima() > ref.getMaxima()) {
506
                    // Achamos o primeiro G1 que rompeu o topo da referência
507
                    return new ParRefG1(ref, c);
508
                }
509
            }
510
        }
511
 
512
        // Nenhum G1 encontrado para esse possível candle referência
513
        return null;
514
    }
515
 
516
 
517
    // ============================================================
518
    //  BLOCO: GATILHO 2
519
    // ============================================================
520
 
521
    /**
522
    * Gatilho 2:
523
    *  - Candle que RETORNA à região do candle referência (fechamento dentro do range total).
524
    *  - É o último candle antes da nova quebra de tendência.
525
    *  - EXCEÇÃO: se for da mesma tendência de G1 e "romper a tendência" de G1
526
    *    (novo topo na compra / novo fundo na venda), também é G2 mesmo sem tocar a região.
527
    */
528
    private boolean isGatilho2(Candle referencia, Candle g1, Candle c) {
529
 
530
        boolean cComprador = c.isCandleComprador();
531
        boolean cVendedor = c.isCandleVendedor();
532
 
533
        if (!cComprador && !cVendedor) {
534
            return false;
535
        }
536
 
537
        // Fechamento dentro da região total do candle referência
538
        boolean fechamentoDentroRegiao =
539
                c.getFechamento() >= referencia.getMinima() &&
540
                c.getFechamento() <= referencia.getMaxima();
541
 
542
        boolean condicaoPrincipal = fechamentoDentroRegiao;
543
 
544
        boolean g1Comprador = g1.isCandleComprador();
545
        boolean g1Vendedor = g1.isCandleVendedor();
546
 
547
        boolean mesmaTendencia =
548
                (g1Comprador && cComprador) ||
549
                (g1Vendedor && cVendedor);
550
 
551
        boolean rompeTendenciaG1 =
552
                (g1Comprador && c.getMaxima() > g1.getMaxima()) ||
553
                (g1Vendedor && c.getMinima() < g1.getMinima());
554
 
555
        boolean condicaoExcecao = mesmaTendencia && rompeTendenciaG1;
556
 
557
        return condicaoPrincipal || condicaoExcecao;
558
    }
559
 
560
    /**
561
     * Se ultrapassar a região do candle referência:
562
     *  - Referência compradora: rompe TOPO
563
     *  - Referência vendedora: rompe FUNDO
564
     * Neste caso devemos buscar um novo candle referência a partir deste candle.
565
     */
566
    private boolean ultrapassaRegiaoReferencia(Candle referencia, Candle c) {
567
        if (referencia == null) return false;
568
 
569
        boolean refComprador = referencia.isCandleComprador();
570
        boolean refVendedor = referencia.isCandleVendedor();
571
 
572
        if (refComprador) {
573
            return c.getMaxima() > referencia.getMaxima();
574
        } else if (refVendedor) {
575
            return c.getMinima() < referencia.getMinima();
576
        }
577
 
578
        return false;
579
    }
580
 
581
 
582
    /**
583
     * Nova quebra de tendência sem atingir a região da referência:
584
     *
585
     * Ideia:
586
     *  - Se G1 é comprador:
587
     *      -> candle atual é vendedor
588
     *      -> mínima do candle atual < mínima do G1 (quebra da perna de compra)
589
     *      -> fechamento NÃO está dentro da região da referência
590
     *
591
     *  - Se G1 é vendedor:
592
     *      -> candle atual é comprador
593
     *      -> máxima do candle atual > máxima do G1
594
     *      -> fechamento NÃO está dentro da região da referência
595
     */
596
    private boolean novaQuebraDeTendenciaSemAtingirRegiao(Candle referencia,
597
                                                          Candle g1,
598
                                                          Candle c) {
599
        if (referencia == null || g1 == null) return false;
600
 
601
        boolean fechamentoDentroRegiao =
602
                c.getFechamento() >= referencia.getMinima() &&
603
                c.getFechamento() <= referencia.getMaxima();
604
 
605
        if (fechamentoDentroRegiao) {
606
            return false; // se tocou a região, não é "sem atingir a região"
607
        }
608
 
609
        boolean g1Comprador = g1.isCandleComprador();
610
        boolean g1Vendedor = g1.isCandleVendedor();
611
 
612
        boolean cComprador = c.isCandleComprador();
613
        boolean cVendedor = c.isCandleVendedor();
614
 
615
        if (g1Comprador && cVendedor) {
616
            // quebra forte contra a compra
617
            return c.getMinima() < g1.getMinima();
618
        } else if (g1Vendedor && cComprador) {
619
            // quebra forte contra a venda
620
            return c.getMaxima() > g1.getMaxima();
621
        }
622
 
623
        return false;
624
    }
625
 
626
 
627
 
628
    // ============================================================
629
    //  BLOCO: GATILHO 3
630
    // ============================================================
631
 
632
    /**
633
     * G3:
634
     *  - Candle posterior ao G2.
635
     *  - Mesma tendência de G1.
636
     *  - Ref COMPRADORA:
637
     *      * topo de G3 < topo da referência
638
     *      * mínima de G3 < mínima de G2 (rompe fundo do G2)
639
     *  - Ref VENDEDORA:
640
     *      * topo de G3 > topo da referência
641
     *      * máxima de G3 > máxima de G2 (rompe topo do G2)
642
     *  (A invalidação por rompimento da referência é tratada em outro método.)
643
     */
644
    private boolean isGatilho3(Candle referencia, Candle g1, Candle g2, Candle g3) {
645
 
646
        boolean g1Comprador = g1.isCandleComprador();
647
        boolean g1Vendedor = g1.isCandleVendedor();
648
 
649
        boolean g3Comprador = g3.isCandleComprador();
650
        boolean g3Vendedor = g3.isCandleVendedor();
651
 
652
        boolean mesmaTendenciaG1 =
653
                (g1Comprador && g3Comprador) ||
654
                (g1Vendedor && g3Vendedor);
655
 
656
        if (!mesmaTendenciaG1) {
657
            return false;
658
        }
659
 
660
        boolean refComprador = referencia.isCandleComprador();
661
        boolean refVendedor = referencia.isCandleVendedor();
662
 
663
        if (refComprador) {
664
            boolean topoMaisBaixo = g3.getMaxima() < referencia.getMaxima();
665
            boolean rompeFundoG2 = g3.getMinima() < g2.getMinima();
666
            return topoMaisBaixo && rompeFundoG2;
667
        } else if (refVendedor) {
668
            boolean topoMaiorQueRef = g3.getMaxima() > referencia.getMaxima();
669
            boolean rompeTopoG2 = g3.getMaxima() > g2.getMaxima();
670
            return topoMaiorQueRef && rompeTopoG2;
671
        }
672
 
673
        return false;
674
    }
675
 
676
    /**
677
     * Invalidação após o G2:
678
     *  - Ref COMPRADORA: se qualquer candle após G2 romper o topo da referência -> invalida.
679
     *  - Ref VENDEDORA: se romper o fundo da referência -> invalida.
680
     */
681
    private boolean rompeReferenciaAposG2(Candle referencia, Candle c) {
682
        if (referencia == null) return false;
683
 
684
        boolean refComprador = referencia.isCandleComprador();
685
        boolean refVendedor = referencia.isCandleVendedor();
686
 
687
        if (refComprador) {
688
            return c.getMaxima() > referencia.getMaxima();
689
        } else if (refVendedor) {
690
            return c.getMinima() < referencia.getMinima();
691
        }
692
 
693
        return false;
694
    }
695
 
696
    // ============================================================
697
    //  BLOCO: GATILHO 4
698
    // ============================================================
699
 
700
    /**
701
     * G4:
702
     *  - Desarma a operação.
703
     *  - Ref COMPRADORA: topo da referência rompido pelo candle G4.
704
     *  - Ref VENDEDORA: fundo da referência rompido pelo candle G4.
705
     */
706
    private boolean isGatilho4(Candle referencia, Candle c) {
707
        if (referencia == null) return false;
708
 
709
        boolean refComprador = referencia.isCandleComprador();
710
        boolean refVendedor = referencia.isCandleVendedor();
711
 
712
        if (refComprador) {
713
            return c.getMaxima() > referencia.getMaxima();
714
        } else if (refVendedor) {
715
            return c.getMinima() < referencia.getMinima();
716
        }
717
 
718
        return false;
719
    }
720
 
721
 
722
    // ============================================================
723
    //  Getters (se quiser inspecionar estado atual externamente)
724
    // ============================================================
725
 
726
    public Candle getReferencia() {
727
        return referencia;
728
    }
729
 
730
    public Candle getGatilho1() {
731
        return gatilho1;
732
    }
733
 
734
    public Candle getGatilho2() {
735
        return gatilho2;
736
    }
737
 
738
    public Candle getGatilho3() {
739
        return gatilho3;
740
    }
741
 
742
    public Candle getGatilho4() {
743
        return gatilho4;
744
    }
745
 
746
    public PadraoGatilho getPadraoAtual() {
747
        return padraoAtual;
748
    }
749
}