Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
795 blopes 1
package br.com.kronus.core;
2
 
3
import java.math.BigDecimal;
4
import java.util.ArrayList;
5
import java.util.List;
6
 
7
import br.com.kronus.core.PadraoGatilho.TipoPadrao;
8
 
9
/**
10
 * Detector de padrões de gatilhos (GR, G1, G2, G3)
11
 * completamente integrado com enum TipoPadrao.
12
 */
13
public class DetectorGatilhos {
14
 
15
    private final boolean logAtivo;
16
    private int idxProximaAnaliseTempoReal = 0;
17
 
18
    /**
19
     * Buffer opcional para capturar logs em memória (modo debug).
20
     * Se for null, não acumula; se não for null, log() adiciona aqui também.
21
     */
22
    private List<String> bufferDebug;
23
 
24
    public DetectorGatilhos() {
25
        this(false);
26
    }
27
 
28
    public DetectorGatilhos(boolean logAtivo) {
29
        this.logAtivo = logAtivo;
30
    }
31
 
32
    private void log(String msg) {
33
        if (logAtivo) {
34
            System.out.println(msg);
35
        }
36
        if (bufferDebug != null) {
37
            bufferDebug.add(msg);
38
        }
39
    }
40
 
41
    // ================================================================
42
    // Estrutura interna de retorno
43
    // ================================================================
44
    private static class ResultadoPadrao {
45
        PadraoGatilho padrao;
46
        int lastIndex;
47
        int proximoInicio;
48
 
49
        ResultadoPadrao(PadraoGatilho padrao, int lastIndex, int proximoInicio) {
50
            this.padrao = padrao;
51
            this.lastIndex = lastIndex;
52
            this.proximoInicio = proximoInicio;
53
        }
54
    }
55
 
56
    // ================================================================
57
    // Criador de padrão parcial
58
    // ================================================================
59
    private ResultadoPadrao criarResultadoParcialComG2(Candle ref,
60
                                                       Candle g1,
61
                                                       Candle g2,
62
                                                       int lastIndex,
63
                                                       int idxGR) {
64
 
65
        PadraoGatilho padrao = new PadraoGatilho();
66
        padrao.setReferencia(ref);
67
        padrao.setGatilho1(g1);
68
        padrao.setGatilho2(g2);
69
        padrao.setGatilho3(null);
70
        padrao.setGatilho4(null);
71
        padrao.setTipoPadrao(TipoPadrao.PARCIAL_G2); // ENUM
72
 
73
        log(String.format("Padrão PARCIAL_G2: GR[%s], G1[%s], G2[%s]",
74
                ref.getDataHora(), g1.getDataHora(), g2.getDataHora()));
75
 
76
        return new ResultadoPadrao(padrao, lastIndex, idxGR + 1);
77
    }
78
 
79
    // ================================================================
80
    // HELPERS
81
    // ================================================================
82
    private boolean isInside(List<Candle> candles, int idx) {
83
        if (idx <= 0) return false;
84
 
85
        Candle atual = candles.get(idx);
86
        Candle anterior = candles.get(idx - 1);
87
 
88
        return BigDecimalUtils.ehMenorOuIgualQue(atual.getMaxima(), anterior.getMaxima())
89
            && BigDecimalUtils.ehMaiorOuIgualQue(atual.getMinima(), anterior.getMinima());
90
    }
91
 
92
    private boolean isDirecional(Candle c) {
93
        return c.isCandleComprador() || c.isCandleVendedor();
94
    }
95
 
96
    private boolean fechamentoDentroRegiaoGR(Candle c, Candle gr) {
97
        return BigDecimalUtils.ehMaiorOuIgualQue(c.getFechamento(), gr.getMinima())
98
            && BigDecimalUtils.ehMenorOuIgualQue(c.getFechamento(), gr.getMaxima());
99
    }
100
 
101
    private BigDecimal fibExtend(BigDecimal origem, BigDecimal destino, BigDecimal fator) {
102
        return origem.add(destino.subtract(origem).multiply(fator));
103
    }
104
 
105
    // ================================================================
106
    // API PRINCIPAL – BACKTEST
107
    // ================================================================
108
 public List<PadraoGatilho> identificarPadroes(List<Candle> candles) {
109
 
110
     List<PadraoGatilho> padroes = new ArrayList<>();
111
     int n = candles.size();
112
     if (n < 4) return padroes;
113
 
114
     log("===== INÍCIO BACKTEST (varrendo todos os candles como possível A) =====");
115
 
116
     for (int idxRef = 0; idxRef < n - 3; idxRef++) {
117
 
118
         log(String.format("---- Nova tentativa: idxRef = %d (candle #%d) ----", idxRef, idxRef + 1));
119
 
120
         ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxRef);
121
 
122
         if (resultado == null) {
123
             log(String.format("idxRef=%d: ResultadoPadrao null, seguindo para próximo candle.", idxRef));
124
             continue;
125
         }
126
 
127
         if (resultado.padrao != null) {
128
             PadraoGatilho p = resultado.padrao;
129
             p.setIdAtivo(p.getReferencia().getNomeAtivo());
130
             Candle gr = p.getReferencia();
131
             Candle g1 = p.getGatilho1();
132
             Candle g2 = p.getGatilho2();
133
             Candle g3 = p.getGatilho3();
134
 
135
             log(String.format(
136
                     ">> PADRÃO ENCONTRADO a partir de idxRef=%d: GR[%s], G1[%s], G2[%s], G3[%s], tipo=%s",
137
                     idxRef,
138
                     (gr != null ? gr.getDataHora() : "null"),
139
                     (g1 != null ? g1.getDataHora() : "null"),
140
                     (g2 != null ? g2.getDataHora() : "null"),
141
                     (g3 != null ? g3.getDataHora() : "null"),
142
                     p.getTipoPadrao()
143
             ));
144
 
145
             padroes.add(p);
146
         } else {
147
             log(String.format("idxRef=%d: nenhuma formação de padrão (sem GR/G1/G2/G3 válidos).", idxRef));
148
         }
149
     }
150
 
151
     log("===== FIM BACKTEST (varredura completa) =====");
152
     return padroes;
153
 }
154
 
155
 
156
    private ResultadoPadrao detectarPadraoAPartir(List<Candle> candles, int idxRef) {
157
 
158
        Candle ref = candles.get(idxRef);
159
 
160
        if (ref.isCandleComprador()) {
161
            return detectarPadraoComprador(candles, idxRef);
162
 
163
        } else if (ref.isCandleVendedor()) {
164
            return detectarPadraoVendedor(candles, idxRef);
165
 
166
        } else {
167
            log(String.format("Candle[%d] neutro; avançando.", idxRef + 1));
168
            return new ResultadoPadrao(null, idxRef, idxRef + 1);
169
        }
170
    }
171
 
172
         // ================================================================
173
         // DEBUG BACKTEST COMPLETO
174
         // ================================================================
175
         public List<String> debugarBacktestCompleto(List<Candle> candles) {
176
             List<String> relatorio = new ArrayList<>();
177
             List<String> antigo = this.bufferDebug;
178
 
179
             this.bufferDebug = relatorio;
180
             try {
181
                 log("####################################################");
182
                 log("DEBUG BACKTEST COMPLETO - iniciar identificarPadroes()");
183
                 identificarPadroes(candles);
184
                 log("DEBUG BACKTEST COMPLETO - fim identificarPadroes()");
185
                 log("####################################################");
186
             } finally {
187
                 this.bufferDebug = antigo;
188
             }
189
 
190
             return relatorio;
191
         }
192
 
193
 
194
    // ================================================================
195
    // PADRÃO COMPRADOR (VENDA)
196
    // ================================================================
197
    private ResultadoPadrao detectarPadraoComprador(List<Candle> candles, int idxA) {
198
 
199
        int n = candles.size();
200
        Candle candleA = candles.get(idxA);
201
 
202
        if (!candleA.isCandleComprador()) {
203
            return new ResultadoPadrao(null, idxA, idxA + 1);
204
        }
205
 
206
        int lastIndex = idxA;
207
 
208
        log("[VENDER] Iniciando em A[" + (idxA + 1) + "] " + candleA.getDataHora());
209
 
210
        // --------------------------------------------------------------
211
        // Busca candle B direcional (mudança de direção)
212
        // --------------------------------------------------------------
213
        int idxProxDirecional = -1;
214
 
215
        for (int i = idxA + 1; i < n; i++) {
216
 
217
            Candle c = candles.get(i);
218
 
219
            if (!isDirecional(c) || isInside(candles, i)) {
220
                log(String.format("[VENDER] Candle[%d] ignorado (não direcional ou inside)", i + 1));
221
                continue;
222
            }
223
 
224
            idxProxDirecional = i;
225
            break;
226
        }
227
 
228
        if (idxProxDirecional == -1) {
229
            log("[VENDER] Nenhum candle direcional após A; abortando padrão.");
230
            return new ResultadoPadrao(null, idxA, idxA + 1);
231
        }
232
 
233
        Candle prox = candles.get(idxProxDirecional);
234
 
235
        if (!prox.isCandleVendedor()) {
236
            log(String.format("[VENDER] Próximo direcional [%d] não é vendedor; A não vira referência.", idxProxDirecional + 1));
237
            return new ResultadoPadrao(null, idxA, idxA + 1);
238
        }
239
 
240
        // Candidato à referência
241
        Candle candidatoRef = candleA;
242
        int idxCandidatoRef = idxA;
243
        log(String.format("[VENDER] CandidatoRef inicial = idx %d", idxCandidatoRef + 1));
244
 
245
        Candle g1 = null;
246
        int idxG1 = -1;
247
 
248
        // --------------------------------------------------------------
249
        // Busca GR dinâmico e G1
250
        // --------------------------------------------------------------
251
        for (int i = idxA + 1; i < n; i++) {
252
 
253
            Candle c = candles.get(i);
254
 
255
            if (!isDirecional(c) || isInside(candles, i)) {
256
                log(String.format("[VENDER] Candle[%d] ignorado (não direcional ou inside) antes do G1", i + 1));
257
                continue;
258
            }
259
 
260
            lastIndex = i;
261
 
262
            // G1
263
            if (c.isCandleVendedor()
264
                && BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) {
265
 
266
                g1 = c;
267
                idxG1 = i;
268
                log(String.format("[VENDER] G1 encontrado em [%d], rompendo fundo de candRef[%d]",
269
                        idxG1 + 1, idxCandidatoRef + 1));
270
                break;
271
            }
272
 
273
            // Atualização do GR dinâmico
274
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) {
275
                candidatoRef = c;
276
                idxCandidatoRef = i;
277
                log(String.format("[VENDER] CandidatoRef atualizado dinamicamente para idx %d", idxCandidatoRef + 1));
278
            }
279
        }
280
 
281
        if (g1 == null) {
282
            log("[VENDER] Não formou G1; recomeçando após A.");
283
            return new ResultadoPadrao(null, lastIndex, idxA + 1);
284
        }
285
 
286
        Candle gr = candidatoRef;
287
        int idxGR = idxCandidatoRef;
288
 
289
        log(String.format("[VENDER] GR definido em [%d], G1 em [%d]", idxGR + 1, idxG1 + 1));
290
 
291
        // --------------------------------------------------------------
292
        // Fibonacci 200% (origem = máxG1, destino = mínG1)
293
        // --------------------------------------------------------------
294
        BigDecimal fib200 = fibExtend(g1.getMaxima(), g1.getMinima(), new BigDecimal("2"));
295
        log(String.format("[VENDER] Fibo200 G1[%d] = %s", idxG1 + 1, fib200.toPlainString()));
296
 
297
        // --------------------------------------------------------------
298
        // Busca G2 e G3
299
        // --------------------------------------------------------------
300
        Candle g2 = null;
301
        int idxG2 = -1;
302
        Candle g3 = null;
303
        int idxG3 = -1;
304
 
305
        for (int i = idxG1 + 1; i < n; i++) {
306
 
307
            Candle c = candles.get(i);
308
 
309
            if (!isDirecional(c) || isInside(candles, i)) {
310
                log(String.format("[VENDER] Candle[%d] ignorado (não direcional ou inside) após G1", i + 1));
311
                continue;
312
            }
313
 
314
            lastIndex = i;
315
 
316
            // DESCARTES:
317
 
318
            // Rompe topo do GR
319
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) {
320
                log(String.format("[VENDER] GR[%d]: candle[%d] rompeu topo do GR. Descartando padrão.",
321
                        idxGR + 1, i + 1));
322
                return new ResultadoPadrao(null, i, idxGR + 1);
323
            }
324
 
325
            // Rompe nível Fib 200%
326
            if (BigDecimalUtils.ehMenorOuIgualQue(c.getMinima(), fib200)) {
327
                log(String.format("[VENDER] GR[%d], G1[%d]: candle[%d] atingiu 200%% da fibo G1. Descartando padrão.",
328
                        idxGR + 1, idxG1 + 1, i + 1));
329
                return new ResultadoPadrao(null, i, idxGR + 1);
330
            }
331
 
332
            // ==========================
333
            // G2
334
            // ==========================
335
            if (c.isCandleComprador()
336
                && fechamentoDentroRegiaoGR(c, gr)) {
337
 
338
                if (g2 == null) {
339
                    g2 = c;
340
                    idxG2 = i;
341
                    log(String.format("[VENDER] GR[%d], G1[%d]: candidato G2 em [%d]",
342
                            idxGR + 1, idxG1 + 1, idxG2 + 1));
343
 
344
                } else if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima())) {
345
                    g2 = c;
346
                    idxG2 = i;
347
                    log(String.format("[VENDER] GR[%d], G1[%d]: G2 atualizado em [%d]",
348
                            idxGR + 1, idxG1 + 1, idxG2 + 1));
349
                }
350
 
351
                continue;
352
            }
353
 
354
            // ==========================
355
            // G3
356
            // ==========================
357
            if (g2 != null && c.isCandleVendedor()) {
358
 
359
                boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima());
360
                boolean topoMenorOuIgualGR = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), gr.getMaxima());
361
 
362
                if (rompeFundoG2 && topoMenorOuIgualGR) {
363
                    g3 = c;
364
                    idxG3 = i;
365
                    log(String.format("[VENDER] GR[%d], G1[%d], G2[%d]: G3 em [%d] (padrão confirmado)",
366
                            idxGR + 1, idxG1 + 1, idxG2 + 1, idxG3 + 1));
367
                    break;
368
                }
369
            }
370
        }
371
 
372
        // Nenhum G3 → padrão parcial
373
        if (g3 == null) {
374
 
375
            if (g2 != null) {
376
                log(String.format("[VENDER] Padrão parcial (GR[%d], G1[%d], G2[%d]) sem G3.",
377
                        idxGR + 1, idxG1 + 1, idxG2 + 1));
378
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex, idxGR);
379
            }
380
 
381
            log(String.format("[VENDER] GR[%d], G1[%d] sem G2/G3. Recomeçando após GR.",
382
                    idxGR + 1, idxG1 + 1));
383
            return new ResultadoPadrao(null, lastIndex, idxGR + 1);
384
        }
385
 
386
        // --------------------------------------------------------------
387
        // PADRÃO COMPLETO (COM G3)
388
        // --------------------------------------------------------------
389
        PadraoGatilho padrao = new PadraoGatilho();
390
        padrao.setReferencia(gr);
391
        padrao.setGatilho1(g1);
392
        padrao.setGatilho2(g2);
393
        padrao.setGatilho3(g3);
394
        padrao.setGatilho4(null);
395
        padrao.setTipoPadrao(TipoPadrao.COMPLETO_G3);
396
 
397
        log(String.format("[VENDER] Padrão COMPLETO_G3: GR[%d], G1[%d], G2[%d], G3[%d]",
398
                idxGR + 1, idxG1 + 1, idxG2 + 1, idxG3 + 1));
399
 
400
        return new ResultadoPadrao(padrao, idxG3, idxGR + 1);
401
    }
402
 
403
    // ================================================================
404
    // PADRÃO VENDEDOR (COMPRA)
405
    // ================================================================
406
    private ResultadoPadrao detectarPadraoVendedor(List<Candle> candles, int idxA) {
407
 
408
        int n = candles.size();
409
        Candle candleA = candles.get(idxA);
410
 
411
        if (!candleA.isCandleVendedor())
412
            return new ResultadoPadrao(null, idxA, idxA + 1);
413
 
414
        int lastIndex = idxA;
415
 
416
        log("[COMPRAR] Iniciando em A[" + (idxA + 1) + "] " + candleA.getDataHora());
417
 
418
        // --------------------------------------------------------------
419
        // Busca B direcional
420
        // --------------------------------------------------------------
421
        int idxProxDirecional = -1;
422
 
423
        for (int i = idxA + 1; i < n; i++) {
424
 
425
            Candle c = candles.get(i);
426
 
427
            if (!isDirecional(c) || isInside(candles, i)) {
428
                log(String.format("[COMPRAR] Candle[%d] ignorado (não direcional ou inside)", i + 1));
429
                continue;
430
            }
431
 
432
            idxProxDirecional = i;
433
            break;
434
        }
435
 
436
        if (idxProxDirecional == -1) {
437
            log("[COMPRAR] Nenhum candle direcional após A; abortando padrão.");
438
            return new ResultadoPadrao(null, idxA, idxA + 1);
439
        }
440
 
441
        Candle prox = candles.get(idxProxDirecional);
442
 
443
        if (!prox.isCandleComprador()) {
444
            log(String.format("[COMPRAR] Próximo direcional [%d] não é comprador; A não vira referência.",
445
                    idxProxDirecional + 1));
446
            return new ResultadoPadrao(null, idxA, idxA + 1);
447
        }
448
 
449
        // GR dinâmico
450
        Candle candidatoRef = candleA;
451
        int idxCandidatoRef = idxA;
452
 
453
        Candle g1 = null;
454
        int idxG1 = -1;
455
 
456
        log(String.format("[COMPRAR] CandidatoRef inicial = idx %d", idxCandidatoRef + 1));
457
 
458
        // --------------------------------------------------------------
459
        // Busca G1 e GR
460
        // --------------------------------------------------------------
461
        for (int i = idxA + 1; i < n; i++) {
462
 
463
            Candle c = candles.get(i);
464
            if (!isDirecional(c) || isInside(candles, i)) {
465
                log(String.format("[COMPRAR] Candle[%d] ignorado (não direcional ou inside) antes do G1", i + 1));
466
                continue;
467
            }
468
 
469
            lastIndex = i;
470
 
471
            if (c.isCandleComprador()
472
                && BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) {
473
 
474
                g1 = c;
475
                idxG1 = i;
476
                log(String.format("[COMPRAR] G1 encontrado em [%d], rompendo topo de candRef[%d]",
477
                        idxG1 + 1, idxCandidatoRef + 1));
478
                break;
479
            }
480
 
481
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) {
482
                candidatoRef = c;
483
                idxCandidatoRef = i;
484
                log(String.format("[COMPRAR] CandidatoRef atualizado dinamicamente para idx %d", idxCandidatoRef + 1));
485
            }
486
        }
487
 
488
        if (g1 == null) {
489
            log("[COMPRAR] Não formou G1; recomeçando após A.");
490
            return new ResultadoPadrao(null, lastIndex, idxA + 1);
491
        }
492
 
493
        Candle gr = candidatoRef;
494
        int idxGR = idxCandidatoRef;
495
 
496
        log(String.format("[COMPRAR] GR definido em [%d], G1 em [%d]", idxGR + 1, idxG1 + 1));
497
 
498
        // --------------------------------------------------------------
499
        // Fibonacci -100% (aqui usando fator 2 entre mín e máx)
500
        // --------------------------------------------------------------
501
        BigDecimal fib200MinMax = fibExtend(g1.getMinima(), g1.getMaxima(), new BigDecimal("2"));
502
        log(String.format("[COMPRAR] Fibo200 G1[%d] = %s", idxG1 + 1, fib200MinMax.toPlainString()));
503
 
504
        // --------------------------------------------------------------
505
        // Busca G2 e G3
506
        // --------------------------------------------------------------
507
        Candle g2 = null;
508
        int idxG2 = -1;
509
        Candle g3 = null;
510
        int idxG3 = -1;
511
 
512
        for (int i = idxG1 + 1; i < n; i++) {
513
 
514
            Candle c = candles.get(i);
515
            if (!isDirecional(c) || isInside(candles, i)) {
516
                log(String.format("[COMPRAR] Candle[%d] ignorado (não direcional ou inside) após G1", i + 1));
517
                continue;
518
            }
519
 
520
            lastIndex = i;
521
 
522
            // DESCARTES
523
 
524
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) {
525
                log(String.format("[COMPRAR] GR[%d]: candle[%d] rompeu fundo do GR. Descartando padrão.",
526
                        idxGR + 1, i + 1));
527
                return new ResultadoPadrao(null, i, idxGR + 1);
528
            }
529
 
530
            if (BigDecimalUtils.ehMaiorQue(c.getMinima(), fib200MinMax)) {
531
                log(String.format("[COMPRAR] GR[%d], G1[%d]: candle[%d] atingiu 200%% da fibo G1. Descartando padrão.",
532
                        idxGR + 1, idxG1 + 1, i + 1));
533
                return new ResultadoPadrao(null, i, idxGR + 1);
534
            }
535
 
536
            // ==========================
537
            // G2
538
            // ==========================
539
            if (c.isCandleVendedor() && fechamentoDentroRegiaoGR(c, gr)) {
540
 
541
                if (g2 == null) {
542
                    g2 = c;
543
                    idxG2 = i;
544
                    log(String.format("[COMPRAR] GR[%d], G1[%d]: candidato G2 em [%d]",
545
                            idxGR + 1, idxG1 + 1, idxG2 + 1));
546
 
547
                } else if (BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima())) {
548
                    g2 = c;
549
                    idxG2 = i;
550
                    log(String.format("[COMPRAR] GR[%d], G1[%d]: G2 atualizado em [%d]",
551
                            idxGR + 1, idxG1 + 1, idxG2 + 1));
552
                }
553
 
554
                continue;
555
            }
556
 
557
            // ==========================
558
            // G3
559
            // ==========================
560
            if (g2 != null && c.isCandleComprador()) {
561
 
562
                boolean rompeTopo = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima());
563
                boolean fundoMaiorOuIgualGR = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), gr.getMinima());
564
 
565
                if (rompeTopo && fundoMaiorOuIgualGR) {
566
                    g3 = c;
567
                    idxG3 = i;
568
                    log(String.format("[COMPRAR] GR[%d], G1[%d], G2[%d]: G3 em [%d] (padrão confirmado)",
569
                            idxGR + 1, idxG1 + 1, idxG2 + 1, idxG3 + 1));
570
                    break;
571
                }
572
            }
573
        }
574
 
575
        // ====================================
576
        // Nenhum G3 → padrão parcial
577
        // ====================================
578
        if (g3 == null) {
579
 
580
            if (g2 != null) {
581
                log(String.format("[COMPRAR] Padrão parcial (GR[%d], G1[%d], G2[%d]) sem G3.",
582
                        idxGR + 1, idxG1 + 1, idxG2 + 1));
583
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex, idxGR);
584
            }
585
 
586
            log(String.format("[COMPRAR] GR[%d], G1[%d] sem G2/G3. Recomeçando após GR.",
587
                    idxGR + 1, idxG1 + 1));
588
            return new ResultadoPadrao(null, lastIndex, idxGR + 1);
589
        }
590
 
591
        // ====================================
592
        // PADRÃO COMPLETO (COM G3)
593
        // ====================================
594
        PadraoGatilho padrao = new PadraoGatilho();
595
        padrao.setReferencia(gr);
596
        padrao.setGatilho1(g1);
597
        padrao.setGatilho2(g2);
598
        padrao.setGatilho3(g3);
599
        padrao.setGatilho4(null);
600
        padrao.setTipoPadrao(TipoPadrao.COMPLETO_G3);
601
 
602
        log(String.format("[COMPRAR] Padrão COMPLETO_G3: GR[%d], G1[%d], G2[%d], G3[%d]",
603
                idxGR + 1, idxG1 + 1, idxG2 + 1, idxG3 + 1));
604
 
605
        return new ResultadoPadrao(padrao, idxG3, idxGR + 1);
606
    }
607
 
608
    // ================================================================
609
    // TEMPO REAL
610
    // ================================================================
611
    public void resetTempoReal() {
612
        idxProximaAnaliseTempoReal = 0;
613
    }
614
 
615
    public PadraoGatilho processarCandleTempoReal(List<Candle> candles) {
616
 
617
        int n = candles.size();
618
        if (n < 4) return null;
619
 
620
        while (idxProximaAnaliseTempoReal < n - 3) {
621
 
622
            ResultadoPadrao r = detectarPadraoAPartir(candles, idxProximaAnaliseTempoReal);
623
 
624
            if (r == null) {
625
                idxProximaAnaliseTempoReal++;
626
                continue;
627
            }
628
 
629
            int next = Math.max(r.proximoInicio, idxProximaAnaliseTempoReal + 1);
630
            idxProximaAnaliseTempoReal = next;
631
 
632
            if (r.padrao != null)
633
                return r.padrao;
634
        }
635
 
636
        return null;
637
    }
638
 
639
    // ================================================================
640
    // DEBUG - para usar em JSF
641
    // ================================================================
642
    /**
643
     * Roda a lógica de detecção a partir de um índice específico e devolve
644
     * um "relatório" em forma de lista de strings com tudo que aconteceu.
645
     *
646
     * NÃO altera idxProximaAnaliseTempoReal.
647
     */
648
    public List<String> debugarAPartirDoIndice(List<Candle> candles, int idxInicio) {
649
 
650
        List<String> relatorio = new ArrayList<>();
651
 
652
        List<String> antigo = bufferDebug;
653
        bufferDebug = relatorio;
654
 
655
        try {
656
            log("============= DEBUG =============");
657
            log("Iniciando análise no índice " + idxInicio);
658
            detectarPadraoAPartir(candles, idxInicio);
659
            log("Fim da análise");
660
            log("================================");
661
        }
662
        finally {
663
            bufferDebug = antigo;
664
        }
665
 
666
        return relatorio;
667
    }
668
 
669
}