Subversion Repositories Integrator Subversion

Rev

Rev 767 | Rev 773 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 767 Rev 771
Line 1... Line 1...
1
package br.com.kronus.core;
1
package br.com.kronus.core;
2
2
3
import java.math.BigDecimal;
-
 
4
import java.util.ArrayList;
3
import java.util.ArrayList;
5
import java.util.List;
4
import java.util.List;
6
5
7
import br.com.sl.domain.model.Candle;
6
import br.com.sl.domain.model.Candle;
8
import br.com.sl.domain.util.BigDecimalUtils;
7
import br.com.sl.domain.util.BigDecimalUtils;
9
8
10
/**
9
/**
11
 * Detector de padrões de gatilhos (GR, G1, G2, G3, G4)
10
 * Detector de padrões de gatilhos (GR, G1, G2, G3, G4)
12
 * trabalhando tanto em modo backtest quanto em modo tempo real.
11
 * trabalhando tanto em modo backtest quanto em modo tempo real.
13
 *
12
 *
14
 * Regras resumidas:
-
 
-
 
13
 * Regras implementadas (texto alinhado):
15
 *
14
 *
16
 * GR comprador (A):
-
 
17
 *   - Candle comprador.
-
 
18
 *   - Último candle da tendência compradora.
-
 
19
 *   - Maior topo da pernada compradora.
-
 
20
 *   - Após ele, tendência muda para vendedora.
-
 
21
 *   - Um vendedor subsequente rompe o fundo de A => G1 vendedor, A vira GR.
-
 
-
 
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.
22
 *
22
 *
23
 * GR vendedor (B):
-
 
24
 *   - Candle vendedor.
-
 
25
 *   - Último candle da tendência vendedora.
-
 
26
 *   - Menor fundo da pernada vendedora.
-
 
27
 *   - Após ele, tendência muda para compradora.
-
 
28
 *   - Um comprador subsequente rompe o topo de B => G1 comprador, B vira GR.
-
 
-
 
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.
29
 *
27
 *
30
 * G2 comprador (a partir de GR comprador):
-
 
31
 *   - Após G1 (vendedor), surge nova tendência compradora.
-
 
32
 *   - O ÚLTIMO candle dessa tendência compradora tem fechamento dentro da região do GR.
-
 
33
 *   - Se nessa tendência compradora alguma barra romper o topo do GR, o padrão é descartado.
-
 
34
 *   - Se a tendência terminar sem que o último comprador feche dentro da região do GR, descarta.
-
 
-
 
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.
35
 *
32
 *
36
 * G3 comprador:
-
 
37
 *   - Após G2, o próximo candle deve ser vendedor.
-
 
38
 *   - Em uma sequência vendedora, algum vendedor:
-
 
39
 *       * rompe o fundo de G2 (mínima < mínima de G2) e
-
 
40
 *       * tem topo <= topo do GR
-
 
41
 *     => esse candle é G3.
-
 
-
 
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.
42
 *
44
 *
43
 * G4 comprador:
-
 
44
 *   - Próximo candle após G3.
-
 
45
 *   - Se romper o topo do GR (máxima > máxima do GR) => G4.
-
 
46
 *   - Desarma a operação, padrão é encerrado, nova análise começa após esse candle.
-
 
-
 
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.
47
 *
54
 *
48
 * G2 vendedor (a partir de GR vendedor):
-
 
49
 *   - Após G1 (comprador), surge nova tendência vendedora.
-
 
50
 *   - O ÚLTIMO candle dessa tendência vendedora tem fechamento dentro da região do GR.
-
 
51
 *   - Se nessa tendência vendedora alguma barra romper o fundo do GR, o padrão é descartado.
-
 
-
 
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.
52
 *
58
 *
53
 * G3 vendedor:
-
 
54
 *   - Após G2, o próximo candle deve ser comprador.
-
 
55
 *   - Em uma sequência compradora, algum comprador:
-
 
56
 *       * rompe o topo de G2 (máxima > máxima de G2) e
-
 
57
 *       * tem fundo >= fundo do GR
-
 
58
 *     => esse candle é G3.
-
 
-
 
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.
59
 *
66
 *
60
 * G4 vendedor:
-
 
61
 *   - Próximo candle após G3.
-
 
62
 *   - Se romper o fundo do GR (mínima < mínima do GR) => G4.
-
 
-
 
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.
63
 *
71
 *
64
 * Outside em relação ao GR:
-
 
65
 *   - Qualquer candle C após o GR que:
-
 
66
 *       * C.maxima > GR.maxima E
-
 
67
 *       * C.minima < GR.minima
-
 
68
 *     => padrão atual é descartado, nova análise começa após C.
-
 
-
 
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.
69
 *
76
 *
70
 * Obs.: Quando um ciclo termina (padrão encontrado ou descartado),
-
 
71
 * a próxima análise SEMPRE começa após o último candle usado.
-
 
-
 
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.
72
 */
106
 */
73
public class DetectorGatilhos {
107
public class DetectorGatilhos {
74
108
75
    private final boolean logAtivo;
109
    private final boolean logAtivo;
76
-
 
77
    // Estado apenas para o modo tempo real
-
 
78
    private int idxProximaAnaliseTempoReal = 0;
110
    private int idxProximaAnaliseTempoReal = 0;
79
111
80
    public DetectorGatilhos() {
112
    public DetectorGatilhos() {
81
        this(false);
113
        this(false);
82
    }
114
    }
Line 90... Line 122...
90
            System.out.println(msg);
122
            System.out.println(msg);
91
        }
123
        }
92
    }
124
    }
93
125
94
    private static class ResultadoPadrao {
126
    private static class ResultadoPadrao {
95
        PadraoGatilho padrao; // pode ser null (quando não houve padrão válido)
-
 
96
        int lastIndex;        // último índice de candle usado no ciclo
-
 
97
-
 
-
 
127
        PadraoGatilho padrao;
-
 
128
        int lastIndex;
98
        ResultadoPadrao(PadraoGatilho padrao, int lastIndex) {
129
        ResultadoPadrao(PadraoGatilho padrao, int lastIndex) {
99
            this.padrao = padrao;
130
            this.padrao = padrao;
100
            this.lastIndex = lastIndex;
131
            this.lastIndex = lastIndex;
101
        }
132
        }
102
    }
133
    }
103
134
104
    /**
-
 
105
     * Cria um resultado com padrão PARCIAL (até G2).
-
 
106
     * Usado quando GR, G1 e G2 são válidos, mas G3 não se forma.
-
 
107
     */
-
 
108
    private ResultadoPadrao criarResultadoParcialComG2(Candle ref,
135
    private ResultadoPadrao criarResultadoParcialComG2(Candle ref,
109
                                                       Candle g1,
136
                                                       Candle g1,
110
                                                       Candle g2,
137
                                                       Candle g2,
111
                                                       int lastIndex) {
138
                                                       int lastIndex) {
112
        PadraoGatilho padrao = new PadraoGatilho();
139
        PadraoGatilho padrao = new PadraoGatilho();
Line 117... Line 144...
117
        padrao.setGatilho4(null);
144
        padrao.setGatilho4(null);
118
        return new ResultadoPadrao(padrao, lastIndex);
145
        return new ResultadoPadrao(padrao, lastIndex);
119
    }
146
    }
120
147
121
    // =====================================================================
148
    // =====================================================================
122
    // API PRINCIPAL – BACKTEST (lista completa de candles)
-
 
-
 
149
    // API PRINCIPAL – BACKTEST
123
    // =====================================================================
150
    // =====================================================================
124
151
125
    public List<PadraoGatilho> identificarPadroes(List<Candle> candles) {
152
    public List<PadraoGatilho> identificarPadroes(List<Candle> candles) {
126
        List<PadraoGatilho> padroes = new ArrayList<>();
153
        List<PadraoGatilho> padroes = new ArrayList<>();
127
        int n = candles.size();
154
        int n = candles.size();
128
        if (n < 4) {
-
 
129
            return padroes;
-
 
130
        }
-
 
-
 
155
        if (n < 4) return padroes;
131
156
132
        int idxRef = 0;
157
        int idxRef = 0;
133
-
 
134
        while (idxRef < n - 3) {
158
        while (idxRef < n - 3) {
135
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxRef);
159
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxRef);
136
160
137
            if (resultado == null) {
161
            if (resultado == null) {
138
                idxRef++;
162
                idxRef++;
Line 141... Line 165...
141
165
142
            if (resultado.padrao != null) {
166
            if (resultado.padrao != null) {
143
                padroes.add(resultado.padrao);
167
                padroes.add(resultado.padrao);
144
            }
168
            }
145
169
146
            // Próxima análise sempre começa após o último candle usado no ciclo
-
 
147
            idxRef = Math.max(resultado.lastIndex + 1, idxRef + 1);
170
            idxRef = Math.max(resultado.lastIndex + 1, idxRef + 1);
148
        }
171
        }
149
172
150
        return padroes;
173
        return padroes;
151
    }
174
    }
152
175
153
    private ResultadoPadrao detectarPadraoAPartir(List<Candle> candles, int idxRef) {
176
    private ResultadoPadrao detectarPadraoAPartir(List<Candle> candles, int idxRef) {
154
        Candle ref = candles.get(idxRef);
177
        Candle ref = candles.get(idxRef);
155
-
 
156
        if (ref.isCandleComprador()) {
178
        if (ref.isCandleComprador()) {
157
            return detectarPadraoComprador(candles, idxRef);
179
            return detectarPadraoComprador(candles, idxRef);
158
        } else if (ref.isCandleVendedor()) {
180
        } else if (ref.isCandleVendedor()) {
159
            return detectarPadraoVendedor(candles, idxRef);
181
            return detectarPadraoVendedor(candles, idxRef);
160
        } else {
182
        } else {
161
            // candle sem tendência não serve como GR
-
 
162
            return new ResultadoPadrao(null, idxRef);
183
            return new ResultadoPadrao(null, idxRef);
163
        }
184
        }
164
    }
185
    }
165
186
166
    // =====================================================================
187
    // =====================================================================
167
    // CASO 1 – GR COMPRADOR (candle A)
-
 
-
 
188
    // HELPERS
168
    // =====================================================================
189
    // =====================================================================
169
190
170
    private ResultadoPadrao detectarPadraoComprador(List<Candle> candles, int idxRef) {
-
 
171
        int n = candles.size();
-
 
172
        Candle ref = candles.get(idxRef); // candidato a GR
-
 
173
        int lastIndex = idxRef;
-
 
-
 
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
    }
174
198
175
        // 0) Validar se idxRef pode ser GR comprador (A)
-
 
176
        if (!ref.isCandleComprador()) {
-
 
177
            return new ResultadoPadrao(null, idxRef);
-
 
178
        }
-
 
-
 
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
    }
179
206
180
        // 0.1 – início da pernada compradora que termina em A
-
 
181
        int inicioTrendCompra = idxRef;
-
 
182
        for (int i = idxRef - 1; i >= 0; i--) {
-
 
183
            Candle c = candles.get(i);
-
 
184
            if (!c.isCandleComprador()) {
-
 
185
                break;
-
 
186
            }
-
 
187
            inicioTrendCompra = i;
-
 
-
 
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);
188
        }
217
        }
189
218
190
        // 0.2 – primeiro candle direcional após A
-
 
191
        int idxPrimeiroDirecionalAposRef = -1;
-
 
192
        for (int i = idxRef + 1; i < n; i++) {
-
 
-
 
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++) {
193
            Candle c = candles.get(i);
222
            Candle c = candles.get(i);
194
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
-
 
223
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor())) {
195
                continue;
224
                continue;
196
            }
225
            }
197
-
 
198
            // OUTSIDE em relação ao GR (até G3)
-
 
199
            if (isOutsideReferencia(ref, c)) {
-
 
200
                lastIndex = i;
-
 
201
                log(String.format(
-
 
202
                        "RefC[%d]: OUTSIDE em [%d] antes da mudança de tendência. Padrão descartado.",
-
 
203
                        idxRef, i));
-
 
204
                return new ResultadoPadrao(null, lastIndex);
-
 
-
 
226
            if (c.isCandleComprador()) {
-
 
227
                // A não é o último comprador
-
 
228
                return new ResultadoPadrao(null, idxA);
-
 
229
            }
-
 
230
            if (c.isCandleVendedor()) {
-
 
231
                idxB = i;
-
 
232
                break;
205
            }
233
            }
206
-
 
207
            idxPrimeiroDirecionalAposRef = i;
-
 
208
            break;
-
 
209
        }
234
        }
-
 
235
        if (idxB == -1) return new ResultadoPadrao(null, idxA);
210
236
211
        if (idxPrimeiroDirecionalAposRef == -1) {
-
 
212
            return new ResultadoPadrao(null, lastIndex);
-
 
213
        }
-
 
-
 
237
        Candle candleB = candles.get(idxB);
214
238
215
        Candle primeiroDirecional = candles.get(idxPrimeiroDirecionalAposRef);
-
 
216
        if (!primeiroDirecional.isCandleVendedor()) {
-
 
217
            // primeiro direcional após A ainda é comprador → A não é último da pernada
-
 
218
            return new ResultadoPadrao(null, idxRef);
-
 
-
 
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;
219
        }
245
        }
220
246
221
        // 0.3 – A tem o maior topo da pernada compradora
-
 
222
        BigDecimal topoRef = ref.getMaxima();
-
 
223
        for (int i = inicioTrendCompra; i <= idxRef; i++) {
-
 
224
            Candle c = candles.get(i);
-
 
225
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), topoRef)) {
-
 
226
                return new ResultadoPadrao(null, idxRef);
-
 
227
            }
-
 
228
        }
-
 
229
-
 
230
        int idxPrimeiroVendedor = idxPrimeiroDirecionalAposRef;
-
 
231
-
 
232
        // 1) Encontrar G1 (vendedor que rompe o fundo do GR)
-
 
-
 
247
        // 3) G1 – primeiro vendedor que rompe fundo do candidatoRef
-
 
248
        Candle g1 = null;
233
        int idxG1 = -1;
249
        int idxG1 = -1;
234
        Candle g1 = null;
-
 
235
-
 
236
        for (int i = idxPrimeiroVendedor; i < n; i++) {
-
 
-
 
250
        for (int i = idxCandidatoRef + 1; i < n; i++) {
237
            Candle c = candles.get(i);
251
            Candle c = candles.get(i);
238
-
 
239
            // OUTSIDE em relação ao GR (até G3)
-
 
240
            if (isOutsideReferencia(ref, c)) {
-
 
241
                lastIndex = i;
-
 
242
                log(String.format(
-
 
243
                        "RefC[%d]: OUTSIDE em [%d] durante busca de G1. Padrão descartado.",
-
 
244
                        idxRef, i));
-
 
245
                return new ResultadoPadrao(null, lastIndex);
-
 
-
 
252
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor())) {
-
 
253
                continue;
246
            }
254
            }
247
255
248
            if (!c.isCandleVendedor()) {
256
            if (!c.isCandleVendedor()) {
249
                // tendência vendedora terminou antes do rompimento
-
 
250
                lastIndex = i - 1;
257
                lastIndex = i - 1;
251
                return new ResultadoPadrao(null, lastIndex);
258
                return new ResultadoPadrao(null, lastIndex);
252
            }
259
            }
253
260
254
            lastIndex = i;
261
            lastIndex = i;
255
-
 
256
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), ref.getMinima())) {
-
 
-
 
262
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) {
-
 
263
                g1 = c;
257
                idxG1 = i;
264
                idxG1 = i;
258
                g1 = c;
-
 
259
                break;
265
                break;
260
            }
266
            }
261
        }
267
        }
262
268
263
        if (idxG1 == -1) {
-
 
264
            return new ResultadoPadrao(null, lastIndex);
-
 
265
        }
-
 
-
 
269
        if (g1 == null) return new ResultadoPadrao(null, lastIndex);
-
 
270
-
 
271
        Candle gr = candidatoRef;
-
 
272
        int idxGR = idxCandidatoRef;
-
 
273
        log(String.format("GR comprador em [%d], G1 (vendedor) em [%d]", idxGR, idxG1));
266
274
267
        log(String.format("RefC[%d] (GR) => G1 (vendedor) em [%d]", idxRef, idxG1));
-
 
-
 
275
        // 4) G2 – nova tendência compradora com fechamento dentro da região do GR
-
 
276
        Candle g2 = null;
-
 
277
        int idxG2 = -1;
268
278
269
        // 2) Encontrar G2 (retorno comprador à região do GR)
-
 
270
        int idxPrimeiroComprador = -1;
279
        int idxPrimeiroComprador = -1;
271
        for (int i = idxG1 + 1; i < n; i++) {
280
        for (int i = idxG1 + 1; i < n; i++) {
272
            Candle c = candles.get(i);
281
            Candle c = candles.get(i);
-
 
282
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
-
 
283
                continue;
273
284
274
            // OUTSIDE em relação ao GR (até G3)
-
 
275
            if (isOutsideReferencia(ref, c)) {
-
 
276
                lastIndex = i;
-
 
277
                log(String.format(
-
 
278
                        "RefC[%d]: OUTSIDE em [%d] antes da tendência de G2. Padrão descartado.",
-
 
279
                        idxRef, i));
-
 
280
                return new ResultadoPadrao(null, lastIndex);
-
 
281
            }
-
 
282
-
 
283
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
284
                continue;
-
 
285
            }
-
 
286
            if (c.isCandleComprador()) {
285
            if (c.isCandleComprador()) {
287
                idxPrimeiroComprador = i;
286
                idxPrimeiroComprador = i;
288
                break;
287
                break;
289
            } else {
288
            } else {
-
 
289
                // ainda vendedor
290
                lastIndex = i;
290
                lastIndex = i;
291
            }
291
            }
292
        }
292
        }
293
293
294
        if (idxPrimeiroComprador == -1) {
-
 
295
            return new ResultadoPadrao(null, lastIndex);
-
 
296
        }
-
 
-
 
294
        if (idxPrimeiroComprador == -1) return new ResultadoPadrao(null, lastIndex);
297
295
298
        boolean rompeuTopoRef = false;
-
 
299
        Candle ultimoCompradorTrend = null;
-
 
300
        int idxUltimoCompradorTrend = -1;
-
 
-
 
296
        Candle ultimoComprador = null;
-
 
297
        int idxUltimoComprador = -1;
301
298
302
        for (int i = idxPrimeiroComprador; i < n; i++) {
299
        for (int i = idxPrimeiroComprador; i < n; i++) {
303
            Candle c = candles.get(i);
300
            Candle c = candles.get(i);
304
301
305
            // OUTSIDE em relação ao GR (até G3)
-
 
306
            if (isOutsideReferencia(ref, c)) {
-
 
-
 
302
            // outside antes de G3 descarta
-
 
303
            if (isOutside(candles, i)) {
307
                lastIndex = i;
304
                lastIndex = i;
308
                log(String.format(
-
 
309
                        "RefC[%d]: OUTSIDE em [%d] durante formação de G2. Padrão descartado.",
-
 
310
                        idxRef, i));
-
 
-
 
305
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (comprador).", idxGR, i));
311
                return new ResultadoPadrao(null, lastIndex);
306
                return new ResultadoPadrao(null, lastIndex);
312
            }
307
            }
313
308
314
            if (!c.isCandleComprador()) {
-
 
315
                break;
-
 
316
            }
-
 
-
 
309
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
-
 
310
                continue;
317
311
318
            ultimoCompradorTrend = c;
-
 
319
            idxUltimoCompradorTrend = i;
-
 
320
            lastIndex = i;
-
 
321
-
 
322
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), ref.getMaxima())) {
-
 
323
                rompeuTopoRef = true;
-
 
-
 
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;
324
                break;
325
                break;
325
            }
326
            }
326
        }
327
        }
327
328
328
        if (rompeuTopoRef || idxUltimoCompradorTrend == -1) {
-
 
329
            return new ResultadoPadrao(null, lastIndex);
-
 
330
        }
-
 
-
 
329
        if (ultimoComprador == null) return new ResultadoPadrao(null, lastIndex);
331
330
332
        boolean fechamentoDentroRegiaoRef =
-
 
333
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoCompradorTrend.getFechamento(), ref.getMinima()) &&
-
 
334
                BigDecimalUtils.ehMenorOuIgualQue(ultimoCompradorTrend.getFechamento(), ref.getMaxima());
-
 
-
 
331
        boolean fechamentoDentroRegiaoGR =
-
 
332
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoComprador.getFechamento(), gr.getMinima()) &&
-
 
333
                BigDecimalUtils.ehMenorOuIgualQue(ultimoComprador.getFechamento(), gr.getMaxima());
335
334
336
        if (!fechamentoDentroRegiaoRef) {
-
 
-
 
335
        if (!fechamentoDentroRegiaoGR) {
337
            return new ResultadoPadrao(null, lastIndex);
336
            return new ResultadoPadrao(null, lastIndex);
338
        }
337
        }
339
338
340
        Candle g2 = ultimoCompradorTrend;
-
 
341
        int idxG2 = idxUltimoCompradorTrend;
-
 
342
        log(String.format("RefC[%d], G1[%d] => G2 (comprador) em [%d]", idxRef, idxG1, idxG2));
-
 
-
 
339
        g2 = ultimoComprador;
-
 
340
        idxG2 = idxUltimoComprador;
-
 
341
        log(String.format("GR[%d], G1[%d] => G2 (comprador) em [%d]", idxGR, idxG1, idxG2));
-
 
342
-
 
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;
343
346
344
        // 3) Encontrar G3 (vendedor rompendo fundo de G2, topo <= topo do GR)
-
 
345
        int idxPrimeiroVendedorAposG2 = -1;
347
        int idxPrimeiroVendedorAposG2 = -1;
346
        for (int i = idxG2 + 1; i < n; i++) {
348
        for (int i = idxG2 + 1; i < n; i++) {
347
            Candle c = candles.get(i);
349
            Candle c = candles.get(i);
348
-
 
349
            // OUTSIDE em relação ao GR (até G3)
-
 
350
            if (isOutsideReferencia(ref, c)) {
-
 
351
                lastIndex = i;
-
 
352
                log(String.format(
-
 
353
                        "RefC[%d]: OUTSIDE em [%d] antes da sequência vendedora de G3. Padrão descartado.",
-
 
354
                        idxRef, i));
-
 
355
                return new ResultadoPadrao(null, lastIndex);
-
 
356
            }
-
 
357
-
 
358
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
-
 
350
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
359
                continue;
351
                continue;
360
            }
-
 
361
352
362
            if (c.isCandleVendedor()) {
353
            if (c.isCandleVendedor()) {
363
                idxPrimeiroVendedorAposG2 = i;
354
                idxPrimeiroVendedorAposG2 = i;
364
                break;
355
                break;
365
            } else {
356
            } else {
-
 
357
                // primeiro direcional não é vendedor => padrão só até G2
366
                lastIndex = i;
358
                lastIndex = i;
367
                // pela sua regra, o próximo após G2 deveria ser vendedor
-
 
368
                // => padrão chegou até G2, mas não evoluiu corretamente para G3
-
 
369
                return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
-
 
359
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
370
            }
360
            }
371
        }
361
        }
372
-
 
373
        if (idxPrimeiroVendedorAposG2 == -1) {
-
 
374
            // padrão com GR, G1, G2, mas sem sequência adequada para G3
-
 
375
            return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
376
        }
-
 
377
-
 
378
        int idxG3 = -1;
-
 
379
        Candle g3 = null;
-
 
-
 
362
        if (idxPrimeiroVendedorAposG2 == -1)
-
 
363
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
380
364
381
        for (int i = idxPrimeiroVendedorAposG2; i < n; i++) {
365
        for (int i = idxPrimeiroVendedorAposG2; i < n; i++) {
382
            Candle c = candles.get(i);
366
            Candle c = candles.get(i);
383
367
384
            // OUTSIDE em relação ao GR (ATÉ IDENTIFICAR G3)
-
 
385
            if (isOutsideReferencia(ref, c)) {
-
 
-
 
368
            // outside antes de G3 descarta
-
 
369
            if (isOutside(candles, i)) {
386
                lastIndex = i;
370
                lastIndex = i;
387
                log(String.format(
-
 
388
                        "RefC[%d]: OUTSIDE em [%d] durante formação de G3. Padrão descartado.",
-
 
389
                        idxRef, i));
-
 
-
 
371
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (vendedor).", idxGR, i));
-
 
372
                return new ResultadoPadrao(null, lastIndex);
-
 
373
            }
-
 
374
-
 
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));
390
                return new ResultadoPadrao(null, lastIndex);
382
                return new ResultadoPadrao(null, lastIndex);
391
            }
383
            }
392
384
393
            if (!c.isCandleVendedor()) {
385
            if (!c.isCandleVendedor()) {
394
                lastIndex = i - 1;
386
                lastIndex = i - 1;
395
                break;
387
                break;
396
            }
388
            }
397
389
398
            lastIndex = i;
390
            lastIndex = i;
399
-
 
400
            boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima());
391
            boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima());
401
            boolean topoMenorOuIgualRef = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), ref.getMaxima());
-
 
402
-
 
403
            if (rompeFundoG2 && topoMenorOuIgualRef) {
-
 
-
 
392
            boolean topoMenorOuIgualGR = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), gr.getMaxima());
-
 
393
            if (rompeFundoG2 && topoMenorOuIgualGR) {
-
 
394
                g3 = c;
404
                idxG3 = i;
395
                idxG3 = i;
405
                g3 = c;
-
 
406
                break;
396
                break;
407
            }
397
            }
408
        }
398
        }
409
399
410
        if (idxG3 == -1 || g3 == null) {
-
 
411
            // Chegou até G2 mas não encontrou G3
-
 
412
            return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
413
        }
-
 
-
 
400
        if (g3 == null)
-
 
401
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
414
402
415
        log(String.format("RefC[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d]",
-
 
416
                idxRef, idxG1, idxG2, idxG3));
-
 
-
 
403
        log(String.format("GR[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d]",
-
 
404
                idxGR, idxG1, idxG2, idxG3));
417
405
418
        // 4) G4 – próximo candle rompe topo do GR (aqui já não precisamos mais checar outside)
-
 
-
 
406
        // 6) G4 – próximo candle rompe topo do GR
419
        Candle g4 = null;
407
        Candle g4 = null;
420
        int proxIdx = idxG3 + 1;
408
        int proxIdx = idxG3 + 1;
421
        if (proxIdx < n) {
409
        if (proxIdx < n) {
422
            Candle c = candles.get(proxIdx);
410
            Candle c = candles.get(proxIdx);
423
            lastIndex = proxIdx;
411
            lastIndex = proxIdx;
424
-
 
425
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), ref.getMaxima())) {
-
 
-
 
412
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) {
426
                g4 = c;
413
                g4 = c;
427
                log(String.format("RefC[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
-
 
428
                        idxRef, idxG1, idxG2, idxG3, proxIdx));
-
 
-
 
414
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
-
 
415
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
429
            } else {
416
            } else {
430
                log(String.format("RefC[%d], G1[%d], G2[%d], G3[%d]: próximo candle [%d] não é G4.",
-
 
431
                        idxRef, idxG1, idxG2, idxG3, proxIdx));
-
 
-
 
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));
432
            }
419
            }
433
        }
420
        }
434
421
435
        PadraoGatilho padrao = new PadraoGatilho();
422
        PadraoGatilho padrao = new PadraoGatilho();
436
        padrao.setReferencia(ref);
-
 
-
 
423
        padrao.setReferencia(gr);
437
        padrao.setGatilho1(g1);
424
        padrao.setGatilho1(g1);
438
        padrao.setGatilho2(g2);
425
        padrao.setGatilho2(g2);
439
        padrao.setGatilho3(g3);
426
        padrao.setGatilho3(g3);
440
        padrao.setGatilho4(g4);
427
        padrao.setGatilho4(g4);
441
428
442
        return new ResultadoPadrao(padrao, lastIndex);
429
        return new ResultadoPadrao(padrao, lastIndex);
443
    }
430
    }
444
-
 
445
431
446
    // =====================================================================
432
    // =====================================================================
447
    // CASO 2 – GR VENDEDOR (candle B)
-
 
-
 
433
    // CASO 2 – PADRÃO A PARTIR DE CANDLE VENDEDOR
448
    // =====================================================================
434
    // =====================================================================
449
435
450
    private ResultadoPadrao detectarPadraoVendedor(List<Candle> candles, int idxRef) {
-
 
-
 
436
    private ResultadoPadrao detectarPadraoVendedor(List<Candle> candles, int idxA) {
451
        int n = candles.size();
437
        int n = candles.size();
452
        Candle ref = candles.get(idxRef); // candidato a GR vendedor
-
 
453
        int lastIndex = idxRef;
-
 
454
-
 
455
        // 0) Validar se idxRef pode ser GR vendedor (B)
-
 
456
        if (!ref.isCandleVendedor()) {
-
 
457
            return new ResultadoPadrao(null, idxRef);
-
 
-
 
438
        int lastIndex = idxA;
-
 
439
        Candle candleA = candles.get(idxA);
-
 
440
        if (!candleA.isCandleVendedor()) {
-
 
441
            return new ResultadoPadrao(null, idxA);
458
        }
442
        }
459
443
460
        // 0.1 – início da pernada vendedora que termina em B
-
 
461
        int inicioTrendVenda = idxRef;
-
 
462
        for (int i = idxRef - 1; i >= 0; i--) {
-
 
-
 
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++) {
463
            Candle c = candles.get(i);
447
            Candle c = candles.get(i);
464
            if (!c.isCandleVendedor()) {
-
 
465
                break;
-
 
466
            }
-
 
467
            inicioTrendVenda = i;
-
 
468
        }
-
 
469
-
 
470
        // 0.2 – primeiro candle direcional após B
-
 
471
        int idxPrimeiroDirecionalAposRef = -1;
-
 
472
        for (int i = idxRef + 1; i < n; i++) {
-
 
473
            Candle c = candles.get(i);
-
 
474
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
-
 
448
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
475
                continue;
449
                continue;
476
            }
-
 
477
450
478
            // OUTSIDE em relação ao GR (até G3)
-
 
479
            if (isOutsideReferencia(ref, c)) {
-
 
480
                lastIndex = i;
-
 
481
                log(String.format(
-
 
482
                        "RefV[%d]: OUTSIDE em [%d] antes da mudança de tendência. Padrão descartado.",
-
 
483
                        idxRef, i));
-
 
484
                return new ResultadoPadrao(null, lastIndex);
-
 
-
 
451
            if (c.isCandleVendedor()) {
-
 
452
                return new ResultadoPadrao(null, idxA);
-
 
453
            }
-
 
454
            if (c.isCandleComprador()) {
-
 
455
                idxB = i;
-
 
456
                break;
485
            }
457
            }
486
-
 
487
            idxPrimeiroDirecionalAposRef = i;
-
 
488
            break;
-
 
489
        }
458
        }
-
 
459
        if (idxB == -1) return new ResultadoPadrao(null, idxA);
490
460
491
        if (idxPrimeiroDirecionalAposRef == -1) {
-
 
492
            return new ResultadoPadrao(null, lastIndex);
-
 
493
        }
-
 
-
 
461
        Candle candleB = candles.get(idxB);
494
462
495
        Candle primeiroDirecional = candles.get(idxPrimeiroDirecionalAposRef);
-
 
496
        if (!primeiroDirecional.isCandleComprador()) {
-
 
497
            // primeiro direcional após B ainda é vendedor
-
 
498
            return new ResultadoPadrao(null, idxRef);
-
 
-
 
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;
499
        }
469
        }
500
470
501
        // 0.3 – B tem o MENOR fundo da pernada vendedora
-
 
502
        BigDecimal fundoRef = ref.getMinima();
-
 
503
        for (int i = inicioTrendVenda; i <= idxRef; i++) {
-
 
504
            Candle c = candles.get(i);
-
 
505
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), fundoRef)) {
-
 
506
                return new ResultadoPadrao(null, idxRef);
-
 
507
            }
-
 
508
        }
-
 
509
-
 
510
        int idxPrimeiroComprador = idxPrimeiroDirecionalAposRef;
-
 
511
-
 
512
        // 1) Encontrar G1 (comprador que rompe topo do GR)
-
 
-
 
471
        // 3) G1 – primeiro comprador que rompe topo do candidatoRef
-
 
472
        Candle g1 = null;
513
        int idxG1 = -1;
473
        int idxG1 = -1;
514
        Candle g1 = null;
-
 
515
-
 
516
        for (int i = idxPrimeiroComprador; i < n; i++) {
-
 
-
 
474
        for (int i = idxCandidatoRef + 1; i < n; i++) {
517
            Candle c = candles.get(i);
475
            Candle c = candles.get(i);
518
-
 
519
            // OUTSIDE em relação ao GR (até G3)
-
 
520
            if (isOutsideReferencia(ref, c)) {
-
 
521
                lastIndex = i;
-
 
522
                log(String.format(
-
 
523
                        "RefV[%d]: OUTSIDE em [%d] durante busca de G1. Padrão descartado.",
-
 
524
                        idxRef, i));
-
 
525
                return new ResultadoPadrao(null, lastIndex);
-
 
526
            }
-
 
-
 
476
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
-
 
477
                continue;
527
478
528
            if (!c.isCandleComprador()) {
479
            if (!c.isCandleComprador()) {
529
                lastIndex = i - 1;
480
                lastIndex = i - 1;
530
                return new ResultadoPadrao(null, lastIndex);
481
                return new ResultadoPadrao(null, lastIndex);
531
            }
482
            }
532
483
533
            lastIndex = i;
484
            lastIndex = i;
534
-
 
535
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), ref.getMaxima())) {
-
 
-
 
485
            if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) {
-
 
486
                g1 = c;
536
                idxG1 = i;
487
                idxG1 = i;
537
                g1 = c;
-
 
538
                break;
488
                break;
539
            }
489
            }
540
        }
490
        }
-
 
491
        if (g1 == null) return new ResultadoPadrao(null, lastIndex);
541
492
542
        if (idxG1 == -1) {
-
 
543
            return new ResultadoPadrao(null, lastIndex);
-
 
544
        }
-
 
-
 
493
        Candle gr = candidatoRef;
-
 
494
        int idxGR = idxCandidatoRef;
-
 
495
        log(String.format("GR vendedor em [%d], G1 (comprador) em [%d]", idxGR, idxG1));
545
496
546
        log(String.format("RefV[%d] (GR) => G1 (comprador) em [%d]", idxRef, idxG1));
-
 
-
 
497
        // 4) G2 – nova tendência vendedora com fechamento dentro da região do GR
-
 
498
        Candle g2 = null;
-
 
499
        int idxG2 = -1;
547
500
548
        // 2) Encontrar G2 (retorno vendedor à região do GR)
-
 
549
        int idxPrimeiroVendedor = -1;
501
        int idxPrimeiroVendedor = -1;
550
        for (int i = idxG1 + 1; i < n; i++) {
502
        for (int i = idxG1 + 1; i < n; i++) {
551
            Candle c = candles.get(i);
503
            Candle c = candles.get(i);
552
-
 
553
            // OUTSIDE em relação ao GR (até G3)
-
 
554
            if (isOutsideReferencia(ref, c)) {
-
 
555
                lastIndex = i;
-
 
556
                log(String.format(
-
 
557
                        "RefV[%d]: OUTSIDE em [%d] antes da tendência de G2. Padrão descartado.",
-
 
558
                        idxRef, i));
-
 
559
                return new ResultadoPadrao(null, lastIndex);
-
 
560
            }
-
 
561
-
 
562
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
-
 
504
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
563
                continue;
505
                continue;
564
            }
-
 
565
506
566
            if (c.isCandleVendedor()) {
507
            if (c.isCandleVendedor()) {
567
                idxPrimeiroVendedor = i;
508
                idxPrimeiroVendedor = i;
568
                break;
509
                break;
569
            } else {
510
            } else {
570
                lastIndex = i;
511
                lastIndex = i;
571
            }
512
            }
572
        }
513
        }
-
 
514
        if (idxPrimeiroVendedor == -1) return new ResultadoPadrao(null, lastIndex);
573
515
574
        if (idxPrimeiroVendedor == -1) {
-
 
575
            return new ResultadoPadrao(null, lastIndex);
-
 
576
        }
-
 
577
-
 
578
        boolean rompeuFundoRef = false;
-
 
579
        Candle ultimoVendedorTrend = null;
-
 
580
        int idxUltimoVendedorTrend = -1;
-
 
-
 
516
        Candle ultimoVendedor = null;
-
 
517
        int idxUltimoVendedor = -1;
-
 
518
        boolean rompeuFundoGR = false;
581
519
582
        for (int i = idxPrimeiroVendedor; i < n; i++) {
520
        for (int i = idxPrimeiroVendedor; i < n; i++) {
583
            Candle c = candles.get(i);
521
            Candle c = candles.get(i);
584
522
585
            // OUTSIDE em relação ao GR (até G3)
-
 
586
            if (isOutsideReferencia(ref, c)) {
-
 
-
 
523
            if (isOutside(candles, i)) {
587
                lastIndex = i;
524
                lastIndex = i;
588
                log(String.format(
-
 
589
                        "RefV[%d]: OUTSIDE em [%d] durante formação de G2. Padrão descartado.",
-
 
590
                        idxRef, i));
-
 
-
 
525
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (vendedor).", idxGR, i));
591
                return new ResultadoPadrao(null, lastIndex);
526
                return new ResultadoPadrao(null, lastIndex);
592
            }
527
            }
593
528
594
            if (!c.isCandleVendedor()) {
-
 
595
                break;
-
 
596
            }
-
 
-
 
529
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
-
 
530
                continue;
597
531
598
            ultimoVendedorTrend = c;
-
 
599
            idxUltimoVendedorTrend = i;
-
 
600
            lastIndex = i;
-
 
601
-
 
602
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), ref.getMinima())) {
-
 
603
                rompeuFundoRef = true;
-
 
-
 
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;
604
                break;
544
                break;
605
            }
545
            }
606
        }
546
        }
607
547
608
        if (rompeuFundoRef || idxUltimoVendedorTrend == -1) {
-
 
-
 
548
        if (rompeuFundoGR || ultimoVendedor == null)
609
            return new ResultadoPadrao(null, lastIndex);
549
            return new ResultadoPadrao(null, lastIndex);
610
        }
-
 
611
550
612
        boolean fechamentoDentroRegiaoRef =
-
 
613
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoVendedorTrend.getFechamento(), ref.getMinima()) &&
-
 
614
                BigDecimalUtils.ehMenorOuIgualQue(ultimoVendedorTrend.getFechamento(), ref.getMaxima());
-
 
-
 
551
        boolean fechamentoDentroRegiaoGR =
-
 
552
                BigDecimalUtils.ehMaiorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMinima()) &&
-
 
553
                BigDecimalUtils.ehMenorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMaxima());
615
554
616
        if (!fechamentoDentroRegiaoRef) {
-
 
-
 
555
        if (!fechamentoDentroRegiaoGR)
617
            return new ResultadoPadrao(null, lastIndex);
556
            return new ResultadoPadrao(null, lastIndex);
618
        }
-
 
619
557
620
        Candle g2 = ultimoVendedorTrend;
-
 
621
        int idxG2 = idxUltimoVendedorTrend;
-
 
622
        log(String.format("RefV[%d], G1[%d] => G2 (vendedor) em [%d]", idxRef, idxG1, idxG2));
-
 
-
 
558
        g2 = ultimoVendedor;
-
 
559
        idxG2 = idxUltimoVendedor;
-
 
560
        log(String.format("GR[%d], G1[%d] => G2 (vendedor) em [%d]", idxGR, idxG1, idxG2));
-
 
561
-
 
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;
623
565
624
        // 3) Encontrar G3 (comprador rompendo topo de G2, fundo >= fundo do GR)
-
 
625
        int idxPrimeiroCompradorAposG2 = -1;
566
        int idxPrimeiroCompradorAposG2 = -1;
626
        for (int i = idxG2 + 1; i < n; i++) {
567
        for (int i = idxG2 + 1; i < n; i++) {
627
            Candle c = candles.get(i);
568
            Candle c = candles.get(i);
628
-
 
629
            // OUTSIDE em relação ao GR (até G3)
-
 
630
            if (isOutsideReferencia(ref, c)) {
-
 
631
                lastIndex = i;
-
 
632
                log(String.format(
-
 
633
                        "RefV[%d]: OUTSIDE em [%d] antes da sequência compradora de G3. Padrão descartado.",
-
 
634
                        idxRef, i));
-
 
635
                return new ResultadoPadrao(null, lastIndex);
-
 
636
            }
-
 
637
-
 
638
            if (!c.isCandleComprador() && !c.isCandleVendedor()) {
-
 
-
 
569
            if (isInside(candles, i) || (!c.isCandleComprador() && !c.isCandleVendedor()))
639
                continue;
570
                continue;
640
            }
-
 
641
571
642
            if (c.isCandleComprador()) {
572
            if (c.isCandleComprador()) {
643
                idxPrimeiroCompradorAposG2 = i;
573
                idxPrimeiroCompradorAposG2 = i;
644
                break;
574
                break;
645
            } else {
575
            } else {
646
                lastIndex = i;
576
                lastIndex = i;
647
                // esperado comprador; padrão segue apenas até G2
-
 
648
                return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
-
 
577
                return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
649
            }
578
            }
650
        }
579
        }
651
-
 
652
        if (idxPrimeiroCompradorAposG2 == -1) {
-
 
653
            // não teve sequência adequada após G2
-
 
654
            return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
655
        }
-
 
656
-
 
657
        int idxG3 = -1;
-
 
658
        Candle g3 = null;
-
 
-
 
580
        if (idxPrimeiroCompradorAposG2 == -1)
-
 
581
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
659
582
660
        for (int i = idxPrimeiroCompradorAposG2; i < n; i++) {
583
        for (int i = idxPrimeiroCompradorAposG2; i < n; i++) {
661
            Candle c = candles.get(i);
584
            Candle c = candles.get(i);
662
585
663
            // OUTSIDE em relação ao GR (ATÉ IDENTIFICAR G3)
-
 
664
            if (isOutsideReferencia(ref, c)) {
-
 
-
 
586
            if (isOutside(candles, i)) {
665
                lastIndex = i;
587
                lastIndex = i;
666
                log(String.format(
-
 
667
                        "RefV[%d]: OUTSIDE em [%d] durante formação de G3. Padrão descartado.",
-
 
668
                        idxRef, i));
-
 
-
 
588
                log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (comprador).", idxGR, i));
-
 
589
                return new ResultadoPadrao(null, lastIndex);
-
 
590
            }
-
 
591
-
 
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));
669
                return new ResultadoPadrao(null, lastIndex);
599
                return new ResultadoPadrao(null, lastIndex);
670
            }
600
            }
671
601
672
            if (!c.isCandleComprador()) {
602
            if (!c.isCandleComprador()) {
673
                lastIndex = i - 1;
603
                lastIndex = i - 1;
674
                break;
604
                break;
675
            }
605
            }
676
606
677
            lastIndex = i;
607
            lastIndex = i;
678
-
 
679
            boolean rompeTopoG2 = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima());
608
            boolean rompeTopoG2 = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima());
680
            boolean fundoMaiorOuIgualRef = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), ref.getMinima());
-
 
681
-
 
682
            if (rompeTopoG2 && fundoMaiorOuIgualRef) {
-
 
-
 
609
            boolean fundoMaiorOuIgualGR = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), gr.getMinima());
-
 
610
            if (rompeTopoG2 && fundoMaiorOuIgualGR) {
-
 
611
                g3 = c;
683
                idxG3 = i;
612
                idxG3 = i;
684
                g3 = c;
-
 
685
                break;
613
                break;
686
            }
614
            }
687
        }
615
        }
688
616
689
        if (idxG3 == -1 || g3 == null) {
-
 
690
            // padrão só até G2
-
 
691
            return criarResultadoParcialComG2(ref, g1, g2, lastIndex);
-
 
692
        }
-
 
-
 
617
        if (g3 == null)
-
 
618
            return criarResultadoParcialComG2(gr, g1, g2, lastIndex);
693
619
694
        log(String.format("RefV[%d], G1[%d], G2[%d] => G3 (comprador) em [%d]",
-
 
695
                idxRef, idxG1, idxG2, idxG3));
-
 
-
 
620
        log(String.format("GR[%d], G1[%d], G2[%d] => G3 (comprador) em [%d]",
-
 
621
                idxGR, idxG1, idxG2, idxG3));
696
622
697
        // 4) G4 – próximo candle rompe fundo do GR
-
 
-
 
623
        // 6) G4 – próximo candle rompe fundo do GR
698
        Candle g4 = null;
624
        Candle g4 = null;
699
        int proxIdx = idxG3 + 1;
625
        int proxIdx = idxG3 + 1;
700
        if (proxIdx < n) {
626
        if (proxIdx < n) {
701
            Candle c = candles.get(proxIdx);
627
            Candle c = candles.get(proxIdx);
702
            lastIndex = proxIdx;
628
            lastIndex = proxIdx;
703
-
 
704
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), ref.getMinima())) {
-
 
-
 
629
            if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) {
705
                g4 = c;
630
                g4 = c;
706
                log(String.format("RefV[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
-
 
707
                        idxRef, idxG1, idxG2, idxG3, proxIdx));
-
 
-
 
631
                log(String.format("GR[%d], G1[%d], G2[%d], G3[%d] => G4 em [%d]",
-
 
632
                        idxGR, idxG1, idxG2, idxG3, proxIdx));
708
            } else {
633
            } else {
709
                log(String.format("RefV[%d], G1[%d], G2[%d], G3[%d]: próximo candle [%d] não é G4.",
-
 
710
                        idxRef, idxG1, idxG2, idxG3, proxIdx));
-
 
-
 
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));
711
            }
636
            }
712
        }
637
        }
713
638
714
        PadraoGatilho padrao = new PadraoGatilho();
639
        PadraoGatilho padrao = new PadraoGatilho();
715
        padrao.setReferencia(ref);
-
 
-
 
640
        padrao.setReferencia(gr);
716
        padrao.setGatilho1(g1);
641
        padrao.setGatilho1(g1);
717
        padrao.setGatilho2(g2);
642
        padrao.setGatilho2(g2);
718
        padrao.setGatilho3(g3);
643
        padrao.setGatilho3(g3);
719
        padrao.setGatilho4(g4);
644
        padrao.setGatilho4(g4);
720
645
721
        return new ResultadoPadrao(padrao, lastIndex);
646
        return new ResultadoPadrao(padrao, lastIndex);
722
    }
-
 
723
-
 
724
-
 
725
    // =====================================================================
-
 
726
    // HELPER – Candle outside em relação ao GR
-
 
727
    // =====================================================================
-
 
728
-
 
729
    /**
-
 
730
     * Outside em relação ao GR:
-
 
731
     *  - atual.max > GR.max e atual.min < GR.min
-
 
732
     */
-
 
733
    private boolean isOutsideReferencia(Candle referencia, Candle atual) {
-
 
734
        if (referencia == null || atual == null) return false;
-
 
735
        return BigDecimalUtils.ehMaiorQue(atual.getMaxima(), referencia.getMaxima())
-
 
736
                && BigDecimalUtils.ehMenorQue(atual.getMinima(), referencia.getMinima());
-
 
737
    }
647
    }
738
648
739
    // =====================================================================
649
    // =====================================================================
740
    // MODO TEMPO REAL (candle a candle)
-
 
-
 
650
    // MODO TEMPO REAL
741
    // =====================================================================
651
    // =====================================================================
742
652
743
    /**
-
 
744
     * Reinicia o estado do modo tempo real.
-
 
745
     */
-
 
746
    public void resetTempoReal() {
653
    public void resetTempoReal() {
747
        this.idxProximaAnaliseTempoReal = 0;
654
        this.idxProximaAnaliseTempoReal = 0;
748
    }
655
    }
749
656
750
    /**
-
 
751
     * Deve ser chamado SEMPRE que um novo candle for adicionado à lista.
-
 
752
     *
-
 
753
     * Exemplo:
-
 
754
     *   candles.add(novoCandle);
-
 
755
     *   PadraoGatilho padrao = detector.processarCandleTempoReal(candles);
-
 
756
     *
-
 
757
     *   if (padrao != null) {
-
 
758
     *       // padrão completo encontrado
-
 
759
     *   }
-
 
760
     */
-
 
761
    public PadraoGatilho processarCandleTempoReal(List<Candle> candles) {
657
    public PadraoGatilho processarCandleTempoReal(List<Candle> candles) {
762
        int n = candles.size();
658
        int n = candles.size();
763
        if (n < 4) {
-
 
764
            return null;
-
 
765
        }
-
 
-
 
659
        if (n < 4) return null;
766
660
767
        while (idxProximaAnaliseTempoReal < n - 3) {
661
        while (idxProximaAnaliseTempoReal < n - 3) {
768
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxProximaAnaliseTempoReal);
662
            ResultadoPadrao resultado = detectarPadraoAPartir(candles, idxProximaAnaliseTempoReal);
769
663
770
            if (resultado == null) {
664
            if (resultado == null) {