Subversion Repositories Integrator Subversion

Rev

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

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