Subversion Repositories Integrator Subversion

Rev

Rev 767 | Rev 773 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
760 blopes 1
package br.com.kronus.core;
2
 
3
import java.util.ArrayList;
764 blopes 4
import java.util.List;
760 blopes 5
 
764 blopes 6
import br.com.sl.domain.model.Candle;
7
import br.com.sl.domain.util.BigDecimalUtils;
8
 
760 blopes 9
/**
764 blopes 10
 * Detector de padrões de gatilhos (GR, G1, G2, G3, G4)
11
 * trabalhando tanto em modo backtest quanto em modo tempo real.
760 blopes 12
 *
771 blopes 13
 * Regras implementadas (texto alinhado):
760 blopes 14
 *
771 blopes 15
 * COMPRADOR
16
 * ---------
17
 * 1) Candle A comprador, candidato à referência.
18
 *    - Após A deve haver mudança de tendência (B).
19
 *    - A deve ser o último da tendência compradora:
20
 *      o primeiro candle direcional após A (ignorando inside/neutros)
21
 *      deve ser vendedor (B). Se for comprador, descarta A.
760 blopes 22
 *
771 blopes 23
 * 2) Ajuste da referência com B:
24
 *    - Se B (vendedor) tiver TOPO maior que o topo de A,
25
 *      B passa a ser o candidato à referência.
26
 *      Senão, A permanece como candidato.
760 blopes 27
 *
771 blopes 28
 * 3) G1:
29
 *    - A partir do candidato à referência, o primeiro candle vendedor
30
 *      subsequente que romper o FUNDO do candidato (mínima < mínima do candidato)
31
 *      será o G1. O candidato torna-se o GR.
760 blopes 32
 *
771 blopes 33
 * 4) G2:
34
 *    - Após o G1, assim que houver nova mudança de tendência para compradora:
35
 *      * encontrar o primeiro comprador após G1 (ignorando inside/neutros);
36
 *      * seguir a sequência compradora até aparecer vendedor.
37
 *      * durante essa sequência:
38
 *          - se houver OUTSIDE (atual rompe topo e fundo do anterior) => descarta.
39
 *          - se ALGUM candle romper o TOPO do GR (máxima > máxima GR) => descarta.
40
 *      * o ÚLTIMO candle dessa tendência compradora deve ter o fechamento
41
 *        dentro da região total do GR (mínimaGR <= fechamento <= máximaGR).
42
 *      * se virar vendedor sem atingir a região do GR => descarta.
43
 *      * caso válido, esse último comprador é o G2.
760 blopes 44
 *
771 blopes 45
 * 5) G3:
46
 *    - Após o G2, o PRÓXIMO candle direcional deve ser vendedor.
47
 *      se for comprador => padrão apenas até G2 (parcial).
48
 *    - numa sequência vendedora:
49
 *      * se houver OUTSIDE => descarta.
50
 *      * se algum candle vendedor romper o fundo de G2 (mínima < mínima G2)
51
 *        e tiver topo <= topo do GR => G3.
52
 *      * se surgir candle que rompa o TOPO do GR (máxima > máximaGR) ANTES do G3,
53
 *        o padrão é descartado.
764 blopes 54
 *
771 blopes 55
 * 6) G4:
56
 *    - Após o G3, se o PRÓXIMO candle romper o topo do GR (máxima > máximaGR),
57
 *      será o G4. Depois disso, o padrão é encerrado e um novo padrão é buscado.
764 blopes 58
 *
771 blopes 59
 * VENDEDOR
60
 * --------
61
 * 1) Candle A vendedor, candidato à referência.
62
 *    - Após A deve haver mudança de tendência (B).
63
 *    - A deve ser o último da tendência vendedora:
64
 *      o primeiro candle direcional após A (ignorando inside/neutros)
65
 *      deve ser comprador (B). Se for vendedor, descarta A.
764 blopes 66
 *
771 blopes 67
 * 2) Ajuste da referência com B:
68
 *    - Se B (comprador) tiver FUNDO mais baixo (mínima menor) que o fundo de A,
69
 *      B passa a ser candidato à referência.
70
 *      Senão, A permanece como candidato.
764 blopes 71
 *
771 blopes 72
 * 3) G1:
73
 *    - A partir do candidato à referência, o primeiro candle comprador
74
 *      subsequente que romper o TOPO do candidato (máxima > máxima do candidato)
75
 *      será o G1. O candidato torna-se o GR.
764 blopes 76
 *
771 blopes 77
 * 4) G2:
78
 *    - Após o G1, nova mudança para tendência vendedora:
79
 *      * encontrar o primeiro vendedor após G1 (ignorando inside/neutros);
80
 *      * seguir a sequência vendedora até aparecer comprador.
81
 *      * durante essa sequência:
82
 *          - se houver OUTSIDE => descarta.
83
 *          - se algum candle romper o FUNDO do GR (mínima < mínimaGR) => descarta.
84
 *      * o ÚLTIMO candle dessa tendência vendedora deve ter o fechamento
85
 *        dentro da região total do GR (mínimaGR <= fechamento <= máximaGR).
86
 *      * se virar comprador sem atingir a região do GR => descarta.
87
 *      * caso válido, esse último vendedor é o G2.
88
 *
89
 * 5) G3:
90
 *    - Após o G2, o PRÓXIMO candle direcional deve ser comprador.
91
 *      se for vendedor => padrão apenas até G2 (parcial).
92
 *    - numa sequência compradora:
93
 *      * se houver OUTSIDE => descarta.
94
 *      * se algum candle comprador romper o topo de G2 (máxima > máxima G2)
95
 *        e tiver fundo >= fundo do GR => G3.
96
 *      * se algum candle romper o FUNDO do GR (mínima < mínimaGR)
97
 *        ANTES do G3 => descarta.
98
 *
99
 * 6) G4:
100
 *    - Após o G3, se o PRÓXIMO candle romper o fundo do GR (mínima < mínimaGR),
101
 *      será o G4. Depois disso, o padrão é encerrado e um novo padrão é buscado.
102
 *
103
 * OUTSIDE (ambos os lados):
104
 *    - Candle cuja máxima > máxima do candle anterior E mínima < mínima do candle anterior.
105
 *    - Se houver um outside antes de identificar o G3, o padrão é descartado.
760 blopes 106
 */
107
public class DetectorGatilhos {
108
 
764 blopes 109
    private final boolean logAtivo;
110
    private int idxProximaAnaliseTempoReal = 0;
760 blopes 111
 
764 blopes 112
    public DetectorGatilhos() {
113
        this(false);
114
    }
760 blopes 115
 
764 blopes 116
    public DetectorGatilhos(boolean logAtivo) {
117
        this.logAtivo = logAtivo;
118
    }
760 blopes 119
 
764 blopes 120
    private void log(String msg) {
121
        if (logAtivo) {
122
            System.out.println(msg);
123
        }
124
    }
760 blopes 125
 
764 blopes 126
    private static class ResultadoPadrao {
771 blopes 127
        PadraoGatilho padrao;
128
        int lastIndex;
764 blopes 129
        ResultadoPadrao(PadraoGatilho padrao, int lastIndex) {
130
            this.padrao = padrao;
131
            this.lastIndex = lastIndex;
132
        }
133
    }
760 blopes 134
 
764 blopes 135
    private ResultadoPadrao criarResultadoParcialComG2(Candle ref,
136
                                                       Candle g1,
137
                                                       Candle g2,
138
                                                       int lastIndex) {
139
        PadraoGatilho padrao = new PadraoGatilho();
140
        padrao.setReferencia(ref);
141
        padrao.setGatilho1(g1);
142
        padrao.setGatilho2(g2);
143
        padrao.setGatilho3(null);
144
        padrao.setGatilho4(null);
145
        return new ResultadoPadrao(padrao, lastIndex);
146
    }
760 blopes 147
 
764 blopes 148
    // =====================================================================
771 blopes 149
    // API PRINCIPAL – BACKTEST
764 blopes 150
    // =====================================================================
760 blopes 151
 
764 blopes 152
    public List<PadraoGatilho> identificarPadroes(List<Candle> candles) {
153
        List<PadraoGatilho> padroes = new ArrayList<>();
154
        int n = candles.size();
771 blopes 155
        if (n < 4) return padroes;
760 blopes 156
 
764 blopes 157
        int idxRef = 0;
158
        while (idxRef < n - 3) {
159
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxRef);
760 blopes 160
 
764 blopes 161
            if (resultado == null) {
162
                idxRef++;
163
                continue;
164
            }
760 blopes 165
 
764 blopes 166
            if (resultado.padrao != null) {
167
                padroes.add(resultado.padrao);
168
            }
760 blopes 169
 
764 blopes 170
            idxRef = Math.max(resultado.lastIndex + 1, idxRef + 1);
171
        }
760 blopes 172
 
764 blopes 173
        return padroes;
174
    }
760 blopes 175
 
764 blopes 176
    private ResultadoPadrao detectarPadraoAPartir(List<Candle> candles, int idxRef) {
177
        Candle ref = candles.get(idxRef);
178
        if (ref.isCandleComprador()) {
179
            return detectarPadraoComprador(candles, idxRef);
180
        } else if (ref.isCandleVendedor()) {
181
            return detectarPadraoVendedor(candles, idxRef);
182
        } else {
183
            return new ResultadoPadrao(null, idxRef);
184
        }
185
    }
760 blopes 186
 
764 blopes 187
    // =====================================================================
771 blopes 188
    // HELPERS
764 blopes 189
    // =====================================================================
760 blopes 190
 
771 blopes 191
    private boolean isInside(List<Candle> candles, int idx) {
192
        if (idx <= 0) return false;
193
        Candle atual = candles.get(idx);
194
        Candle anterior = candles.get(idx - 1);
195
        return BigDecimalUtils.ehMenorOuIgualQue(atual.getMaxima(), anterior.getMaxima())
196
                && BigDecimalUtils.ehMaiorOuIgualQue(atual.getMinima(), anterior.getMinima());
197
    }
760 blopes 198
 
771 blopes 199
    private boolean isOutside(List<Candle> candles, int idx) {
200
        if (idx <= 0) return false;
201
        Candle atual = candles.get(idx);
202
        Candle anterior = candles.get(idx - 1);
203
        return BigDecimalUtils.ehMaiorQue(atual.getMaxima(), anterior.getMaxima())
204
                && BigDecimalUtils.ehMenorQue(atual.getMinima(), anterior.getMinima());
205
    }
760 blopes 206
 
771 blopes 207
    // =====================================================================
208
    // CASO 1 – PADRÃO A PARTIR DE CANDLE COMPRADOR
209
    // =====================================================================
210
 
211
    private ResultadoPadrao detectarPadraoComprador(List<Candle> candles, int idxA) {
212
        int n = candles.size();
213
        int lastIndex = idxA;
214
        Candle candleA = candles.get(idxA);
215
        if (!candleA.isCandleComprador()) {
216
            return new ResultadoPadrao(null, idxA);
764 blopes 217
        }
760 blopes 218
 
771 blopes 219
        // 1) A deve ser último da tendência compradora -> achar B vendedor
220
        int idxB = -1;
221
        for (int i = idxA + 1; i < n; i++) {
764 blopes 222
            Candle c = candles.get(i);
771 blopes 223
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor())) {
764 blopes 224
                continue;
225
            }
771 blopes 226
            if (c.isCandleComprador()) {
227
                // A não é o último comprador
228
                return new ResultadoPadrao(null, idxA);
764 blopes 229
            }
771 blopes 230
            if (c.isCandleVendedor()) {
231
                idxB = i;
232
                break;
233
            }
764 blopes 234
        }
771 blopes 235
        if (idxB == -1) return new ResultadoPadrao(null, idxA);
760 blopes 236
 
771 blopes 237
        Candle candleB = candles.get(idxB);
760 blopes 238
 
771 blopes 239
        // 2) Candidato à referência: A ou B (conforme topo)
240
        Candle candidatoRef = candleA;
241
        int idxCandidatoRef = idxA;
242
        if (BigDecimalUtils.ehMaiorQue(candleB.getMaxima(), candleA.getMaxima())) {
243
            candidatoRef = candleB;
244
            idxCandidatoRef = idxB;
764 blopes 245
        }
760 blopes 246
 
771 blopes 247
        // 3) G1 – primeiro vendedor que rompe fundo do candidatoRef
248
        Candle g1 = null;
764 blopes 249
        int idxG1 = -1;
771 blopes 250
        for (int i = idxCandidatoRef + 1; i < n; i++) {
764 blopes 251
            Candle c = candles.get(i);
771 blopes 252
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor())) {
253
                continue;
764 blopes 254
            }
760 blopes 255
 
764 blopes 256
            if (!c.isCandleVendedor()) {
257
                lastIndex = i - 1;
258
                return new ResultadoPadrao(null, lastIndex);
259
            }
760 blopes 260
 
764 blopes 261
            lastIndex = i;
771 blopes 262
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) {
263
                g1 = c;
764 blopes 264
                idxG1 = i;
265
                break;
266
            }
267
        }
760 blopes 268
 
771 blopes 269
        if (g1 == null) return new ResultadoPadrao(null, lastIndex);
760 blopes 270
 
771 blopes 271
        Candle gr = candidatoRef;
272
        int idxGR = idxCandidatoRef;
273
        log(String.format("GR comprador em [%d], G1 (vendedor) em [%d]", idxGR, idxG1));
760 blopes 274
 
771 blopes 275
        // 4) G2 – nova tendência compradora com fechamento dentro da região do GR
276
        Candle g2 = null;
277
        int idxG2 = -1;
278
 
764 blopes 279
        int idxPrimeiroComprador = -1;
280
        for (int i = idxG1 + 1; i < n; i++) {
281
            Candle c = candles.get(i);
771 blopes 282
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
283
                continue;
760 blopes 284
 
764 blopes 285
            if (c.isCandleComprador()) {
286
                idxPrimeiroComprador = i;
287
                break;
288
            } else {
771 blopes 289
                // ainda vendedor
764 blopes 290
                lastIndex = i;
291
            }
292
        }
760 blopes 293
 
771 blopes 294
        if (idxPrimeiroComprador == -1) return new ResultadoPadrao(null, lastIndex);
760 blopes 295
 
771 blopes 296
        Candle ultimoComprador = null;
297
        int idxUltimoComprador = -1;
760 blopes 298
 
764 blopes 299
        for (int i = idxPrimeiroComprador; i < n; i++) {
300
            Candle c = candles.get(i);
760 blopes 301
 
771 blopes 302
            // outside antes de G3 descarta
303
            if (isOutside(candles, i)) {
767 blopes 304
                lastIndex = i;
771 blopes 305
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (comprador).", idxGR, i));
767 blopes 306
                return new ResultadoPadrao(null, lastIndex);
307
            }
308
 
771 blopes 309
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
310
                continue;
760 blopes 311
 
771 blopes 312
            if (c.isCandleComprador()) {
313
                // se romper topo do GR, descarta
314
                if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) {
315
                    lastIndex = i;
316
                    log(String.format("GR[%d]: candle [%d] rompeu topo do GR durante G2.", idxGR, i));
317
                    return new ResultadoPadrao(null, lastIndex);
318
                }
319
                ultimoComprador = c;
320
                idxUltimoComprador = i;
321
                lastIndex = i;
322
            } else if (c.isCandleVendedor()) {
323
                // terminou tendência compradora
324
                lastIndex = i - 1;
764 blopes 325
                break;
326
            }
327
        }
760 blopes 328
 
771 blopes 329
        if (ultimoComprador == null) return new ResultadoPadrao(null, lastIndex);
760 blopes 330
 
771 blopes 331
        boolean fechamentoDentroRegiaoGR =
332
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoComprador.getFechamento(), gr.getMinima()) &&
333
                BigDecimalUtils.ehMenorOuIgualQue(ultimoComprador.getFechamento(), gr.getMaxima());
760 blopes 334
 
771 blopes 335
        if (!fechamentoDentroRegiaoGR) {
764 blopes 336
            return new ResultadoPadrao(null, lastIndex);
337
        }
760 blopes 338
 
771 blopes 339
        g2 = ultimoComprador;
340
        idxG2 = idxUltimoComprador;
341
        log(String.format("GR[%d], G1[%d] => G2 (comprador) em [%d]", idxGR, idxG1, idxG2));
760 blopes 342
 
771 blopes 343
        // 5) G3 – próximo direcional vendedor; vendedor que rompe fundo de G2 com topo <= topo GR
344
        Candle g3 = null;
345
        int idxG3 = -1;
346
 
764 blopes 347
        int idxPrimeiroVendedorAposG2 = -1;
348
        for (int i = idxG2 + 1; i < n; i++) {
349
            Candle c = candles.get(i);
771 blopes 350
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
764 blopes 351
                continue;
760 blopes 352
 
764 blopes 353
            if (c.isCandleVendedor()) {
354
                idxPrimeiroVendedorAposG2 = i;
355
                break;
356
            } else {
771 blopes 357
                // primeiro direcional não é vendedor => padrão só até G2
764 blopes 358
                lastIndex = i;
771 blopes 359
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
764 blopes 360
            }
361
        }
771 blopes 362
        if (idxPrimeiroVendedorAposG2 == -1)
363
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
760 blopes 364
 
764 blopes 365
        for (int i = idxPrimeiroVendedorAposG2; i < n; i++) {
366
            Candle c = candles.get(i);
767 blopes 367
 
771 blopes 368
            // outside antes de G3 descarta
369
            if (isOutside(candles, i)) {
767 blopes 370
                lastIndex = i;
771 blopes 371
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (vendedor).", idxGR, i));
767 blopes 372
                return new ResultadoPadrao(null, lastIndex);
373
            }
374
 
771 blopes 375
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
376
                continue;
377
 
378
            // se romper topo do GR antes de G3 => descarta
379
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) {
380
                lastIndex = i;
381
                log(String.format("GR[%d]: candle [%d] rompeu topo do GR antes de G3.", idxGR, i));
382
                return new ResultadoPadrao(null, lastIndex);
383
            }
384
 
764 blopes 385
            if (!c.isCandleVendedor()) {
386
                lastIndex = i - 1;
387
                break;
388
            }
760 blopes 389
 
764 blopes 390
            lastIndex = i;
391
            boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima());
771 blopes 392
            boolean topoMenorOuIgualGR = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), gr.getMaxima());
393
            if (rompeFundoG2 && topoMenorOuIgualGR) {
394
                g3 = c;
764 blopes 395
                idxG3 = i;
396
                break;
397
            }
398
        }
760 blopes 399
 
771 blopes 400
        if (g3 == null)
401
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
760 blopes 402
 
771 blopes 403
        log(String.format("GR[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d]",
404
                idxGR, idxG1, idxG2, idxG3));
760 blopes 405
 
771 blopes 406
        // 6) G4 – próximo candle rompe topo do GR
764 blopes 407
        Candle g4 = null;
408
        int proxIdx = idxG3 + 1;
409
        if (proxIdx < n) {
410
            Candle c = candles.get(proxIdx);
411
            lastIndex = proxIdx;
771 blopes 412
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) {
764 blopes 413
                g4 = c;
771 blopes 414
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
415
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
764 blopes 416
            } else {
771 blopes 417
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d]: próximo candle [%d] não é G4.",
418
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
764 blopes 419
            }
420
        }
760 blopes 421
 
764 blopes 422
        PadraoGatilho padrao = new PadraoGatilho();
771 blopes 423
        padrao.setReferencia(gr);
764 blopes 424
        padrao.setGatilho1(g1);
425
        padrao.setGatilho2(g2);
426
        padrao.setGatilho3(g3);
427
        padrao.setGatilho4(g4);
760 blopes 428
 
764 blopes 429
        return new ResultadoPadrao(padrao, lastIndex);
430
    }
760 blopes 431
 
764 blopes 432
    // =====================================================================
771 blopes 433
    // CASO 2 – PADRÃO A PARTIR DE CANDLE VENDEDOR
764 blopes 434
    // =====================================================================
760 blopes 435
 
771 blopes 436
    private ResultadoPadrao detectarPadraoVendedor(List<Candle> candles, int idxA) {
764 blopes 437
        int n = candles.size();
771 blopes 438
        int lastIndex = idxA;
439
        Candle candleA = candles.get(idxA);
440
        if (!candleA.isCandleVendedor()) {
441
            return new ResultadoPadrao(null, idxA);
764 blopes 442
        }
760 blopes 443
 
771 blopes 444
        // 1) A deve ser último da tendência vendedora -> achar B comprador
445
        int idxB = -1;
446
        for (int i = idxA + 1; i < n; i++) {
764 blopes 447
            Candle c = candles.get(i);
771 blopes 448
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
764 blopes 449
                continue;
760 blopes 450
 
771 blopes 451
            if (c.isCandleVendedor()) {
452
                return new ResultadoPadrao(null, idxA);
764 blopes 453
            }
771 blopes 454
            if (c.isCandleComprador()) {
455
                idxB = i;
456
                break;
457
            }
764 blopes 458
        }
771 blopes 459
        if (idxB == -1) return new ResultadoPadrao(null, idxA);
760 blopes 460
 
771 blopes 461
        Candle candleB = candles.get(idxB);
760 blopes 462
 
771 blopes 463
        // 2) Candidato à referência: A ou B (conforme fundo)
464
        Candle candidatoRef = candleA;
465
        int idxCandidatoRef = idxA;
466
        if (BigDecimalUtils.ehMenorQue(candleB.getMinima(), candleA.getMinima())) {
467
            candidatoRef = candleB;
468
            idxCandidatoRef = idxB;
764 blopes 469
        }
760 blopes 470
 
771 blopes 471
        // 3) G1 – primeiro comprador que rompe topo do candidatoRef
472
        Candle g1 = null;
764 blopes 473
        int idxG1 = -1;
771 blopes 474
        for (int i = idxCandidatoRef + 1; i < n; i++) {
764 blopes 475
            Candle c = candles.get(i);
771 blopes 476
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
477
                continue;
760 blopes 478
 
764 blopes 479
            if (!c.isCandleComprador()) {
480
                lastIndex = i - 1;
481
                return new ResultadoPadrao(null, lastIndex);
482
            }
760 blopes 483
 
764 blopes 484
            lastIndex = i;
771 blopes 485
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) {
486
                g1 = c;
764 blopes 487
                idxG1 = i;
488
                break;
489
            }
490
        }
771 blopes 491
        if (g1 == null) return new ResultadoPadrao(null, lastIndex);
760 blopes 492
 
771 blopes 493
        Candle gr = candidatoRef;
494
        int idxGR = idxCandidatoRef;
495
        log(String.format("GR vendedor em [%d], G1 (comprador) em [%d]", idxGR, idxG1));
760 blopes 496
 
771 blopes 497
        // 4) G2 – nova tendência vendedora com fechamento dentro da região do GR
498
        Candle g2 = null;
499
        int idxG2 = -1;
760 blopes 500
 
764 blopes 501
        int idxPrimeiroVendedor = -1;
502
        for (int i = idxG1 + 1; i < n; i++) {
503
            Candle c = candles.get(i);
771 blopes 504
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
764 blopes 505
                continue;
760 blopes 506
 
764 blopes 507
            if (c.isCandleVendedor()) {
508
                idxPrimeiroVendedor = i;
509
                break;
510
            } else {
511
                lastIndex = i;
512
            }
513
        }
771 blopes 514
        if (idxPrimeiroVendedor == -1) return new ResultadoPadrao(null, lastIndex);
760 blopes 515
 
771 blopes 516
        Candle ultimoVendedor = null;
517
        int idxUltimoVendedor = -1;
518
        boolean rompeuFundoGR = false;
760 blopes 519
 
764 blopes 520
        for (int i = idxPrimeiroVendedor; i < n; i++) {
521
            Candle c = candles.get(i);
760 blopes 522
 
771 blopes 523
            if (isOutside(candles, i)) {
767 blopes 524
                lastIndex = i;
771 blopes 525
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (vendedor).", idxGR, i));
767 blopes 526
                return new ResultadoPadrao(null, lastIndex);
527
            }
528
 
771 blopes 529
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
530
                continue;
760 blopes 531
 
771 blopes 532
            if (c.isCandleVendedor()) {
533
                // se romper fundo do GR, descarta
534
                if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) {
535
                    rompeuFundoGR = true;
536
                    lastIndex = i;
537
                    break;
538
                }
539
                ultimoVendedor = c;
540
                idxUltimoVendedor = i;
541
                lastIndex = i;
542
            } else if (c.isCandleComprador()) {
543
                lastIndex = i - 1;
764 blopes 544
                break;
545
            }
546
        }
760 blopes 547
 
771 blopes 548
        if (rompeuFundoGR || ultimoVendedor == null)
764 blopes 549
            return new ResultadoPadrao(null, lastIndex);
760 blopes 550
 
771 blopes 551
        boolean fechamentoDentroRegiaoGR =
552
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMinima()) &&
553
                BigDecimalUtils.ehMenorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMaxima());
760 blopes 554
 
771 blopes 555
        if (!fechamentoDentroRegiaoGR)
764 blopes 556
            return new ResultadoPadrao(null, lastIndex);
760 blopes 557
 
771 blopes 558
        g2 = ultimoVendedor;
559
        idxG2 = idxUltimoVendedor;
560
        log(String.format("GR[%d], G1[%d] => G2 (vendedor) em [%d]", idxGR, idxG1, idxG2));
760 blopes 561
 
771 blopes 562
        // 5) G3 – próximo direcional comprador; comprador que rompe topo de G2 com fundo >= fundo GR
563
        Candle g3 = null;
564
        int idxG3 = -1;
565
 
764 blopes 566
        int idxPrimeiroCompradorAposG2 = -1;
567
        for (int i = idxG2 + 1; i < n; i++) {
568
            Candle c = candles.get(i);
771 blopes 569
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
764 blopes 570
                continue;
760 blopes 571
 
764 blopes 572
            if (c.isCandleComprador()) {
573
                idxPrimeiroCompradorAposG2 = i;
574
                break;
575
            } else {
576
                lastIndex = i;
771 blopes 577
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
764 blopes 578
            }
579
        }
771 blopes 580
        if (idxPrimeiroCompradorAposG2 == -1)
581
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
760 blopes 582
 
764 blopes 583
        for (int i = idxPrimeiroCompradorAposG2; i < n; i++) {
584
            Candle c = candles.get(i);
767 blopes 585
 
771 blopes 586
            if (isOutside(candles, i)) {
767 blopes 587
                lastIndex = i;
771 blopes 588
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (comprador).", idxGR, i));
767 blopes 589
                return new ResultadoPadrao(null, lastIndex);
590
            }
591
 
771 blopes 592
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
593
                continue;
594
 
595
            // se romper fundo do GR antes de G3 => descarta
596
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) {
597
                lastIndex = i;
598
                log(String.format("GR[%d]: candle [%d] rompeu fundo do GR antes de G3.", idxGR, i));
599
                return new ResultadoPadrao(null, lastIndex);
600
            }
601
 
764 blopes 602
            if (!c.isCandleComprador()) {
603
                lastIndex = i - 1;
604
                break;
605
            }
760 blopes 606
 
764 blopes 607
            lastIndex = i;
608
            boolean rompeTopoG2 = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima());
771 blopes 609
            boolean fundoMaiorOuIgualGR = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), gr.getMinima());
610
            if (rompeTopoG2 && fundoMaiorOuIgualGR) {
611
                g3 = c;
764 blopes 612
                idxG3 = i;
613
                break;
614
            }
615
        }
760 blopes 616
 
771 blopes 617
        if (g3 == null)
618
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
760 blopes 619
 
771 blopes 620
        log(String.format("GR[%d], G1[%d], G2[%d] => G3 (comprador) em [%d]",
621
                idxGR, idxG1, idxG2, idxG3));
760 blopes 622
 
771 blopes 623
        // 6) G4 – próximo candle rompe fundo do GR
764 blopes 624
        Candle g4 = null;
625
        int proxIdx = idxG3 + 1;
626
        if (proxIdx < n) {
627
            Candle c = candles.get(proxIdx);
628
            lastIndex = proxIdx;
771 blopes 629
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) {
764 blopes 630
                g4 = c;
771 blopes 631
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
632
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
764 blopes 633
            } else {
771 blopes 634
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d]: próximo candle [%d] não é G4.",
635
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
764 blopes 636
            }
637
        }
760 blopes 638
 
764 blopes 639
        PadraoGatilho padrao = new PadraoGatilho();
771 blopes 640
        padrao.setReferencia(gr);
764 blopes 641
        padrao.setGatilho1(g1);
642
        padrao.setGatilho2(g2);
643
        padrao.setGatilho3(g3);
644
        padrao.setGatilho4(g4);
760 blopes 645
 
764 blopes 646
        return new ResultadoPadrao(padrao, lastIndex);
647
    }
760 blopes 648
 
764 blopes 649
    // =====================================================================
771 blopes 650
    // MODO TEMPO REAL
764 blopes 651
    // =====================================================================
760 blopes 652
 
764 blopes 653
    public void resetTempoReal() {
654
        this.idxProximaAnaliseTempoReal = 0;
655
    }
760 blopes 656
 
764 blopes 657
    public PadraoGatilho processarCandleTempoReal(List<Candle> candles) {
658
        int n = candles.size();
771 blopes 659
        if (n < 4) return null;
760 blopes 660
 
764 blopes 661
        while (idxProximaAnaliseTempoReal < n - 3) {
662
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxProximaAnaliseTempoReal);
760 blopes 663
 
764 blopes 664
            if (resultado == null) {
665
                idxProximaAnaliseTempoReal++;
666
                continue;
667
            }
760 blopes 668
 
764 blopes 669
            int proximoInicio = resultado.lastIndex + 1;
670
            if (proximoInicio <= idxProximaAnaliseTempoReal) {
671
                proximoInicio = idxProximaAnaliseTempoReal + 1;
672
            }
673
            idxProximaAnaliseTempoReal = proximoInicio;
760 blopes 674
 
764 blopes 675
            if (resultado.padrao != null) {
676
                return resultado.padrao;
677
            }
678
        }
760 blopes 679
 
764 blopes 680
        return null;
681
    }
760 blopes 682
 
764 blopes 683
}