Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 760 | blopes | 1 | package br.com.robo.model; |
| 2 | |||
| 3 | import java.util.ArrayList; |
||
| 4 | import java.util.List; |
||
| 5 | |||
| 6 | |||
| 7 | /** |
||
| 8 | * Identifica sequências de 3 gatilhos em tendência de alta, |
||
| 9 | * conforme regras fornecidas. |
||
| 10 | */ |
||
| 11 | public class TriggerFinder { |
||
| 12 | |||
| 13 | /** |
||
| 14 | * Representa um padrão completo de gatilhos: |
||
| 15 | * - refIndex: candle de referência (topo da perna de alta) |
||
| 16 | * - g1Index: índice do Gatilho tipo 1 |
||
| 17 | * - g2Index: índice do Gatilho tipo 2 |
||
| 18 | * - g3Index: índice do Gatilho tipo 3 |
||
| 19 | */ |
||
| 20 | public static class TriggerPattern { |
||
| 21 | private final int refIndex; |
||
| 22 | private final int g1Index; |
||
| 23 | private final int g2Index; |
||
| 24 | private final int g3Index; |
||
| 25 | |||
| 26 | public TriggerPattern(int refIndex, int g1Index, int g2Index, int g3Index) { |
||
| 27 | this.refIndex = refIndex; |
||
| 28 | this.g1Index = g1Index; |
||
| 29 | this.g2Index = g2Index; |
||
| 30 | this.g3Index = g3Index; |
||
| 31 | } |
||
| 32 | |||
| 33 | public int getRefIndex() { |
||
| 34 | return refIndex; |
||
| 35 | } |
||
| 36 | |||
| 37 | public int getG1Index() { |
||
| 38 | return g1Index; |
||
| 39 | } |
||
| 40 | |||
| 41 | public int getG2Index() { |
||
| 42 | return g2Index; |
||
| 43 | } |
||
| 44 | |||
| 45 | public int getG3Index() { |
||
| 46 | return g3Index; |
||
| 47 | } |
||
| 48 | |||
| 49 | @Override |
||
| 50 | public String toString() { |
||
| 51 | return "TriggerPattern{" + |
||
| 52 | "refIndex=" + refIndex + |
||
| 53 | ", g1Index=" + g1Index + |
||
| 54 | ", g2Index=" + g2Index + |
||
| 55 | ", g3Index=" + g3Index + |
||
| 56 | '}'; |
||
| 57 | } |
||
| 58 | } |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Encontra todos os padrões de 3 gatilhos em tendência de alta |
||
| 62 | * na lista de candles. |
||
| 63 | */ |
||
| 64 | public List<TriggerPattern> findTriggers(List<Candle> candles) { |
||
| 65 | List<TriggerPattern> patterns = new ArrayList<>(); |
||
| 66 | if (candles == null || candles.size() < 5) { |
||
| 67 | return patterns; |
||
| 68 | } |
||
| 69 | |||
| 70 | int i = 2; // começa em 2 por causa da checagem de tendência |
||
| 71 | while (i < candles.size() - 3) { |
||
| 72 | |||
| 73 | int refIndex = i - 1; |
||
| 74 | Candle ref = candles.get(refIndex); |
||
| 75 | Candle possibleG1 = candles.get(i); |
||
| 76 | |||
| 77 | // 1) Tendência de alta antes do refIndex |
||
| 78 | if (!isUptrend(candles, refIndex)) { |
||
| 79 | i++; |
||
| 80 | continue; |
||
| 81 | } |
||
| 82 | |||
| 83 | // 2) Candle de referência deve ser comprador |
||
| 84 | if (!isBullish(ref)) { |
||
| 85 | i++; |
||
| 86 | continue; |
||
| 87 | } |
||
| 88 | |||
| 89 | // 3) Gatilho tipo 1: candle vendedor contrário ao ref |
||
| 90 | if (!isBearish(possibleG1)) { |
||
| 91 | i++; |
||
| 92 | continue; |
||
| 93 | } |
||
| 94 | |||
| 95 | int g1Index = i; |
||
| 96 | Candle g1 = possibleG1; |
||
| 97 | |||
| 98 | int g2Index = -1; |
||
| 99 | int g3Index = -1; |
||
| 100 | |||
| 101 | // ------------------------------- |
||
| 102 | // 4) Procurar G2 |
||
| 103 | // |
||
| 104 | // Gatilho tipo 2: |
||
| 105 | // - Candle comprador dentro da região da referência (ref) |
||
| 106 | // => high(G2) <= high(ref) e low(G2) >= low(ref) |
||
| 107 | // OU |
||
| 108 | // - Candle vendedor rompendo com pavio o topo do G1 |
||
| 109 | // => candle vendedor e high(G2) > high(G1) |
||
| 110 | // |
||
| 111 | // IMPORTANTE: aqui AINDA NÃO vale a regra especial |
||
| 112 | // do rompimento do topo da referência; ela só entra |
||
| 113 | // na etapa de busca do G3. |
||
| 114 | // ------------------------------- |
||
| 115 | int j = g1Index + 1; |
||
| 116 | while (j < candles.size() - 1) { // deixa pelo menos 1 candle depois pra ser o G3 |
||
| 117 | Candle c = candles.get(j); |
||
| 118 | |||
| 119 | boolean isG2 = false; |
||
| 120 | |||
| 121 | // Caso 1: comprador dentro da região da referência |
||
| 122 | boolean compradorDentroRef = |
||
| 123 | isBullish(c) |
||
| 124 | && c.getHigh() <= ref.getHigh() |
||
| 125 | && c.getLow() >= ref.getLow(); |
||
| 126 | |||
| 127 | // Caso 2: vendedor rompendo com pavio o topo do G1 |
||
| 128 | // (pavio rompe topo do G1) |
||
| 129 | boolean vendedorRompeTopoG1 = |
||
| 130 | isBearish(c) |
||
| 131 | && c.getHigh() > g1.getHigh(); |
||
| 132 | |||
| 133 | if (compradorDentroRef || vendedorRompeTopoG1) { |
||
| 134 | isG2 = true; |
||
| 135 | } |
||
| 136 | |||
| 137 | if (isG2) { |
||
| 138 | g2Index = j; |
||
| 139 | break; |
||
| 140 | } |
||
| 141 | |||
| 142 | j++; |
||
| 143 | } |
||
| 144 | |||
| 145 | // Se não achou G2, avança e segue a busca |
||
| 146 | if (g2Index == -1) { |
||
| 147 | i++; |
||
| 148 | continue; |
||
| 149 | } |
||
| 150 | |||
| 151 | Candle g2 = candles.get(g2Index); |
||
| 152 | |||
| 153 | // ------------------------------- |
||
| 154 | // 5) Procurar G3 |
||
| 155 | // |
||
| 156 | // Gatilho tipo 3: |
||
| 157 | // - Candle vendedor |
||
| 158 | // - topo < topo do candle de referência |
||
| 159 | // - rompe o fundo do G2 (low(G3) < low(G2)) |
||
| 160 | // |
||
| 161 | // REGRA ESPECIAL: |
||
| 162 | // "Se houver o rompimento do topo do referência, |
||
| 163 | // não considerar mais os gatilhos definidos anteriormente." |
||
| 164 | // |
||
| 165 | // Ou seja: durante a busca do G3, se QUALQUER candle tiver |
||
| 166 | // high > high(ref), invalida essa sequência. |
||
| 167 | // ------------------------------- |
||
| 168 | boolean invalidSequence = false; |
||
| 169 | int k = g2Index + 1; |
||
| 170 | while (k < candles.size()) { |
||
| 171 | Candle c = candles.get(k); |
||
| 172 | |||
| 173 | // Regra especial: rompimento do topo da referência |
||
| 174 | if (c.getHigh() > ref.getHigh()) { |
||
| 175 | invalidSequence = true; |
||
| 176 | break; |
||
| 177 | } |
||
| 178 | |||
| 179 | if (isBearish(c) |
||
| 180 | && c.getHigh() < ref.getHigh() |
||
| 181 | && c.getLow() < g2.getLow()) { |
||
| 182 | g3Index = k; |
||
| 183 | break; |
||
| 184 | } |
||
| 185 | |||
| 186 | k++; |
||
| 187 | } |
||
| 188 | |||
| 189 | if (invalidSequence) { |
||
| 190 | // sequência descartada, segue a partir do candle |
||
| 191 | // que rompeu o topo da referência |
||
| 192 | i = k + 1; |
||
| 193 | continue; |
||
| 194 | } |
||
| 195 | |||
| 196 | if (g3Index != -1) { |
||
| 197 | patterns.add(new TriggerPattern(refIndex, g1Index, g2Index, g3Index)); |
||
| 198 | // pula pra depois do G3 pra evitar muitos padrões sobrepostos |
||
| 199 | i = g3Index + 1; |
||
| 200 | } else { |
||
| 201 | // não houve G3, segue a partir do próximo candle após G1 |
||
| 202 | i = g1Index + 1; |
||
| 203 | } |
||
| 204 | } |
||
| 205 | |||
| 206 | return patterns; |
||
| 207 | } |
||
| 208 | |||
| 209 | // ---------- Funções auxiliares ---------- |
||
| 210 | |||
| 211 | private boolean isBullish(Candle c) { |
||
| 212 | return c.getClose() > c.getOpen(); |
||
| 213 | } |
||
| 214 | |||
| 215 | private boolean isBearish(Candle c) { |
||
| 216 | return c.getClose() < c.getOpen(); |
||
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * Tendência de alta simples: |
||
| 221 | * close[ref] > close[ref-1] > close[ref-2] |
||
| 222 | */ |
||
| 223 | private boolean isUptrend(List<Candle> candles, int refIndex) { |
||
| 224 | if (refIndex < 2) { |
||
| 225 | return false; |
||
| 226 | } |
||
| 227 | double c0 = candles.get(refIndex).getClose(); |
||
| 228 | double c1 = candles.get(refIndex - 1).getClose(); |
||
| 229 | double c2 = candles.get(refIndex - 2).getClose(); |
||
| 230 | return c0 > c1 && c1 > c2; |
||
| 231 | } |
||
| 232 | |||
| 233 | } |