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