Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 778 | blopes | 1 | package br.com.kronus.core; |
| 2 | |||
| 3 | import br.com.ec.core.util.VerificadorUtil; |
||
| 4 | import br.com.kronus.core.Candle; |
||
| 5 | |||
| 6 | import java.util.ArrayList; |
||
| 7 | import java.util.List; |
||
| 8 | |||
| 9 | /** |
||
| 10 | * Detector + Monitor de Gatilhos (Referência, G1, G2, G3, G4) |
||
| 11 | * |
||
| 12 | * REGRAS IMPLEMENTADAS (resumo): |
||
| 13 | * |
||
| 14 | * Candle Referência: |
||
| 15 | * - Candle que demarca uma região (topo ou fundo) e que posteriormente |
||
| 16 | * será rompido contrariamente à sua tendência pelo Gatilho 1. |
||
| 17 | * - Ex.: candle comprador -> se um candle posterior romper seu FUNDO, |
||
| 18 | * esse posterior é G1 e o rompido é a referência. |
||
| 19 | * Ex.: candle vendedor -> se um candle posterior romper seu TOPO, |
||
| 20 | * esse posterior é G1 e o rompido é a referência. |
||
| 21 | * |
||
| 22 | * Gatilho 1: |
||
| 23 | * - Primeiro candle que rompe a tendência contrária a algum candle anterior |
||
| 24 | * (sem insiders). |
||
| 25 | * - Se G1 for COMPRADOR: deve romper o TOPO do candle referência. |
||
| 26 | * - Se G1 for VENDEDOR: deve romper o FUNDO do candle referência. |
||
| 27 | * |
||
| 28 | * Gatilho 2: |
||
| 29 | * - Candle que retorna à região do candle referência buscando liquidez. |
||
| 30 | * - Último candle antes da nova quebra de tendência. |
||
| 31 | * - Fechamento deve estar dentro da região total (incluindo pavio) |
||
| 32 | * do candle referência. |
||
| 33 | * - Se ultrapassar a máxima (ou mínima) do candle referência, deve-se procurar |
||
| 34 | * por um novo candle referência a partir desse candle. |
||
| 35 | * - EXCEÇÃO: se o candle for da mesma tendência de G1 e tiver rompido |
||
| 36 | * sua tendência (novo topo na compra / novo fundo na venda), também é G2. |
||
| 37 | * |
||
| 38 | * Gatilho 3: |
||
| 39 | * - Candle posterior ao G2. |
||
| 40 | * - Mesma tendência de G1. |
||
| 41 | * - Se referência for COMPRADORA: |
||
| 42 | * * topo de G3 < topo da referência |
||
| 43 | * * G3 rompe o FUNDO do G2 |
||
| 44 | * * se romper o topo da referência após o G2 → invalida padrão. |
||
| 45 | * - Se referência for VENDEDORA: |
||
| 46 | * * topo de G3 > topo da referência |
||
| 47 | * * G3 rompe o TOPO do G2 |
||
| 48 | * * se romper o fundo da referência após o G2 → invalida padrão. |
||
| 49 | * |
||
| 50 | * Gatilho 4: |
||
| 51 | * - Desarma a operação. |
||
| 52 | * - Se referência COMPRADORA: seu topo será rompido pelo candle do G4. |
||
| 53 | * - Se referência VENDEDORA: seu fundo será rompido. |
||
| 54 | * - Após G4 a análise recomeça. |
||
| 55 | */ |
||
| 56 | public class DetectorGatilhos_ { |
||
| 57 | |||
| 58 | private Candle referencia; |
||
| 59 | private Candle gatilho1; |
||
| 60 | private Candle gatilho2; |
||
| 61 | private Candle gatilho3; |
||
| 62 | private Candle gatilho4; |
||
| 63 | private int idxRef = -1; |
||
| 64 | private int idxG1 = -1; |
||
| 65 | private int idxG2 = -1; |
||
| 66 | private int idxG3 = -1; |
||
| 67 | private int idxG4 = -1; |
||
| 68 | |||
| 69 | private boolean candidatoG4Avaliado = false; |
||
| 70 | |||
| 71 | |||
| 72 | private PadraoGatilho padraoAtual; |
||
| 73 | |||
| 74 | private boolean logAtivo; |
||
| 75 | |||
| 76 | public DetectorGatilhos_() { |
||
| 77 | this(false); |
||
| 78 | } |
||
| 79 | |||
| 80 | public DetectorGatilhos_(boolean logAtivo) { |
||
| 81 | this.logAtivo = logAtivo; |
||
| 82 | } |
||
| 83 | |||
| 84 | public boolean isLogAtivo() { |
||
| 85 | return logAtivo; |
||
| 86 | } |
||
| 87 | |||
| 88 | public void setLogAtivo(boolean logAtivo) { |
||
| 89 | this.logAtivo = logAtivo; |
||
| 90 | } |
||
| 91 | |||
| 92 | private void log(String msg) { |
||
| 93 | if (logAtivo) { |
||
| 94 | System.out.println(msg); |
||
| 95 | } |
||
| 96 | } |
||
| 97 | |||
| 98 | private void reset() { |
||
| 99 | referencia = null; |
||
| 100 | gatilho1 = null; |
||
| 101 | gatilho2 = null; |
||
| 102 | gatilho3 = null; |
||
| 103 | gatilho4 = null; |
||
| 104 | padraoAtual = null; |
||
| 105 | |||
| 106 | idxRef = -1; |
||
| 107 | idxG1 = -1; |
||
| 108 | idxG2 = -1; |
||
| 109 | idxG3 = -1; |
||
| 110 | idxG4 = -1; |
||
| 111 | |||
| 112 | candidatoG4Avaliado = false; |
||
| 113 | } |
||
| 114 | |||
| 115 | // ============================================================ |
||
| 116 | // A) MODO SIMULADOR / BACKTEST |
||
| 117 | // -> continua: List<PadraoGatilho> padroes = detector.identificarPadroes(candles); |
||
| 118 | // ============================================================ |
||
| 119 | |||
| 120 | public List<PadraoGatilho> identificarPadroes(List<Candle> candles) { |
||
| 121 | reset(); |
||
| 122 | List<PadraoGatilho> padroes = new ArrayList<>(); |
||
| 123 | |||
| 124 | for (int i = 0; i < candles.size(); i++) { |
||
| 125 | processarCandleInterno(candles, i, null, padroes); |
||
| 126 | } |
||
| 127 | |||
| 128 | return padroes; |
||
| 129 | } |
||
| 130 | |||
| 131 | // ============================================================ |
||
| 132 | // B) MODO MONITOR (tempo real / replay) |
||
| 133 | // ============================================================ |
||
| 134 | |||
| 135 | /** |
||
| 136 | * Processa o candle idx e retorna os logs deste passo. |
||
| 137 | * Os logs também são enviados para System.out.println se logAtivo = true. |
||
| 138 | */ |
||
| 139 | public List<String> processarCandle(List<Candle> candles, int idx) { |
||
| 140 | List<String> logs = new ArrayList<>(); |
||
| 141 | processarCandleInterno(candles, idx, logs, null); |
||
| 142 | return logs; |
||
| 143 | } |
||
| 144 | |||
| 145 | // ============================================================ |
||
| 146 | // C) LÓGICA INTERNA (compartilhada entre simulador e monitor) |
||
| 147 | // ============================================================ |
||
| 148 | |||
| 149 | private void processarCandleInterno(List<Candle> candles, |
||
| 150 | int idx, |
||
| 151 | List<String> logs, |
||
| 152 | List<PadraoGatilho> padroes) { |
||
| 153 | |||
| 154 | if (idx < 0 || idx >= candles.size()) { |
||
| 155 | return; |
||
| 156 | } |
||
| 157 | |||
| 158 | Candle atual = candles.get(idx); |
||
| 159 | if (VerificadorUtil.naoEstaNulo(atual)) { |
||
| 160 | if (VerificadorUtil.estaNulo(atual.getContador())) { |
||
| 161 | System.out.println("TESTE"); |
||
| 162 | } else { |
||
| 163 | if (atual.getContador() == 11) { |
||
| 164 | System.out.println("TESTE"); |
||
| 165 | } |
||
| 166 | if (atual.getContador() == 57) { |
||
| 167 | System.out.println("TESTE"); |
||
| 168 | } |
||
| 169 | } |
||
| 170 | } |
||
| 171 | |||
| 172 | // -------------------------------------------------------------------- |
||
| 173 | // 1) Ainda não temos Ref + G1 -> tentar formar com o candle atual |
||
| 174 | // -------------------------------------------------------------------- |
||
| 175 | if (referencia == null || gatilho1 == null) { |
||
| 176 | ParRefG1 par = encontrarReferenciaEGatilho1(candles, idx); |
||
| 177 | |||
| 178 | if (par != null) { |
||
| 179 | referencia = par.referencia; |
||
| 180 | gatilho1 = par.g1; |
||
| 181 | |||
| 182 | idxRef = candles.indexOf(referencia); |
||
| 183 | idxG1 = candles.indexOf(gatilho1); |
||
| 184 | |||
| 185 | // Segurança extra: não aceitar se por algum bug der o mesmo índice ou inverso |
||
| 186 | if (idxRef < 0 || idxG1 < 0 || idxRef >= idxG1) { |
||
| 187 | // Inconsistente, descarta e segue |
||
| 188 | referencia = null; |
||
| 189 | gatilho1 = null; |
||
| 190 | idxRef = -1; |
||
| 191 | idxG1 = -1; |
||
| 192 | String msg = String.format( |
||
| 193 | "[%d] Par (Ref, G1) inconsistente encontrado. Descartando.", |
||
| 194 | idx |
||
| 195 | ); |
||
| 196 | if (logs != null) logs.add(msg); |
||
| 197 | log(msg); |
||
| 198 | return; |
||
| 199 | } |
||
| 200 | |||
| 201 | gatilho2 = null; |
||
| 202 | gatilho3 = null; |
||
| 203 | gatilho4 = null; |
||
| 204 | idxG2 = idxG3 = idxG4 = -1; |
||
| 205 | |||
| 206 | padraoAtual = new PadraoGatilho(); |
||
| 207 | padraoAtual.setReferencia(referencia); |
||
| 208 | padraoAtual.setGatilho1(gatilho1); |
||
| 209 | |||
| 210 | String direcao = gatilho1.isCandleComprador() ? "COMPRA" : "VENDA"; |
||
| 211 | int idxRef = candles.indexOf(referencia); |
||
| 212 | int idxG1 = candles.indexOf(gatilho1); |
||
| 213 | |||
| 214 | String msg = String.format( |
||
| 215 | "[%d] Gatilho 1 (%s) identificado. Referência = candle [%d], G1 = candle [%d].", |
||
| 216 | idx, direcao, idxRef, idxG1 |
||
| 217 | ); |
||
| 218 | if (logs != null) logs.add(msg); |
||
| 219 | log(msg); |
||
| 220 | } else { |
||
| 221 | String msg = "[" + idx + "] Sem referência/G1 formados ainda."; |
||
| 222 | if (logs != null) logs.add(msg); |
||
| 223 | log(msg); |
||
| 224 | } |
||
| 225 | |||
| 226 | return; |
||
| 227 | } |
||
| 228 | |||
| 229 | // -------------------------------------------------------------------- |
||
| 230 | // 2) Temos Ref + G1, mas ainda não temos G2 |
||
| 231 | // -------------------------------------------------------------------- |
||
| 232 | if (gatilho2 == null) { |
||
| 233 | |||
| 234 | boolean cEhG2 = isGatilho2(referencia, gatilho1, atual); |
||
| 235 | |||
| 236 | if (cEhG2) { |
||
| 237 | // G2 deve ser posterior a G1 e candle diferente |
||
| 238 | if (idx > idxG1) { |
||
| 239 | gatilho2 = atual; |
||
| 240 | idxG2 = idx; |
||
| 241 | |||
| 242 | if (padraoAtual != null) { |
||
| 243 | padraoAtual.setGatilho2(gatilho2); |
||
| 244 | } |
||
| 245 | |||
| 246 | String msg = String.format( |
||
| 247 | "[%d] Gatilho 2 identificado.", |
||
| 248 | idx |
||
| 249 | ); |
||
| 250 | if (logs != null) logs.add(msg); |
||
| 251 | log(msg); |
||
| 252 | } else { |
||
| 253 | // Se por alguma razão o método estiver sendo chamado de forma estranha |
||
| 254 | String msg = String.format( |
||
| 255 | "[%d] Candle candidato a G2, mas índice %d <= idxG1 %d. Ignorando para manter ordem cronológica.", |
||
| 256 | idx, idx, idxG1 |
||
| 257 | ); |
||
| 258 | if (logs != null) logs.add(msg); |
||
| 259 | log(msg); |
||
| 260 | } |
||
| 261 | return; |
||
| 262 | } |
||
| 263 | |||
| 264 | // Se NÃO é G2, verificamos se: |
||
| 265 | // 1) ultrapassou a região da referência (topo/fundo) |
||
| 266 | // 2) houve nova quebra de tendência sem atingir a região da referência |
||
| 267 | boolean ultrapassaRef = ultrapassaRegiaoReferencia(referencia, atual); |
||
| 268 | boolean novaQuebraSemRegiao = novaQuebraDeTendenciaSemAtingirRegiao(referencia, gatilho1, atual); |
||
| 269 | |||
| 270 | if (ultrapassaRef || novaQuebraSemRegiao) { |
||
| 271 | String motivo = ultrapassaRef |
||
| 272 | ? "ultrapassou a região da referência" |
||
| 273 | : "nova quebra de tendência sem atingir a região da referência"; |
||
| 274 | |||
| 275 | String msg = String.format( |
||
| 276 | "[%d] Candle %s. Procurando novo candle referência a partir deste candle.", |
||
| 277 | idx, motivo |
||
| 278 | ); |
||
| 279 | if (logs != null) logs.add(msg); |
||
| 280 | log(msg); |
||
| 281 | |||
| 282 | // Reset e tentamos nova Ref+G1 com o candle atual |
||
| 283 | reset(); |
||
| 284 | ParRefG1 novo = encontrarReferenciaEGatilho1(candles, idx); |
||
| 285 | if (novo != null) { |
||
| 286 | referencia = novo.referencia; |
||
| 287 | gatilho1 = novo.g1; |
||
| 288 | padraoAtual = new PadraoGatilho(); |
||
| 289 | padraoAtual.setReferencia(referencia); |
||
| 290 | padraoAtual.setGatilho1(gatilho1); |
||
| 291 | |||
| 292 | String direcao = gatilho1.isCandleComprador() ? "COMPRA" : "VENDA"; |
||
| 293 | int idxRef = candles.indexOf(referencia); |
||
| 294 | int idxG1 = candles.indexOf(gatilho1); |
||
| 295 | |||
| 296 | String msg2 = String.format( |
||
| 297 | "[%d] Novo Gatilho 1 (%s) identificado após reset. Ref = [%d], G1 = [%d].", |
||
| 298 | idx, direcao, idxRef, idxG1 |
||
| 299 | ); |
||
| 300 | if (logs != null) logs.add(msg2); |
||
| 301 | log(msg2); |
||
| 302 | } |
||
| 303 | return; |
||
| 304 | } |
||
| 305 | |||
| 306 | // Nem é G2, nem invalida a referência → seguimos aguardando G2 |
||
| 307 | String msg = "[" + idx + "] Aguardando Gatilho 2."; |
||
| 308 | if (logs != null) logs.add(msg); |
||
| 309 | log(msg); |
||
| 310 | |||
| 311 | return; |
||
| 312 | } |
||
| 313 | |||
| 314 | // -------------------------------------------------------------------- |
||
| 315 | // 3) Temos Ref + G1 + G2, mas ainda não G3 |
||
| 316 | // -------------------------------------------------------------------- |
||
| 317 | if (gatilho3 == null) { |
||
| 318 | |||
| 319 | // Invalidação após G2: |
||
| 320 | if (rompeReferenciaAposG2(referencia, atual)) { |
||
| 321 | String msg = String.format( |
||
| 322 | "[%d] Rompimento da referência após o G2. Padrão invalidado. Resetando.", |
||
| 323 | idx |
||
| 324 | ); |
||
| 325 | if (logs != null) logs.add(msg); |
||
| 326 | log(msg); |
||
| 327 | reset(); |
||
| 328 | return; |
||
| 329 | } |
||
| 330 | |||
| 331 | if (isGatilho3(referencia, gatilho1, gatilho2, atual)) { |
||
| 332 | |||
| 333 | if (idx > idxG2) { |
||
| 334 | gatilho3 = atual; |
||
| 335 | idxG3 = idx; |
||
| 336 | candidatoG4Avaliado = false; |
||
| 337 | |||
| 338 | if (padraoAtual != null) { |
||
| 339 | padraoAtual.setGatilho3(gatilho3); |
||
| 340 | } |
||
| 341 | |||
| 342 | String msg = String.format( |
||
| 343 | "[%d] Gatilho 3 identificado.", |
||
| 344 | idx |
||
| 345 | ); |
||
| 346 | if (logs != null) logs.add(msg); |
||
| 347 | log(msg); |
||
| 348 | |||
| 349 | if (padroes != null && padraoAtual != null && !padroes.contains(padraoAtual)) { |
||
| 350 | padroes.add(padraoAtual); |
||
| 351 | } |
||
| 352 | } else { |
||
| 353 | String msg = String.format( |
||
| 354 | "[%d] Candle candidato a G3, mas índice %d <= idxG2 %d. Ignorando.", |
||
| 355 | idx, idx, idxG2 |
||
| 356 | ); |
||
| 357 | if (logs != null) logs.add(msg); |
||
| 358 | log(msg); |
||
| 359 | } |
||
| 360 | |||
| 361 | return; |
||
| 362 | } |
||
| 363 | |||
| 364 | return; |
||
| 365 | } |
||
| 366 | |||
| 367 | // -------------------------------------------------------------------- |
||
| 368 | // 4) Já temos Ref + G1 + G2 + G3 -> monitorar G4 (desarme) monitorar APENAS o primeiro candle pós G3 para possível G4 |
||
| 369 | // -------------------------------------------------------------------- |
||
| 370 | if (gatilho3 != null) { |
||
| 371 | Candle g3 = gatilho3; |
||
| 372 | |||
| 373 | // Se ainda não avaliamos o candidato a G4 |
||
| 374 | if (!candidatoG4Avaliado) { |
||
| 375 | |||
| 376 | // 4.1) Descartar insiders: candles completamente dentro do range do G3 |
||
| 377 | if (isInsiderEmRelacaoAoG3(g3, atual)) { |
||
| 378 | String msg = String.format( |
||
| 379 | "[%d] Candle insider em relação ao G3. Aguardando próximo candle para possível G4.", |
||
| 380 | idx |
||
| 381 | ); |
||
| 382 | if (logs != null) logs.add(msg); |
||
| 383 | log(msg); |
||
| 384 | return; |
||
| 385 | } |
||
| 386 | |||
| 387 | // 4.2) Este é o PRIMEIRO candle "útil" pós G3 (não insider) |
||
| 388 | candidatoG4Avaliado = true; |
||
| 389 | |||
| 390 | // G4 só pode ser depois de G3 e em candle diferente |
||
| 391 | if (idx > idxG3 && isGatilho4(referencia, atual)) { |
||
| 392 | gatilho4 = atual; |
||
| 393 | idxG4 = idx; |
||
| 394 | |||
| 395 | if (padraoAtual != null) { |
||
| 396 | padraoAtual.setGatilho4(gatilho4); |
||
| 397 | } |
||
| 398 | |||
| 399 | String msg = String.format( |
||
| 400 | "[%d] Gatilho 4 identificado. Operação desarmada. Padrão encerrado.", |
||
| 401 | idx |
||
| 402 | ); |
||
| 403 | if (logs != null) logs.add(msg); |
||
| 404 | log(msg); |
||
| 405 | |||
| 406 | reset(); |
||
| 407 | } else { |
||
| 408 | String msg = String.format( |
||
| 409 | "[%d] Primeiro candle pós G3 não configurou Gatilho 4. Padrão encerrado sem G4.", |
||
| 410 | idx |
||
| 411 | ); |
||
| 412 | if (logs != null) logs.add(msg); |
||
| 413 | log(msg); |
||
| 414 | reset(); |
||
| 415 | } |
||
| 416 | |||
| 417 | return; |
||
| 418 | } |
||
| 419 | |||
| 420 | // Se já avaliamos o candidato a G4, o padrão já foi encerrado (com ou sem G4), |
||
| 421 | // então não deveríamos mais cair aqui em condições normais. |
||
| 422 | return; |
||
| 423 | } |
||
| 424 | |||
| 425 | |||
| 426 | } |
||
| 427 | |||
| 428 | /** |
||
| 429 | * Um candle é considerado insider em relação ao G3 se estiver completamente |
||
| 430 | * contido dentro da máxima e mínima do G3. |
||
| 431 | */ |
||
| 432 | private boolean isInsiderEmRelacaoAoG3(Candle g3, Candle c) { |
||
| 433 | return c.getMaxima() <= g3.getMaxima() |
||
| 434 | && c.getMinima() >= g3.getMinima(); |
||
| 435 | } |
||
| 436 | |||
| 437 | |||
| 438 | // ============================================================ |
||
| 439 | // BLOCO: REFERÊNCIA + G1 |
||
| 440 | // ============================================================ |
||
| 441 | |||
| 442 | private static class ParRefG1 { |
||
| 443 | Candle referencia; |
||
| 444 | Candle g1; |
||
| 445 | ParRefG1(Candle referencia, Candle g1) { |
||
| 446 | this.referencia = referencia; |
||
| 447 | this.g1 = g1; |
||
| 448 | } |
||
| 449 | } |
||
| 450 | |||
| 451 | /** |
||
| 452 | * Encontra par (Referência, G1) partindo de um possível candle referência, |
||
| 453 | * analisando os candles posteriores. |
||
| 454 | * |
||
| 455 | * Regras: |
||
| 456 | * |
||
| 457 | * Candle Referência: |
||
| 458 | * - Candle que demarca topo/fundo e que será rompido, posteriormente, |
||
| 459 | * contrariamente à sua tendência pelo Gatilho 1. |
||
| 460 | * |
||
| 461 | * Exemplo: |
||
| 462 | * - Candle COMPRADOR (referência): |
||
| 463 | * Se ALGUM candle VENDEDOR subsequente romper o FUNDO dele: |
||
| 464 | * -> esse VENDEDOR é o Gatilho 1 |
||
| 465 | * -> o COMPRADOR rompido é a Referência. |
||
| 466 | * |
||
| 467 | * - Candle VENDEDOR (referência): |
||
| 468 | * Se ALGUM candle COMPRADOR subsequente romper o TOPO dele: |
||
| 469 | * -> esse COMPRADOR é o Gatilho 1 |
||
| 470 | * -> o VENDEDOR rompido é a Referência. |
||
| 471 | * |
||
| 472 | * A função procura o PRIMEIRO G1 que respeita essas regras. |
||
| 473 | */ |
||
| 474 | private ParRefG1 encontrarReferenciaEGatilho1(List<Candle> candles, int idxRef) { |
||
| 475 | Candle ref = candles.get(idxRef); |
||
| 476 | |||
| 477 | boolean refComprador = ref.isCandleComprador(); |
||
| 478 | boolean refVendedor = ref.isCandleVendedor(); |
||
| 479 | |||
| 480 | // Se a referência não tiver tendência clara, ignoramos (doji/indeciso) |
||
| 481 | if (!refComprador && !refVendedor) { |
||
| 482 | return null; |
||
| 483 | } |
||
| 484 | |||
| 485 | // Percorre os candles POSTERIORES ao possível candle referência |
||
| 486 | for (int i = idxRef + 1; i < candles.size(); i++) { |
||
| 487 | Candle c = candles.get(i); |
||
| 488 | |||
| 489 | // também ignoramos candles sem tendência clara como candidatos a G1 |
||
| 490 | boolean cComprador = c.isCandleComprador(); |
||
| 491 | boolean cVendedor = c.isCandleVendedor(); |
||
| 492 | if (!cComprador && !cVendedor) { |
||
| 493 | continue; |
||
| 494 | } |
||
| 495 | |||
| 496 | if (refComprador && cVendedor) { |
||
| 497 | // Referência COMPRADORA -> G1 VENDEDOR deve romper o FUNDO da referência |
||
| 498 | if (c.getMinima() < ref.getMinima()) { |
||
| 499 | // Achamos o primeiro G1 que rompeu o fundo da referência |
||
| 500 | return new ParRefG1(ref, c); |
||
| 501 | } |
||
| 502 | |||
| 503 | } else if (refVendedor && cComprador) { |
||
| 504 | // Referência VENDEDORA -> G1 COMPRADOR deve romper o TOPO da referência |
||
| 505 | if (c.getMaxima() > ref.getMaxima()) { |
||
| 506 | // Achamos o primeiro G1 que rompeu o topo da referência |
||
| 507 | return new ParRefG1(ref, c); |
||
| 508 | } |
||
| 509 | } |
||
| 510 | } |
||
| 511 | |||
| 512 | // Nenhum G1 encontrado para esse possível candle referência |
||
| 513 | return null; |
||
| 514 | } |
||
| 515 | |||
| 516 | |||
| 517 | // ============================================================ |
||
| 518 | // BLOCO: GATILHO 2 |
||
| 519 | // ============================================================ |
||
| 520 | |||
| 521 | /** |
||
| 522 | * Gatilho 2: |
||
| 523 | * - Candle que RETORNA à região do candle referência (fechamento dentro do range total). |
||
| 524 | * - É o último candle antes da nova quebra de tendência. |
||
| 525 | * - EXCEÇÃO: se for da mesma tendência de G1 e "romper a tendência" de G1 |
||
| 526 | * (novo topo na compra / novo fundo na venda), também é G2 mesmo sem tocar a região. |
||
| 527 | */ |
||
| 528 | private boolean isGatilho2(Candle referencia, Candle g1, Candle c) { |
||
| 529 | |||
| 530 | boolean cComprador = c.isCandleComprador(); |
||
| 531 | boolean cVendedor = c.isCandleVendedor(); |
||
| 532 | |||
| 533 | if (!cComprador && !cVendedor) { |
||
| 534 | return false; |
||
| 535 | } |
||
| 536 | |||
| 537 | // Fechamento dentro da região total do candle referência |
||
| 538 | boolean fechamentoDentroRegiao = |
||
| 539 | c.getFechamento() >= referencia.getMinima() && |
||
| 540 | c.getFechamento() <= referencia.getMaxima(); |
||
| 541 | |||
| 542 | boolean condicaoPrincipal = fechamentoDentroRegiao; |
||
| 543 | |||
| 544 | boolean g1Comprador = g1.isCandleComprador(); |
||
| 545 | boolean g1Vendedor = g1.isCandleVendedor(); |
||
| 546 | |||
| 547 | boolean mesmaTendencia = |
||
| 548 | (g1Comprador && cComprador) || |
||
| 549 | (g1Vendedor && cVendedor); |
||
| 550 | |||
| 551 | boolean rompeTendenciaG1 = |
||
| 552 | (g1Comprador && c.getMaxima() > g1.getMaxima()) || |
||
| 553 | (g1Vendedor && c.getMinima() < g1.getMinima()); |
||
| 554 | |||
| 555 | boolean condicaoExcecao = mesmaTendencia && rompeTendenciaG1; |
||
| 556 | |||
| 557 | return condicaoPrincipal || condicaoExcecao; |
||
| 558 | } |
||
| 559 | |||
| 560 | /** |
||
| 561 | * Se ultrapassar a região do candle referência: |
||
| 562 | * - Referência compradora: rompe TOPO |
||
| 563 | * - Referência vendedora: rompe FUNDO |
||
| 564 | * Neste caso devemos buscar um novo candle referência a partir deste candle. |
||
| 565 | */ |
||
| 566 | private boolean ultrapassaRegiaoReferencia(Candle referencia, Candle c) { |
||
| 567 | if (referencia == null) return false; |
||
| 568 | |||
| 569 | boolean refComprador = referencia.isCandleComprador(); |
||
| 570 | boolean refVendedor = referencia.isCandleVendedor(); |
||
| 571 | |||
| 572 | if (refComprador) { |
||
| 573 | return c.getMaxima() > referencia.getMaxima(); |
||
| 574 | } else if (refVendedor) { |
||
| 575 | return c.getMinima() < referencia.getMinima(); |
||
| 576 | } |
||
| 577 | |||
| 578 | return false; |
||
| 579 | } |
||
| 580 | |||
| 581 | |||
| 582 | /** |
||
| 583 | * Nova quebra de tendência sem atingir a região da referência: |
||
| 584 | * |
||
| 585 | * Ideia: |
||
| 586 | * - Se G1 é comprador: |
||
| 587 | * -> candle atual é vendedor |
||
| 588 | * -> mínima do candle atual < mínima do G1 (quebra da perna de compra) |
||
| 589 | * -> fechamento NÃO está dentro da região da referência |
||
| 590 | * |
||
| 591 | * - Se G1 é vendedor: |
||
| 592 | * -> candle atual é comprador |
||
| 593 | * -> máxima do candle atual > máxima do G1 |
||
| 594 | * -> fechamento NÃO está dentro da região da referência |
||
| 595 | */ |
||
| 596 | private boolean novaQuebraDeTendenciaSemAtingirRegiao(Candle referencia, |
||
| 597 | Candle g1, |
||
| 598 | Candle c) { |
||
| 599 | if (referencia == null || g1 == null) return false; |
||
| 600 | |||
| 601 | boolean fechamentoDentroRegiao = |
||
| 602 | c.getFechamento() >= referencia.getMinima() && |
||
| 603 | c.getFechamento() <= referencia.getMaxima(); |
||
| 604 | |||
| 605 | if (fechamentoDentroRegiao) { |
||
| 606 | return false; // se tocou a região, não é "sem atingir a região" |
||
| 607 | } |
||
| 608 | |||
| 609 | boolean g1Comprador = g1.isCandleComprador(); |
||
| 610 | boolean g1Vendedor = g1.isCandleVendedor(); |
||
| 611 | |||
| 612 | boolean cComprador = c.isCandleComprador(); |
||
| 613 | boolean cVendedor = c.isCandleVendedor(); |
||
| 614 | |||
| 615 | if (g1Comprador && cVendedor) { |
||
| 616 | // quebra forte contra a compra |
||
| 617 | return c.getMinima() < g1.getMinima(); |
||
| 618 | } else if (g1Vendedor && cComprador) { |
||
| 619 | // quebra forte contra a venda |
||
| 620 | return c.getMaxima() > g1.getMaxima(); |
||
| 621 | } |
||
| 622 | |||
| 623 | return false; |
||
| 624 | } |
||
| 625 | |||
| 626 | |||
| 627 | |||
| 628 | // ============================================================ |
||
| 629 | // BLOCO: GATILHO 3 |
||
| 630 | // ============================================================ |
||
| 631 | |||
| 632 | /** |
||
| 633 | * G3: |
||
| 634 | * - Candle posterior ao G2. |
||
| 635 | * - Mesma tendência de G1. |
||
| 636 | * - Ref COMPRADORA: |
||
| 637 | * * topo de G3 < topo da referência |
||
| 638 | * * mínima de G3 < mínima de G2 (rompe fundo do G2) |
||
| 639 | * - Ref VENDEDORA: |
||
| 640 | * * topo de G3 > topo da referência |
||
| 641 | * * máxima de G3 > máxima de G2 (rompe topo do G2) |
||
| 642 | * (A invalidação por rompimento da referência é tratada em outro método.) |
||
| 643 | */ |
||
| 644 | private boolean isGatilho3(Candle referencia, Candle g1, Candle g2, Candle g3) { |
||
| 645 | |||
| 646 | boolean g1Comprador = g1.isCandleComprador(); |
||
| 647 | boolean g1Vendedor = g1.isCandleVendedor(); |
||
| 648 | |||
| 649 | boolean g3Comprador = g3.isCandleComprador(); |
||
| 650 | boolean g3Vendedor = g3.isCandleVendedor(); |
||
| 651 | |||
| 652 | boolean mesmaTendenciaG1 = |
||
| 653 | (g1Comprador && g3Comprador) || |
||
| 654 | (g1Vendedor && g3Vendedor); |
||
| 655 | |||
| 656 | if (!mesmaTendenciaG1) { |
||
| 657 | return false; |
||
| 658 | } |
||
| 659 | |||
| 660 | boolean refComprador = referencia.isCandleComprador(); |
||
| 661 | boolean refVendedor = referencia.isCandleVendedor(); |
||
| 662 | |||
| 663 | if (refComprador) { |
||
| 664 | boolean topoMaisBaixo = g3.getMaxima() < referencia.getMaxima(); |
||
| 665 | boolean rompeFundoG2 = g3.getMinima() < g2.getMinima(); |
||
| 666 | return topoMaisBaixo && rompeFundoG2; |
||
| 667 | } else if (refVendedor) { |
||
| 668 | boolean topoMaiorQueRef = g3.getMaxima() > referencia.getMaxima(); |
||
| 669 | boolean rompeTopoG2 = g3.getMaxima() > g2.getMaxima(); |
||
| 670 | return topoMaiorQueRef && rompeTopoG2; |
||
| 671 | } |
||
| 672 | |||
| 673 | return false; |
||
| 674 | } |
||
| 675 | |||
| 676 | /** |
||
| 677 | * Invalidação após o G2: |
||
| 678 | * - Ref COMPRADORA: se qualquer candle após G2 romper o topo da referência -> invalida. |
||
| 679 | * - Ref VENDEDORA: se romper o fundo da referência -> invalida. |
||
| 680 | */ |
||
| 681 | private boolean rompeReferenciaAposG2(Candle referencia, Candle c) { |
||
| 682 | if (referencia == null) return false; |
||
| 683 | |||
| 684 | boolean refComprador = referencia.isCandleComprador(); |
||
| 685 | boolean refVendedor = referencia.isCandleVendedor(); |
||
| 686 | |||
| 687 | if (refComprador) { |
||
| 688 | return c.getMaxima() > referencia.getMaxima(); |
||
| 689 | } else if (refVendedor) { |
||
| 690 | return c.getMinima() < referencia.getMinima(); |
||
| 691 | } |
||
| 692 | |||
| 693 | return false; |
||
| 694 | } |
||
| 695 | |||
| 696 | // ============================================================ |
||
| 697 | // BLOCO: GATILHO 4 |
||
| 698 | // ============================================================ |
||
| 699 | |||
| 700 | /** |
||
| 701 | * G4: |
||
| 702 | * - Desarma a operação. |
||
| 703 | * - Ref COMPRADORA: topo da referência rompido pelo candle G4. |
||
| 704 | * - Ref VENDEDORA: fundo da referência rompido pelo candle G4. |
||
| 705 | */ |
||
| 706 | private boolean isGatilho4(Candle referencia, Candle c) { |
||
| 707 | if (referencia == null) return false; |
||
| 708 | |||
| 709 | boolean refComprador = referencia.isCandleComprador(); |
||
| 710 | boolean refVendedor = referencia.isCandleVendedor(); |
||
| 711 | |||
| 712 | if (refComprador) { |
||
| 713 | return c.getMaxima() > referencia.getMaxima(); |
||
| 714 | } else if (refVendedor) { |
||
| 715 | return c.getMinima() < referencia.getMinima(); |
||
| 716 | } |
||
| 717 | |||
| 718 | return false; |
||
| 719 | } |
||
| 720 | |||
| 721 | |||
| 722 | // ============================================================ |
||
| 723 | // Getters (se quiser inspecionar estado atual externamente) |
||
| 724 | // ============================================================ |
||
| 725 | |||
| 726 | public Candle getReferencia() { |
||
| 727 | return referencia; |
||
| 728 | } |
||
| 729 | |||
| 730 | public Candle getGatilho1() { |
||
| 731 | return gatilho1; |
||
| 732 | } |
||
| 733 | |||
| 734 | public Candle getGatilho2() { |
||
| 735 | return gatilho2; |
||
| 736 | } |
||
| 737 | |||
| 738 | public Candle getGatilho3() { |
||
| 739 | return gatilho3; |
||
| 740 | } |
||
| 741 | |||
| 742 | public Candle getGatilho4() { |
||
| 743 | return gatilho4; |
||
| 744 | } |
||
| 745 | |||
| 746 | public PadraoGatilho getPadraoAtual() { |
||
| 747 | return padraoAtual; |
||
| 748 | } |
||
| 749 | } |