Subversion Repositories Integrator Subversion

Rev

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

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