Rev 773 | Rev 775 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 773 | Rev 774 | ||
|---|---|---|---|
| Line 12... | Line 12... | ||
| 12 | *
|
12 | *
|
| 13 | * IMPORTANTE:
|
13 | * IMPORTANTE:
|
| 14 | * - Este detector FINALIZA o padrão assim que identifica o G3.
|
14 | * - Este detector FINALIZA o padrão assim que identifica o G3.
|
| 15 | * - O G4 será identificado apenas na camada de sinais de trade, fora desta classe.
|
15 | * - O G4 será identificado apenas na camada de sinais de trade, fora desta classe.
|
| 16 | *
|
16 | *
|
| 17 | * REGRAS IMPLEMENTADAS (com GR DINÂMICO invertido)
|
- | |
| 18 | * ================================================
|
- | |
| - | 17 | * REGRAS IMPLEMENTADAS (versão alinhada)
|
|
| - | 18 | * ======================================
|
|
| 19 | *
|
19 | *
|
| 20 | * COMPRADOR
|
- | |
| 21 | * ---------
|
- | |
| 22 | * Ponto de partida: um candle COMPRADOR é candidato à referência.
|
- | |
| - | 20 | * 1) PADRÃO COMPRADOR
|
|
| - | 21 | * --------------------
|
|
| - | 22 | * Ponto de partida:
|
|
| - | 23 | * - Um candle COMPRADOR é tomado como CANDIDATO À REFERÊNCIA.
|
|
| 23 | *
|
24 | *
|
| 24 | * G1:
|
- | |
| 25 | * - Enquanto não houver G1:
|
- | |
| 26 | * * O candidato à referência pode ser atualizado dinamicamente:
|
- | |
| 27 | * QUALQUER candle posterior, antes do G1, que fizer uma MÁXIMA
|
- | |
| 28 | * MAIOR que o candidato atual passa a ser o novo candidato.
|
- | |
| 29 | * * Se algum candle VENDEDOR subsequente ROMPER o FUNDO
|
- | |
| 30 | * do candle candidato à referência (mínima < mínima do candidato),
|
- | |
| 31 | * esse candle será o gatilho tipo 1 [G1] e o candidato vigente
|
- | |
| 32 | * torna-se o candle referência [GR].
|
- | |
| - | 25 | * GR DINÂMICO (compra):
|
|
| - | 26 | * - Enquanto NÃO houver G1:
|
|
| - | 27 | * * Qualquer candle DIRECIONAL posterior (comprador ou vendedor)
|
|
| - | 28 | * que fizer uma MÁXIMA MAIOR que a do candidato atual
|
|
| - | 29 | * passa a ser o NOVO candidato à referência.
|
|
| 33 | *
|
30 | *
|
| 34 | * G2:
|
- | |
| 35 | * - Após o G1 (com GR definido):
|
- | |
| 36 | * * Assim que tiver nova tendência COMPRADORA:
|
- | |
| 37 | * - O último candle dessa sequência compradora deve ter o FECHAMENTO
|
- | |
| 38 | * dentro da região total (mínima..máxima) do GR.
|
- | |
| 39 | * - Se mudar para vendedor sem nenhum candle fechar dentro da região
|
- | |
| 40 | * do GR → descarta o padrão.
|
- | |
| 41 | * * Se qualquer candle (após o GR) romper o TOPO do GR
|
- | |
| 42 | * (máxima > máxima GR) → descarta o padrão.
|
- | |
| 43 | * * Se válido, esse último comprador é o gatilho tipo 2 [G2].
|
- | |
| - | 31 | * G1 (compra):
|
|
| - | 32 | * - Pode ser QUALQUER candle vendedor subsequente (não precisa ser o primeiro),
|
|
| - | 33 | * desde que ROMPA o FUNDO do candidato:
|
|
| - | 34 | * mínima(candle vendedor) < mínima(candidatoRef).
|
|
| - | 35 | * - No momento em que isso ocorrer:
|
|
| - | 36 | * * Esse candle vendedor é o G1.
|
|
| - | 37 | * * O candidato vigente passa a ser o GR (gatilho referência).
|
|
| 44 | *
|
38 | *
|
| 45 | * G3:
|
- | |
| - | 39 | * G2 (compra):
|
|
| - | 40 | * - Após o G1, com GR definido:
|
|
| - | 41 | * * Procurar uma nova tendência COMPRADORA:
|
|
| - | 42 | * - Achar o primeiro candle COMPRADOR após o G1.
|
|
| - | 43 | * - Seguir a sequência compradora (ignorando INSIDE e neutros)
|
|
| - | 44 | * até aparecer candle vendedor.
|
|
| - | 45 | * * O ÚLTIMO candle dessa tendência compradora será candidato a G2.
|
|
| - | 46 | * * Para ser G2:
|
|
| - | 47 | * - O FECHAMENTO deve ficar DENTRO da região [mínGR, máxGR].
|
|
| - | 48 | * * Se mudar para vendedor sem nenhum candle fechar dentro da região do GR:
|
|
| - | 49 | * - descarta o padrão.
|
|
| - | 50 | * - Regra global comprador:
|
|
| - | 51 | * * Se qualquer candle, após o GR, ROMPER o TOPO do GR
|
|
| - | 52 | * (máxima > máximaGR) em qualquer ponto antes da conclusão:
|
|
| - | 53 | * - descarta o padrão.
|
|
| - | 54 | *
|
|
| - | 55 | * G3 (compra) – NOVA REGRA:
|
|
| 46 | * - Após o G2:
|
56 | * - Após o G2:
|
| 47 | * * O próximo direcional deve ser VENDEDOR; se for comprador,
|
- | |
| 48 | * o padrão vai apenas até G2 (parcial).
|
- | |
| 49 | * * Numa sequência vendedora:
|
- | |
| 50 | * - Se algum candle VENDEDOR posterior ROMPER o FUNDO do G2
|
- | |
| 51 | * (mínima < mínima G2) e tiver TOPO <= topo do GR
|
- | |
| 52 | * (máxima <= máxima GR), sem mudança de tendência
|
- | |
| 53 | * (sem compradores no meio), será o gatilho tipo 3 [G3].
|
- | |
| - | 57 | * * Se ALGUM candle vendedor subsequente:
|
|
| - | 58 | * - romper o FUNDO do G2 (mínima < mínimaG2) E
|
|
| - | 59 | * - tiver TOPO menor ou igual ao TOPO do GR (máxima ≤ máximaGR)
|
|
| - | 60 | * então esse candle será o G3.
|
|
| - | 61 | * - Não precisa ser o primeiro vendedor após o G2; pode haver mudança de tendência,
|
|
| - | 62 | * candles inside, etc., desde que a regra acima seja satisfeita e não ocorra:
|
|
| - | 63 | * * outside antes do G3, ou
|
|
| - | 64 | * * rompimento do topo do GR (regra global).
|
|
| - | 65 | * - Ao identificar G3, o padrão é finalizado.
|
|
| 54 | *
|
66 | *
|
| 55 | * Regra global comprador:
|
- | |
| 56 | * - Se algum candle posterior ao GR ROMPER o TOPO do GR
|
- | |
| 57 | * (máxima > máxima GR), desconsiderar o padrão.
|
- | |
| - | 67 | * 2) PADRÃO VENDEDOR
|
|
| - | 68 | * -------------------
|
|
| - | 69 | * Ponto de partida:
|
|
| - | 70 | * - Um candle VENDEDOR é tomado como CANDIDATO À REFERÊNCIA.
|
|
| 58 | *
|
71 | *
|
| 59 | * -----------------------------------------------------------
|
- | |
| - | 72 | * GR DINÂMICO (venda):
|
|
| - | 73 | * - Enquanto NÃO houver G1:
|
|
| - | 74 | * * Qualquer candle DIRECIONAL posterior (comprador ou vendedor)
|
|
| - | 75 | * que fizer uma MÍNIMA MENOR que a do candidato atual
|
|
| - | 76 | * passa a ser o NOVO candidato à referência.
|
|
| 60 | *
|
77 | *
|
| 61 | * VENDEDOR
|
- | |
| 62 | * --------
|
- | |
| 63 | * Ponto de partida: um candle VENDEDOR é candidato à referência.
|
- | |
| 64 | *
|
- | |
| 65 | * G1:
|
- | |
| 66 | * - Enquanto não houver G1:
|
- | |
| 67 | * * O candidato à referência pode ser atualizado dinamicamente:
|
- | |
| 68 | * QUALQUER candle posterior, antes do G1, que fizer uma MÍNIMA
|
- | |
| 69 | * MENOR que a do candidato atual passa a ser o novo candidato.
|
- | |
| 70 | * * Se algum candle COMPRADOR subsequente ROMPER o TOPO
|
- | |
| 71 | * do candidato (máxima > máxima do candidato), esse candle
|
- | |
| 72 | * será o gatilho tipo 1 [G1] e o candidato vigente torna-se o GR.
|
- | |
| - | 78 | * G1 (venda):
|
|
| - | 79 | * - Pode ser QUALQUER candle comprador subsequente (não precisa ser o primeiro),
|
|
| - | 80 | * desde que ROMPA o TOPO do candidato:
|
|
| - | 81 | * máxima(candle comprador) > máxima(candidatoRef).
|
|
| - | 82 | * - No momento em que isso ocorrer:
|
|
| - | 83 | * * Esse candle comprador é o G1.
|
|
| - | 84 | * * O candidato vigente passa a ser o GR (gatilho referência).
|
|
| 73 | *
|
85 | *
|
| 74 | * Regra global vendedor:
|
86 | * Regra global vendedor:
|
| 75 | * - Se algum candle posterior ao GR ROMPER o FUNDO do GR
|
- | |
| 76 | * (mínima < mínima GR), desconsiderar o padrão.
|
- | |
| - | 87 | * - Se qualquer candle posterior ao GR ROMPER o FUNDO do GR
|
|
| - | 88 | * (mínima < mínimaGR) em qualquer momento antes da conclusão:
|
|
| - | 89 | * - descarta o padrão.
|
|
| 77 | *
|
90 | *
|
| 78 | * G2:
|
- | |
| 79 | * - Após o G1 (com GR definido):
|
- | |
| 80 | * * Assim que tiver nova tendência VENDEDORA:
|
- | |
| 81 | * - O último candle dessa sequência vendedora deve ter FECHAMENTO
|
- | |
| 82 | * dentro da região total (mínima..máxima) do GR.
|
- | |
| 83 | * - Se mudar para comprador sem nenhum candle fechar dentro
|
- | |
| 84 | * da região do GR → descarta o padrão.
|
- | |
| 85 | * * Se romper o FUNDO do GR em qualquer momento após o GR
|
- | |
| 86 | * → descarta o padrão.
|
- | |
| 87 | * * Se válido, esse último vendedor é o gatilho tipo 2 [G2].
|
- | |
| - | 91 | * G2 (venda):
|
|
| - | 92 | * - Após o G1, com GR definido:
|
|
| - | 93 | * * Procurar uma nova tendência VENDEDORA:
|
|
| - | 94 | * - Achar o primeiro VENDEDOR após o G1.
|
|
| - | 95 | * - Seguir a sequência vendedora (ignorando INSIDE e neutros)
|
|
| - | 96 | * até aparecer candle comprador.
|
|
| - | 97 | * * O ÚLTIMO candle dessa tendência vendedora será candidato a G2.
|
|
| - | 98 | * * Para ser G2:
|
|
| - | 99 | * - O FECHAMENTO deve ficar DENTRO da região [mínGR, máxGR].
|
|
| - | 100 | * * Se mudar para comprador sem nenhum candle fechar dentro da região do GR:
|
|
| - | 101 | * - descarta o padrão.
|
|
| - | 102 | * * Se durante essa fase algum candle romper o FUNDO do GR
|
|
| - | 103 | * (mínima < mínimaGR) → descarta o padrão (regra global).
|
|
| 88 | *
|
104 | *
|
| 89 | * G3:
|
- | |
| - | 105 | * G3 (venda) – NOVA REGRA:
|
|
| 90 | * - Após o G2:
|
106 | * - Após o G2:
|
| 91 | * * O próximo direcional deve ser COMPRADOR; se for vendedor,
|
- | |
| 92 | * o padrão vai até G2 (parcial).
|
- | |
| 93 | * * Numa sequência compradora:
|
- | |
| 94 | * - Se algum COMPRADOR posterior ROMPER o TOPO de G2
|
- | |
| 95 | * (máxima > máxima G2) e tiver FUNDO >= fundo do GR
|
- | |
| 96 | * (mínima >= mínima GR), sem mudança de tendência
|
- | |
| 97 | * (sem vendedores no meio), será o gatilho tipo 3 [G3].
|
- | |
| - | 107 | * * Se ALGUM candle comprador subsequente:
|
|
| - | 108 | * - romper o TOPO do G2 (máxima > máximaG2) E
|
|
| - | 109 | * - tiver FUNDO maior ou igual ao FUNDO do GR (mínima ≥ mínimaGR)
|
|
| - | 110 | * então esse candle será o G3.
|
|
| - | 111 | * - Não precisa ser o primeiro comprador após o G2; pode haver mudança de tendência,
|
|
| - | 112 | * candles inside, etc., desde que a regra acima seja satisfeita e não ocorra:
|
|
| - | 113 | * * outside antes do G3, ou
|
|
| - | 114 | * * rompimento do fundo do GR (regra global).
|
|
| - | 115 | * - Ao identificar G3, o padrão é finalizado.
|
|
| 98 | *
|
116 | *
|
| 99 | * Regra global adicional (texto original repetia):
|
- | |
| 100 | * - Mantida a regra principal: vendedor é invalidado
|
- | |
| 101 | * quando rompem o FUNDO do GR.
|
- | |
| - | 117 | * 3) OUTSIDE (ambos os lados)
|
|
| - | 118 | * ---------------------------
|
|
| - | 119 | * - Candle cuja:
|
|
| - | 120 | * máxima > máxima(candle anterior)
|
|
| - | 121 | * E mínima < mínima(candle anterior).
|
|
| - | 122 | * - Se houver OUTSIDE em qualquer estágio antes de G3:
|
|
| - | 123 | * - o padrão atual (GR/G1/G2) é descartado.
|
|
| 102 | *
|
124 | *
|
| 103 | * -----------------------------------------------------------
|
- | |
| 104 | *
|
- | |
| 105 | * OUTSIDE (ambos os lados):
|
- | |
| 106 | * - Candle cuja máxima > máxima do candle anterior
|
- | |
| 107 | * E mínima < mínima do candle anterior.
|
- | |
| 108 | * - Se houver um OUTSIDE antes de identificar o G3:
|
- | |
| 109 | * * Desconsidera o padrão atual (GR+G1+G2) e recomeça
|
- | |
| 110 | * a busca a partir do GR (na prática, aborta o padrão).
|
- | |
| 111 | *
|
- | |
| 112 | * OBS:
|
- | |
| 113 | * - Os gatilhos devem seguir ORDEM cronológica:
|
- | |
| 114 | * G1 -> G2 -> G3, sempre em candles diferentes.
|
- | |
| - | 125 | * 4) ORDEM CRONOLÓGICA
|
|
| - | 126 | * ---------------------
|
|
| - | 127 | * - Sempre GR -> G1 -> G2 -> G3.
|
|
| - | 128 | * - Todos em candles diferentes, na ordem do tempo.
|
|
| 115 | */
|
129 | */
|
| 116 | public class DetectorGatilhos { |
130 | public class DetectorGatilhos { |
| 117 | 131 | ||
| 118 | private final boolean logAtivo; |
132 | private final boolean logAtivo; |
| 119 | private int idxProximaAnaliseTempoReal = 0; |
133 | private int idxProximaAnaliseTempoReal = 0; |
| Line 130... | Line 144... | ||
| 130 | if (logAtivo) { |
144 | if (logAtivo) { |
| 131 | System.out.println(msg); |
145 | System.out.println(msg); |
| 132 | }
|
146 | }
|
| 133 | }
|
147 | }
|
| 134 | 148 | ||
| 135 | // -----------------------------------------------------------------
|
- | |
| - | 149 | // -------------------------------------------------------------
|
|
| 136 | // Estrutura interna de retorno
|
150 | // Estrutura interna de retorno
|
| 137 | // -----------------------------------------------------------------
|
- | |
| - | 151 | // -------------------------------------------------------------
|
|
| 138 | private static class ResultadoPadrao { |
152 | private static class ResultadoPadrao { |
| 139 | PadraoGatilho padrao;
|
153 | PadraoGatilho padrao;
|
| 140 | int lastIndex; |
154 | int lastIndex; |
| 141 | 155 | ||
| 142 | ResultadoPadrao(PadraoGatilho padrao, int lastIndex) { |
156 | ResultadoPadrao(PadraoGatilho padrao, int lastIndex) { |
| 143 | this.padrao = padrao; |
157 | this.padrao = padrao; |
| 144 | this.lastIndex = lastIndex; |
158 | this.lastIndex = lastIndex; |
| 145 | }
|
159 | }
|
| 146 | }
|
160 | }
|
| 147 | 161 | ||
| 148 | private ResultadoPadrao criarResultadoParcialComG2(Candle ref, |
- | |
| - | 162 | private ResultadoPadrao criarResultadoParcialComG2(Candle gr, |
|
| 149 | Candle g1, |
163 | Candle g1, |
| 150 | Candle g2, |
164 | Candle g2, |
| 151 | int lastIndex) { |
165 | int lastIndex) { |
| 152 | PadraoGatilho padrao = new PadraoGatilho(); |
166 | PadraoGatilho padrao = new PadraoGatilho(); |
| 153 | padrao.setReferencia(ref); |
- | |
| - | 167 | padrao.setReferencia(gr); |
|
| 154 | padrao.setGatilho1(g1); |
168 | padrao.setGatilho1(g1); |
| 155 | padrao.setGatilho2(g2); |
169 | padrao.setGatilho2(g2); |
| 156 | padrao.setGatilho3(null); |
170 | padrao.setGatilho3(null); |
| 157 | padrao.setGatilho4(null); // G4 só na camada de trade |
171 | padrao.setGatilho4(null); // G4 só na camada de trade |
| 158 | return new ResultadoPadrao(padrao, lastIndex); |
172 | return new ResultadoPadrao(padrao, lastIndex); |
| Line 235... | Line 249... | ||
| 235 | }
|
249 | }
|
| 236 | 250 | ||
| 237 | int idxCandidatoRef = idxInicio; |
251 | int idxCandidatoRef = idxInicio; |
| 238 | 252 | ||
| 239 | // --------------------
|
253 | // --------------------
|
| 240 | // 1) Buscar G1 (vendedor rompe fundo do candidato)
|
- | |
| - | 254 | // 1) Buscar G1: QUALQUER vendedor subsequente que rompa o fundo do candidatoRef
|
|
| - | 255 | // (com GR dinâmico pela MÁXIMA antes de G1)
|
|
| 241 | // --------------------
|
256 | // --------------------
|
| 242 | Candle g1 = null; |
257 | Candle g1 = null; |
| 243 | int idxG1 = -1; |
258 | int idxG1 = -1; |
| 244 | boolean temGR = false; |
- | |
| 245 | Candle gr = null; |
259 | Candle gr = null; |
| 246 | int idxGR = -1; |
260 | int idxGR = -1; |
| 247 | 261 | ||
| 248 | for (int i = idxInicio + 1; i < n; i++) { |
262 | for (int i = idxInicio + 1; i < n; i++) { |
| 249 | Candle c = candles.get(i); |
263 | Candle c = candles.get(i); |
| 250 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| - | 264 | if (isInside(candles, i) || !isDirecional(c)) { |
|
| 251 | continue; |
265 | continue; |
| - | 266 | }
|
|
| 252 | 267 | ||
| 253 | // Outside antes de G3 (aqui ainda antes de G1)
|
- | |
| - | 268 | // OUTSIDE antes de G1 => descarta padrão
|
|
| 254 | if (isOutside(candles, i)) { |
269 | if (isOutside(candles, i)) { |
| 255 | lastIndex = i;
|
270 | lastIndex = i;
|
| 256 | log(String.format("Comprador: OUTSIDE em [%d] antes de G1. Abortando padrão.", i)); |
271 | log(String.format("Comprador: OUTSIDE em [%d] antes de G1. Abortando padrão.", i)); |
| 257 | return new ResultadoPadrao(null, lastIndex); |
272 | return new ResultadoPadrao(null, lastIndex); |
| 258 | }
|
273 | }
|
| 259 | 274 | ||
| 260 | // G1: vendedor rompe fundo do candidato
|
- | |
| - | 275 | // GR dinâmico (compra): atualiza candidatoRef pelo TOPO mais alto
|
|
| - | 276 | // enquanto não existir G1
|
|
| - | 277 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) { |
|
| - | 278 | candidatoRef = c;
|
|
| - | 279 | idxCandidatoRef = i;
|
|
| - | 280 | lastIndex = i;
|
|
| - | 281 | continue; |
|
| - | 282 | }
|
|
| - | 283 | ||
| - | 284 | // G1: QUALQUER vendedor subsequente que rompa o fundo do candidatoRef
|
|
| 261 | if (c.isCandleVendedor() |
285 | if (c.isCandleVendedor() |
| 262 | && BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) { |
286 | && BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) { |
| 263 | g1 = c;
|
287 | g1 = c;
|
| 264 | idxG1 = i;
|
288 | idxG1 = i;
|
| 265 | gr = candidatoRef;
|
289 | gr = candidatoRef;
|
| 266 | idxGR = idxCandidatoRef;
|
290 | idxGR = idxCandidatoRef;
|
| 267 | temGR = true; |
- | |
| 268 | lastIndex = i;
|
291 | lastIndex = i;
|
| 269 | break; |
292 | break; |
| 270 | }
|
- | |
| 271 | - | ||
| 272 | // GR DINÂMICO (compra): atualiza pelo TOPO mais alto antes de G1
|
- | |
| 273 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) { |
- | |
| 274 | candidatoRef = c;
|
- | |
| 275 | idxCandidatoRef = i;
|
- | |
| 276 | lastIndex = i;
|
- | |
| 277 | continue; |
- | |
| 278 | }
|
293 | }
|
| 279 | 294 | ||
| 280 | lastIndex = i;
|
295 | lastIndex = i;
|
| 281 | }
|
296 | }
|
| 282 | 297 | ||
| 283 | if (g1 == null || !temGR) { |
- | |
| - | 298 | if (g1 == null || gr == null) { |
|
| 284 | log(String.format("Comprador: não foi possível formar G1 a partir de idx %d.", idxInicio)); |
299 | log(String.format("Comprador: não foi possível formar G1 a partir de idx %d.", idxInicio)); |
| 285 | return new ResultadoPadrao(null, lastIndex); |
300 | return new ResultadoPadrao(null, lastIndex); |
| 286 | }
|
301 | }
|
| 287 | 302 | ||
| 288 | log(String.format("GR COMPRADOR em [%d], G1 (vendedor) em [%d].", idxGR, idxG1)); |
303 | log(String.format("GR COMPRADOR em [%d], G1 (vendedor) em [%d].", idxGR, idxG1)); |
| 289 | 304 | ||
| 290 | // --------------------
|
305 | // --------------------
|
| 291 | // 2) Buscar G2 (tendência compradora com fechamento dentro do GR)
|
- | |
| - | 306 | // 2) Buscar G2 – tendência COMPRADORA com fechamento dentro do GR
|
|
| 292 | // --------------------
|
307 | // --------------------
|
| 293 | Candle g2 = null; |
308 | Candle g2 = null; |
| 294 | int idxG2 = -1; |
309 | int idxG2 = -1; |
| 295 | 310 | ||
| 296 | int idxPrimeiroComprador = -1; |
311 | int idxPrimeiroComprador = -1; |
| 297 | for (int i = idxG1 + 1; i < n; i++) { |
312 | for (int i = idxG1 + 1; i < n; i++) { |
| 298 | Candle c = candles.get(i); |
313 | Candle c = candles.get(i); |
| 299 | if (isInside(candles, i) || !isDirecional(c)) |
314 | if (isInside(candles, i) || !isDirecional(c)) |
| 300 | continue; |
315 | continue; |
| 301 | 316 | ||
| 302 | // Outside antes de G3
|
- | |
| - | 317 | // OUTSIDE antes de G2 => descarta padrão
|
|
| 303 | if (isOutside(candles, i)) { |
318 | if (isOutside(candles, i)) { |
| 304 | lastIndex = idxGR;
|
319 | lastIndex = idxGR;
|
| 305 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da perna compradora de G2.", idxGR, i)); |
- | |
| - | 320 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da tendência compradora de G2.", idxGR, i)); |
|
| 306 | return new ResultadoPadrao(null, lastIndex); |
321 | return new ResultadoPadrao(null, lastIndex); |
| 307 | }
|
322 | }
|
| 308 | 323 | ||
| 309 | // Regra global: romper topo do GR invalida
|
- | |
| - | 324 | // Regra global comprador: se romper topo do GR => descarta
|
|
| 310 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
325 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
| 311 | lastIndex = i;
|
326 | lastIndex = i;
|
| 312 | log(String.format("GR[%d]: candle[%d] rompeu topo do GR antes de G2.", idxGR, i)); |
- | |
| - | 327 | log(String.format("GR[%d]: candle[%d] rompeu TOPO do GR antes/na formação de G2.", idxGR, i)); |
|
| 313 | return new ResultadoPadrao(null, lastIndex); |
328 | return new ResultadoPadrao(null, lastIndex); |
| 314 | }
|
329 | }
|
| 315 | 330 | ||
| 316 | if (c.isCandleComprador()) { |
331 | if (c.isCandleComprador()) { |
| 317 | idxPrimeiroComprador = i;
|
332 | idxPrimeiroComprador = i;
|
| Line 332... | Line 347... | ||
| 332 | for (int i = idxPrimeiroComprador; i < n; i++) { |
347 | for (int i = idxPrimeiroComprador; i < n; i++) { |
| 333 | Candle c = candles.get(i); |
348 | Candle c = candles.get(i); |
| 334 | if (isInside(candles, i) || !isDirecional(c)) |
349 | if (isInside(candles, i) || !isDirecional(c)) |
| 335 | continue; |
350 | continue; |
| 336 | 351 | ||
| 337 | // Outside durante G2
|
- | |
| - | 352 | // OUTSIDE durante G2 => descarta padrão
|
|
| 338 | if (isOutside(candles, i)) { |
353 | if (isOutside(candles, i)) { |
| 339 | lastIndex = idxGR;
|
354 | lastIndex = idxGR;
|
| 340 | log(String.format("GR[%d]: OUTSIDE em [%d] durante G2 (comprador).", idxGR, i)); |
- | |
| - | 355 | log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (comprador).", idxGR, i)); |
|
| 341 | return new ResultadoPadrao(null, lastIndex); |
356 | return new ResultadoPadrao(null, lastIndex); |
| 342 | }
|
357 | }
|
| 343 | 358 | ||
| 344 | // Regra global: romper topo do GR invalida
|
- | |
| - | 359 | // Regra global comprador: se romper topo do GR => descarta
|
|
| 345 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
360 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
| 346 | lastIndex = i;
|
361 | lastIndex = i;
|
| 347 | log(String.format("GR[%d]: candle[%d] rompeu topo do GR durante G2.", idxGR, i)); |
- | |
| - | 362 | log(String.format("GR[%d]: candle[%d] rompeu TOPO do GR durante G2.", idxGR, i)); |
|
| 348 | return new ResultadoPadrao(null, lastIndex); |
363 | return new ResultadoPadrao(null, lastIndex); |
| 349 | }
|
364 | }
|
| 350 | 365 | ||
| 351 | if (c.isCandleComprador()) { |
366 | if (c.isCandleComprador()) { |
| 352 | ultimoComprador = c;
|
367 | ultimoComprador = c;
|
| Line 367... | Line 382... | ||
| 367 | BigDecimalUtils.ehMaiorOuIgualQue(ultimoComprador.getFechamento(), gr.getMinima()) && |
382 | BigDecimalUtils.ehMaiorOuIgualQue(ultimoComprador.getFechamento(), gr.getMinima()) && |
| 368 | BigDecimalUtils.ehMenorOuIgualQue(ultimoComprador.getFechamento(), gr.getMaxima()); |
383 | BigDecimalUtils.ehMenorOuIgualQue(ultimoComprador.getFechamento(), gr.getMaxima()); |
| 369 | 384 | ||
| 370 | if (!fechamentoDentroRegiaoGR) { |
385 | if (!fechamentoDentroRegiaoGR) { |
| 371 | log(String.format( |
386 | log(String.format( |
| 372 | "GR[%d], G1[%d]: último comprador[%d] não fechou dentro da região do GR (fech=%s, faixa=[%s,%s]).",
|
- | |
| 373 | idxGR, idxG1, idxUltimoComprador, |
- | |
| 374 | ultimoComprador.getFechamento().toPlainString(), |
- | |
| 375 | gr.getMinima().toPlainString(), |
- | |
| 376 | gr.getMaxima().toPlainString() |
- | |
| - | 387 | "GR[%d], G1[%d]: último comprador[%d] NÃO fechou dentro da região do GR.",
|
|
| - | 388 | idxGR, idxG1, idxUltimoComprador |
|
| 377 | )); |
389 | )); |
| 378 | return new ResultadoPadrao(null, lastIndex); |
390 | return new ResultadoPadrao(null, lastIndex); |
| 379 | }
|
391 | }
|
| 380 | 392 | ||
| 381 | g2 = ultimoComprador;
|
393 | g2 = ultimoComprador;
|
| 382 | idxG2 = idxUltimoComprador;
|
394 | idxG2 = idxUltimoComprador;
|
| 383 | log(String.format("GR[%d], G1[%d] => G2 (comprador) em [%d].", idxGR, idxG1, idxG2)); |
395 | log(String.format("GR[%d], G1[%d] => G2 (comprador) em [%d].", idxGR, idxG1, idxG2)); |
| 384 | 396 | ||
| 385 | // --------------------
|
397 | // --------------------
|
| 386 | // 3) Buscar G3 (vendedor rompe fundo de G2 com topo <= topo GR)
|
- | |
| - | 398 | // 3) Buscar G3 – NOVA REGRA:
|
|
| - | 399 | // QUALQUER vendedor subsequente que rompa fundo de G2
|
|
| - | 400 | // e tenha topo <= topo do GR.
|
|
| 387 | // --------------------
|
401 | // --------------------
|
| 388 | Candle g3 = null; |
402 | Candle g3 = null; |
| 389 | int idxG3 = -1; |
403 | int idxG3 = -1; |
| 390 | 404 | ||
| 391 | int idxPrimeiroVendedorAposG2 = -1; |
- | |
| 392 | for (int i = idxG2 + 1; i < n; i++) { |
405 | for (int i = idxG2 + 1; i < n; i++) { |
| 393 | Candle c = candles.get(i); |
406 | Candle c = candles.get(i); |
| 394 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| - | 407 | if (!isDirecional(c) || isInside(candles, i)) { |
|
| 395 | continue; |
408 | continue; |
| - | 409 | }
|
|
| 396 | 410 | ||
| - | 411 | // OUTSIDE antes/durante busca de G3 => descarta padrão
|
|
| 397 | if (isOutside(candles, i)) { |
412 | if (isOutside(candles, i)) { |
| 398 | lastIndex = idxGR;
|
413 | lastIndex = idxGR;
|
| 399 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da sequência vendedora de G3.", idxGR, i)); |
- | |
| - | 414 | log(String.format("GR[%d]: OUTSIDE em [%d] durante busca de G3 (vendedor).", idxGR, i)); |
|
| 400 | return new ResultadoPadrao(null, lastIndex); |
415 | return new ResultadoPadrao(null, lastIndex); |
| 401 | }
|
416 | }
|
| 402 | 417 | ||
| 403 | // Regra global: romper topo do GR invalida
|
- | |
| - | 418 | // Regra global comprador: romper topo do GR => descarta
|
|
| 404 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
419 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
| 405 | lastIndex = i;
|
420 | lastIndex = i;
|
| 406 | log(String.format("GR[%d]: candle[%d] rompeu topo do GR antes da perna vendedora de G3.", idxGR, i)); |
- | |
| - | 421 | log(String.format("GR[%d]: candle[%d] rompeu TOPO do GR durante busca de G3.", idxGR, i)); |
|
| 407 | return new ResultadoPadrao(null, lastIndex); |
422 | return new ResultadoPadrao(null, lastIndex); |
| 408 | }
|
423 | }
|
| 409 | 424 | ||
| 410 | if (c.isCandleVendedor()) { |
425 | if (c.isCandleVendedor()) { |
| 411 | idxPrimeiroVendedorAposG2 = i;
|
- | |
| 412 | break; |
- | |
| 413 | } else { |
- | |
| 414 | // primeiro direcional após G2 é comprador → padrão só até G2
|
- | |
| 415 | lastIndex = i;
|
- | |
| 416 | log(String.format("GR[%d], G1[%d], G2[%d]: primeiro direcional após G2 é comprador (idx %d).", |
- | |
| 417 | idxGR, idxG1, idxG2, i)); |
- | |
| 418 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 419 | }
|
- | |
| 420 | }
|
- | |
| 421 | - | ||
| 422 | if (idxPrimeiroVendedorAposG2 == -1) { |
- | |
| 423 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve vendedor após G2.", idxGR, idxG1, idxG2)); |
- | |
| 424 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 425 | }
|
- | |
| 426 | - | ||
| 427 | for (int i = idxPrimeiroVendedorAposG2; i < n; i++) { |
- | |
| 428 | Candle c = candles.get(i); |
- | |
| 429 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| 430 | continue; |
- | |
| 431 | - | ||
| 432 | if (isOutside(candles, i)) { |
- | |
| 433 | lastIndex = idxGR;
|
- | |
| 434 | log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (vendedor).", idxGR, i)); |
- | |
| 435 | return new ResultadoPadrao(null, lastIndex); |
- | |
| 436 | }
|
- | |
| 437 | - | ||
| 438 | // Regra global: romper topo do GR invalida
|
- | |
| 439 | if (BigDecimalUtils.ehMaiorQue(c.getMaxima(), gr.getMaxima())) { |
- | |
| 440 | lastIndex = i;
|
- | |
| 441 | log(String.format("GR[%d]: candle[%d] rompeu topo do GR durante busca de G3.", idxGR, i)); |
- | |
| 442 | return new ResultadoPadrao(null, lastIndex); |
- | |
| 443 | }
|
- | |
| 444 | - | ||
| 445 | if (!c.isCandleVendedor()) { |
- | |
| 446 | // apareceu comprador antes de G3 → padrão só até G2
|
- | |
| 447 | lastIndex = i - 1; |
- | |
| 448 | log(String.format("GR[%d], G1[%d], G2[%d]: comprador em [%d] antes de G3.", idxGR, idxG1, idxG2, i)); |
- | |
| 449 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 450 | }
|
- | |
| 451 | - | ||
| 452 | lastIndex = i;
|
- | |
| 453 | boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima()); |
- | |
| 454 | boolean topoMenorOuIgualGR = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), gr.getMaxima()); |
- | |
| 455 | if (rompeFundoG2 && topoMenorOuIgualGR) { |
- | |
| 456 | g3 = c;
|
- | |
| 457 | idxG3 = i;
|
- | |
| 458 | break; |
- | |
| - | 426 | boolean rompeFundoG2 = BigDecimalUtils.ehMenorQue(c.getMinima(), g2.getMinima()); |
|
| - | 427 | boolean topoMenorOuIgualGR = BigDecimalUtils.ehMenorOuIgualQue(c.getMaxima(), gr.getMaxima()); |
|
| - | 428 | if (rompeFundoG2 && topoMenorOuIgualGR) { |
|
| - | 429 | g3 = c;
|
|
| - | 430 | idxG3 = i;
|
|
| - | 431 | lastIndex = i;
|
|
| - | 432 | break; |
|
| - | 433 | }
|
|
| 459 | }
|
434 | }
|
| 460 | }
|
435 | }
|
| 461 | 436 | ||
| 462 | if (g3 == null) { |
437 | if (g3 == null) { |
| 463 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve G3, padrão parcial.", idxGR, idxG1, idxG2)); |
- | |
| - | 438 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve G3 (comprador -> padrão parcial).", |
|
| - | 439 | idxGR, idxG1, idxG2)); |
|
| 464 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
440 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
| 465 | }
|
441 | }
|
| 466 | 442 | ||
| 467 | lastIndex = idxG3;
|
- | |
| 468 | log(String.format("GR[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d].", idxGR, idxG1, idxG2, idxG3)); |
- | |
| - | 443 | log(String.format("GR[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d].", |
|
| - | 444 | idxGR, idxG1, idxG2, idxG3)); |
|
| 469 | 445 | ||
| 470 | PadraoGatilho padrao = new PadraoGatilho(); |
446 | PadraoGatilho padrao = new PadraoGatilho(); |
| 471 | padrao.setReferencia(gr); |
447 | padrao.setReferencia(gr); |
| 472 | padrao.setGatilho1(g1); |
448 | padrao.setGatilho1(g1); |
| 473 | padrao.setGatilho2(g2); |
449 | padrao.setGatilho2(g2); |
| Line 491... | Line 467... | ||
| 491 | }
|
467 | }
|
| 492 | 468 | ||
| 493 | int idxCandidatoRef = idxInicio; |
469 | int idxCandidatoRef = idxInicio; |
| 494 | 470 | ||
| 495 | // --------------------
|
471 | // --------------------
|
| 496 | // 1) Buscar G1 (comprador rompe topo do candidato)
|
- | |
| - | 472 | // 1) Buscar G1: QUALQUER comprador subsequente que rompa o topo do candidatoRef
|
|
| - | 473 | // (com GR dinâmico pela MÍNIMA antes de G1)
|
|
| 497 | // --------------------
|
474 | // --------------------
|
| 498 | Candle g1 = null; |
475 | Candle g1 = null; |
| 499 | int idxG1 = -1; |
476 | int idxG1 = -1; |
| 500 | boolean temGR = false; |
- | |
| 501 | Candle gr = null; |
477 | Candle gr = null; |
| 502 | int idxGR = -1; |
478 | int idxGR = -1; |
| 503 | 479 | ||
| 504 | for (int i = idxInicio + 1; i < n; i++) { |
480 | for (int i = idxInicio + 1; i < n; i++) { |
| 505 | Candle c = candles.get(i); |
481 | Candle c = candles.get(i); |
| 506 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| - | 482 | if (isInside(candles, i) || !isDirecional(c)) { |
|
| 507 | continue; |
483 | continue; |
| - | 484 | }
|
|
| 508 | 485 | ||
| 509 | // Outside antes de G1
|
- | |
| - | 486 | // OUTSIDE antes de G1 => descarta padrão
|
|
| 510 | if (isOutside(candles, i)) { |
487 | if (isOutside(candles, i)) { |
| 511 | lastIndex = i;
|
488 | lastIndex = i;
|
| 512 | log(String.format("Vendedor: OUTSIDE em [%d] antes de G1. Abortando padrão.", i)); |
489 | log(String.format("Vendedor: OUTSIDE em [%d] antes de G1. Abortando padrão.", i)); |
| 513 | return new ResultadoPadrao(null, lastIndex); |
490 | return new ResultadoPadrao(null, lastIndex); |
| 514 | }
|
491 | }
|
| 515 | 492 | ||
| 516 | // G1: comprador rompe topo do candidato
|
- | |
| - | 493 | // GR dinâmico (venda): atualiza candidatoRef pelo FUNDO mais baixo
|
|
| - | 494 | // enquanto não existir G1
|
|
| - | 495 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) { |
|
| - | 496 | candidatoRef = c;
|
|
| - | 497 | idxCandidatoRef = i;
|
|
| - | 498 | lastIndex = i;
|
|
| - | 499 | continue; |
|
| - | 500 | }
|
|
| - | 501 | ||
| - | 502 | // G1: QUALQUER comprador subsequente que rompa o topo do candidatoRef
|
|
| 517 | if (c.isCandleComprador() |
503 | if (c.isCandleComprador() |
| 518 | && BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) { |
504 | && BigDecimalUtils.ehMaiorQue(c.getMaxima(), candidatoRef.getMaxima())) { |
| 519 | g1 = c;
|
505 | g1 = c;
|
| 520 | idxG1 = i;
|
506 | idxG1 = i;
|
| 521 | gr = candidatoRef;
|
507 | gr = candidatoRef;
|
| 522 | idxGR = idxCandidatoRef;
|
508 | idxGR = idxCandidatoRef;
|
| 523 | temGR = true; |
- | |
| 524 | lastIndex = i;
|
509 | lastIndex = i;
|
| 525 | break; |
510 | break; |
| 526 | }
|
- | |
| 527 | - | ||
| 528 | // GR DINÂMICO (venda): atualiza pelo FUNDO mais baixo antes de G1
|
- | |
| 529 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), candidatoRef.getMinima())) { |
- | |
| 530 | candidatoRef = c;
|
- | |
| 531 | idxCandidatoRef = i;
|
- | |
| 532 | lastIndex = i;
|
- | |
| 533 | continue; |
- | |
| 534 | }
|
511 | }
|
| 535 | 512 | ||
| 536 | lastIndex = i;
|
513 | lastIndex = i;
|
| 537 | }
|
514 | }
|
| 538 | 515 | ||
| 539 | if (g1 == null || !temGR) { |
- | |
| - | 516 | if (g1 == null || gr == null) { |
|
| 540 | log(String.format("Vendedor: não foi possível formar G1 a partir de idx %d.", idxInicio)); |
517 | log(String.format("Vendedor: não foi possível formar G1 a partir de idx %d.", idxInicio)); |
| 541 | return new ResultadoPadrao(null, lastIndex); |
518 | return new ResultadoPadrao(null, lastIndex); |
| 542 | }
|
519 | }
|
| 543 | 520 | ||
| 544 | log(String.format("GR VENDEDOR em [%d], G1 (comprador) em [%d].", idxGR, idxG1)); |
521 | log(String.format("GR VENDEDOR em [%d], G1 (comprador) em [%d].", idxGR, idxG1)); |
| 545 | 522 | ||
| 546 | // --------------------
|
523 | // --------------------
|
| 547 | // 2) Buscar G2 (tendência vendedora com fechamento dentro do GR)
|
- | |
| - | 524 | // 2) Buscar G2 – tendência VENDEDORA com fechamento dentro do GR
|
|
| 548 | // --------------------
|
525 | // --------------------
|
| 549 | Candle g2 = null; |
526 | Candle g2 = null; |
| 550 | int idxG2 = -1; |
527 | int idxG2 = -1; |
| 551 | 528 | ||
| 552 | int idxPrimeiroVendedor = -1; |
529 | int idxPrimeiroVendedor = -1; |
| 553 | for (int i = idxG1 + 1; i < n; i++) { |
530 | for (int i = idxG1 + 1; i < n; i++) { |
| 554 | Candle c = candles.get(i); |
531 | Candle c = candles.get(i); |
| 555 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| 556 | continue; |
- | |
| - | 532 | if (isInside(candles, i) || !isDirecional(c)) continue; |
|
| 557 | 533 | ||
| 558 | // Outside antes de G3
|
- | |
| - | 534 | // OUTSIDE antes de G2 => descarta padrão
|
|
| 559 | if (isOutside(candles, i)) { |
535 | if (isOutside(candles, i)) { |
| 560 | lastIndex = idxGR;
|
536 | lastIndex = idxGR;
|
| 561 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da perna vendedora de G2.", idxGR, i)); |
- | |
| - | 537 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da tendência vendedora de G2.", idxGR, i)); |
|
| 562 | return new ResultadoPadrao(null, lastIndex); |
538 | return new ResultadoPadrao(null, lastIndex); |
| 563 | }
|
539 | }
|
| 564 | 540 | ||
| 565 | // Regra global vendedor: romper fundo do GR invalida
|
- | |
| - | 541 | // Regra global vendedor: romper fundo do GR => descarta
|
|
| 566 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
542 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
| 567 | lastIndex = i;
|
543 | lastIndex = i;
|
| 568 | log(String.format("GR[%d]: candle[%d] rompeu fundo do GR antes de G2.", idxGR, i)); |
- | |
| - | 544 | log(String.format("GR[%d]: candle[%d] rompeu FUNDO do GR antes/na formação de G2.", idxGR, i)); |
|
| 569 | return new ResultadoPadrao(null, lastIndex); |
545 | return new ResultadoPadrao(null, lastIndex); |
| 570 | }
|
546 | }
|
| 571 | 547 | ||
| 572 | if (c.isCandleVendedor()) { |
548 | if (c.isCandleVendedor()) { |
| 573 | idxPrimeiroVendedor = i;
|
549 | idxPrimeiroVendedor = i;
|
| Line 585... | Line 561... | ||
| 585 | Candle ultimoVendedor = null; |
561 | Candle ultimoVendedor = null; |
| 586 | int idxUltimoVendedor = -1; |
562 | int idxUltimoVendedor = -1; |
| 587 | 563 | ||
| 588 | for (int i = idxPrimeiroVendedor; i < n; i++) { |
564 | for (int i = idxPrimeiroVendedor; i < n; i++) { |
| 589 | Candle c = candles.get(i); |
565 | Candle c = candles.get(i); |
| 590 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| 591 | continue; |
- | |
| - | 566 | if (isInside(candles, i) || !isDirecional(c)) continue; |
|
| 592 | 567 | ||
| 593 | // Outside durante G2
|
- | |
| - | 568 | // OUTSIDE durante G2 => descarta padrão
|
|
| 594 | if (isOutside(candles, i)) { |
569 | if (isOutside(candles, i)) { |
| 595 | lastIndex = idxGR;
|
570 | lastIndex = idxGR;
|
| 596 | log(String.format("GR[%d]: OUTSIDE em [%d] durante G2 (vendedor).", idxGR, i)); |
- | |
| - | 571 | log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (vendedor).", idxGR, i)); |
|
| 597 | return new ResultadoPadrao(null, lastIndex); |
572 | return new ResultadoPadrao(null, lastIndex); |
| 598 | }
|
573 | }
|
| 599 | 574 | ||
| 600 | // Regra global vendedor: romper fundo do GR invalida
|
- | |
| - | 575 | // Regra global vendedor: romper fundo do GR => descarta
|
|
| 601 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
576 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
| 602 | lastIndex = i;
|
577 | lastIndex = i;
|
| 603 | log(String.format("GR[%d]: candle[%d] rompeu fundo do GR durante G2.", idxGR, i)); |
- | |
| - | 578 | log(String.format("GR[%d]: candle[%d] rompeu FUNDO do GR durante G2.", idxGR, i)); |
|
| 604 | return new ResultadoPadrao(null, lastIndex); |
579 | return new ResultadoPadrao(null, lastIndex); |
| 605 | }
|
580 | }
|
| 606 | 581 | ||
| 607 | if (c.isCandleVendedor()) { |
582 | if (c.isCandleVendedor()) { |
| 608 | ultimoVendedor = c;
|
583 | ultimoVendedor = c;
|
| Line 623... | Line 598... | ||
| 623 | BigDecimalUtils.ehMaiorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMinima()) && |
598 | BigDecimalUtils.ehMaiorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMinima()) && |
| 624 | BigDecimalUtils.ehMenorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMaxima()); |
599 | BigDecimalUtils.ehMenorOuIgualQue(ultimoVendedor.getFechamento(), gr.getMaxima()); |
| 625 | 600 | ||
| 626 | if (!fechamentoDentroRegiaoGR) { |
601 | if (!fechamentoDentroRegiaoGR) { |
| 627 | log(String.format( |
602 | log(String.format( |
| 628 | "GR[%d], G1[%d]: último vendedor[%d] não fechou dentro da região do GR (fech=%s, faixa=[%s,%s]).",
|
- | |
| 629 | idxGR, idxG1, idxUltimoVendedor, |
- | |
| 630 | ultimoVendedor.getFechamento().toPlainString(), |
- | |
| 631 | gr.getMinima().toPlainString(), |
- | |
| 632 | gr.getMaxima().toPlainString() |
- | |
| - | 603 | "GR[%d], G1[%d]: último vendedor[%d] NÃO fechou dentro da região do GR.",
|
|
| - | 604 | idxGR, idxG1, idxUltimoVendedor |
|
| 633 | )); |
605 | )); |
| 634 | return new ResultadoPadrao(null, lastIndex); |
606 | return new ResultadoPadrao(null, lastIndex); |
| 635 | }
|
607 | }
|
| 636 | 608 | ||
| 637 | g2 = ultimoVendedor;
|
609 | g2 = ultimoVendedor;
|
| 638 | idxG2 = idxUltimoVendedor;
|
610 | idxG2 = idxUltimoVendedor;
|
| 639 | log(String.format("GR[%d], G1[%d] => G2 (vendedor) em [%d].", idxGR, idxG1, idxG2)); |
611 | log(String.format("GR[%d], G1[%d] => G2 (vendedor) em [%d].", idxGR, idxG1, idxG2)); |
| 640 | 612 | ||
| 641 | // --------------------
|
613 | // --------------------
|
| 642 | // 3) Buscar G3 (comprador rompe topo de G2 com fundo >= fundo GR)
|
- | |
| - | 614 | // 3) Buscar G3 – NOVA REGRA:
|
|
| - | 615 | // QUALQUER comprador subsequente que rompa topo de G2
|
|
| - | 616 | // e tenha fundo >= fundo do GR.
|
|
| 643 | // --------------------
|
617 | // --------------------
|
| 644 | Candle g3 = null; |
618 | Candle g3 = null; |
| 645 | int idxG3 = -1; |
619 | int idxG3 = -1; |
| 646 | 620 | ||
| 647 | int idxPrimeiroCompradorAposG2 = -1; |
- | |
| 648 | for (int i = idxG2 + 1; i < n; i++) { |
621 | for (int i = idxG2 + 1; i < n; i++) { |
| 649 | Candle c = candles.get(i); |
622 | Candle c = candles.get(i); |
| 650 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| - | 623 | if (!isDirecional(c) || isInside(candles, i)) { |
|
| 651 | continue; |
624 | continue; |
| - | 625 | }
|
|
| 652 | 626 | ||
| - | 627 | // OUTSIDE antes/durante busca de G3 => descarta padrão
|
|
| 653 | if (isOutside(candles, i)) { |
628 | if (isOutside(candles, i)) { |
| 654 | lastIndex = idxGR;
|
629 | lastIndex = idxGR;
|
| 655 | log(String.format("GR[%d]: OUTSIDE em [%d] antes da sequência compradora de G3.", idxGR, i)); |
- | |
| - | 630 | log(String.format("GR[%d]: OUTSIDE em [%d] durante busca de G3 (comprador).", idxGR, i)); |
|
| 656 | return new ResultadoPadrao(null, lastIndex); |
631 | return new ResultadoPadrao(null, lastIndex); |
| 657 | }
|
632 | }
|
| 658 | 633 | ||
| 659 | // Regra global vendedor: romper fundo do GR antes de G3 invalida
|
- | |
| - | 634 | // Regra global vendedor: romper fundo do GR => descarta
|
|
| 660 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
635 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
| 661 | lastIndex = i;
|
636 | lastIndex = i;
|
| 662 | log(String.format("GR[%d]: candle[%d] rompeu fundo do GR antes de G3.", idxGR, i)); |
- | |
| - | 637 | log(String.format("GR[%d]: candle[%d] rompeu FUNDO do GR durante busca de G3.", idxGR, i)); |
|
| 663 | return new ResultadoPadrao(null, lastIndex); |
638 | return new ResultadoPadrao(null, lastIndex); |
| 664 | }
|
639 | }
|
| 665 | 640 | ||
| 666 | if (c.isCandleComprador()) { |
641 | if (c.isCandleComprador()) { |
| 667 | idxPrimeiroCompradorAposG2 = i;
|
- | |
| 668 | break; |
- | |
| 669 | } else { |
- | |
| 670 | // primeiro direcional não é comprador → padrão até G2
|
- | |
| 671 | lastIndex = i;
|
- | |
| 672 | log(String.format("GR[%d], G1[%d], G2[%d]: primeiro direcional após G2 não é comprador (idx %d).", |
- | |
| 673 | idxGR, idxG1, idxG2, i)); |
- | |
| 674 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 675 | }
|
- | |
| 676 | }
|
- | |
| 677 | - | ||
| 678 | if (idxPrimeiroCompradorAposG2 == -1) { |
- | |
| 679 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve comprador após G2.", idxGR, idxG1, idxG2)); |
- | |
| 680 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 681 | }
|
- | |
| 682 | - | ||
| 683 | for (int i = idxPrimeiroCompradorAposG2; i < n; i++) { |
- | |
| 684 | Candle c = candles.get(i); |
- | |
| 685 | if (isInside(candles, i) || !isDirecional(c)) |
- | |
| 686 | continue; |
- | |
| 687 | - | ||
| 688 | if (isOutside(candles, i)) { |
- | |
| 689 | lastIndex = idxGR;
|
- | |
| 690 | log(String.format("GR[%d]: OUTSIDE em [%d] durante formação de G3 (comprador).", idxGR, i)); |
- | |
| 691 | return new ResultadoPadrao(null, lastIndex); |
- | |
| 692 | }
|
- | |
| 693 | - | ||
| 694 | // Regra global vendedor: romper fundo do GR invalida
|
- | |
| 695 | if (BigDecimalUtils.ehMenorQue(c.getMinima(), gr.getMinima())) { |
- | |
| 696 | lastIndex = i;
|
- | |
| 697 | log(String.format("GR[%d]: candle[%d] rompeu fundo do GR durante busca de G3.", idxGR, i)); |
- | |
| 698 | return new ResultadoPadrao(null, lastIndex); |
- | |
| 699 | }
|
- | |
| 700 | - | ||
| 701 | if (!c.isCandleComprador()) { |
- | |
| 702 | // apareceu vendedor antes de G3 → padrão só até G2
|
- | |
| 703 | lastIndex = i - 1; |
- | |
| 704 | log(String.format("GR[%d], G1[%d], G2[%d]: vendedor em [%d] antes de G3.", idxGR, idxG1, idxG2, i)); |
- | |
| 705 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
- | |
| 706 | }
|
- | |
| 707 | - | ||
| 708 | lastIndex = i;
|
- | |
| 709 | boolean rompeTopoG2 = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima()); |
- | |
| 710 | boolean fundoMaiorOuIgualGR = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), gr.getMinima()); |
- | |
| 711 | if (rompeTopoG2 && fundoMaiorOuIgualGR) { |
- | |
| 712 | g3 = c;
|
- | |
| 713 | idxG3 = i;
|
- | |
| 714 | break; |
- | |
| - | 642 | boolean rompeTopoG2 = BigDecimalUtils.ehMaiorQue(c.getMaxima(), g2.getMaxima()); |
|
| - | 643 | boolean fundoMaiorOuIgualGR = BigDecimalUtils.ehMaiorOuIgualQue(c.getMinima(), gr.getMinima()); |
|
| - | 644 | if (rompeTopoG2 && fundoMaiorOuIgualGR) { |
|
| - | 645 | g3 = c;
|
|
| - | 646 | idxG3 = i;
|
|
| - | 647 | lastIndex = i;
|
|
| - | 648 | break; |
|
| - | 649 | }
|
|
| 715 | }
|
650 | }
|
| 716 | }
|
651 | }
|
| 717 | 652 | ||
| 718 | if (g3 == null) { |
653 | if (g3 == null) { |
| 719 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve G3, padrão parcial.", idxGR, idxG1, idxG2)); |
- | |
| - | 654 | log(String.format("GR[%d], G1[%d], G2[%d]: não houve G3 (vendedor -> padrão parcial).", |
|
| - | 655 | idxGR, idxG1, idxG2)); |
|
| 720 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
656 | return criarResultadoParcialComG2(gr, g1, g2, lastIndex); |
| 721 | }
|
657 | }
|
| 722 | 658 | ||
| 723 | lastIndex = idxG3;
|
- | |
| 724 | log(String.format("GR[%d], G1[%d], G2[%d] => G3 (comprador) em [%d].", idxGR, idxG1, idxG2, idxG3)); |
- | |
| - | 659 | log(String.format("GR[%d], G1[%d], G2[%d] => G3 (comprador) em [%d].", |
|
| - | 660 | idxGR, idxG1, idxG2, idxG3)); |
|
| 725 | 661 | ||
| 726 | PadraoGatilho padrao = new PadraoGatilho(); |
662 | PadraoGatilho padrao = new PadraoGatilho(); |
| 727 | padrao.setReferencia(gr); |
663 | padrao.setReferencia(gr); |
| 728 | padrao.setGatilho1(g1); |
664 | padrao.setGatilho1(g1); |
| 729 | padrao.setGatilho2(g2); |
665 | padrao.setGatilho2(g2); |
| Line 739... | Line 675... | ||
| 739 | 675 | ||
| 740 | public void resetTempoReal() { |
676 | public void resetTempoReal() { |
| 741 | this.idxProximaAnaliseTempoReal = 0; |
677 | this.idxProximaAnaliseTempoReal = 0; |
| 742 | }
|
678 | }
|
| 743 | 679 | ||
| - | 680 | /**
|
|
| - | 681 | * Deve ser chamado SEMPRE que um novo candle for adicionado à lista.
|
|
| - | 682 | *
|
|
| - | 683 | * Exemplo:
|
|
| - | 684 | * candles.add(novoCandle);
|
|
| - | 685 | * PadraoGatilho padrao = detector.processarCandleTempoReal(candles);
|
|
| - | 686 | *
|
|
| - | 687 | * if (padrao != null) {
|
|
| - | 688 | * // padrão completo (até G3) encontrado
|
|
| - | 689 | * }
|
|
| - | 690 | */
|
|
| 744 | public PadraoGatilho processarCandleTempoReal(List<Candle> candles) { |
691 | public PadraoGatilho processarCandleTempoReal(List<Candle> candles) { |
| 745 | int n = candles.size(); |
692 | int n = candles.size(); |
| 746 | if (n < 4) return null; |
693 | if (n < 4) return null; |
| 747 | 694 | ||
| 748 | while (idxProximaAnaliseTempoReal < n - 3) { |
695 | while (idxProximaAnaliseTempoReal < n - 3) { |