Subversion Repositories Integrator Subversion

Rev

Blame | Last modification | View Log | Download | RSS feed

package br.com.robo.model;

import java.util.ArrayList;
import java.util.List;


/**
 * Identifica sequências de 3 gatilhos em tendência de alta,
 * conforme regras fornecidas.
 */

public class TriggerFinder {

    /**
     * Representa um padrão completo de gatilhos:
     * - refIndex: candle de referência (topo da perna de alta)
     * - g1Index: índice do Gatilho tipo 1
     * - g2Index: índice do Gatilho tipo 2
     * - g3Index: índice do Gatilho tipo 3
     */

    public static class TriggerPattern {
        private final int refIndex;
        private final int g1Index;
        private final int g2Index;
        private final int g3Index;

        public TriggerPattern(int refIndex, int g1Index, int g2Index, int g3Index) {
            this.refIndex = refIndex;
            this.g1Index = g1Index;
            this.g2Index = g2Index;
            this.g3Index = g3Index;
        }

        public int getRefIndex() {
            return refIndex;
        }

        public int getG1Index() {
            return g1Index;
        }

        public int getG2Index() {
            return g2Index;
        }

        public int getG3Index() {
            return g3Index;
        }

        @Override
        public String toString() {
            return "TriggerPattern{" +
                    "refIndex=" + refIndex +
                    ", g1Index=" + g1Index +
                    ", g2Index=" + g2Index +
                    ", g3Index=" + g3Index +
                    '}';
        }
    }

    /**
     * Encontra todos os padrões de 3 gatilhos em tendência de alta
     * na lista de candles.
     */

    public List<TriggerPattern> findTriggers(List<Candle> candles) {
        List<TriggerPattern> patterns = new ArrayList<>();
        if (candles == null || candles.size() < 5) {
            return patterns;
        }

        int i = 2; // começa em 2 por causa da checagem de tendência
        while (i < candles.size() - 3) {

            int refIndex = i - 1;
            Candle ref = candles.get(refIndex);
            Candle possibleG1 = candles.get(i);

            // 1) Tendência de alta antes do refIndex
            if (!isUptrend(candles, refIndex)) {
                i++;
                continue;
            }

            // 2) Candle de referência deve ser comprador
            if (!isBullish(ref)) {
                i++;
                continue;
            }

            // 3) Gatilho tipo 1: candle vendedor contrário ao ref
            if (!isBearish(possibleG1)) {
                i++;
                continue;
            }

            int g1Index = i;
            Candle g1 = possibleG1;

            int g2Index = -1;
            int g3Index = -1;

            // -------------------------------
            // 4) Procurar G2
            //
            // Gatilho tipo 2:
            // - Candle comprador dentro da região da referência (ref)
            //      => high(G2) <= high(ref) e low(G2) >= low(ref)
            //   OU
            // - Candle vendedor rompendo com pavio o topo do G1
            //      => candle vendedor e high(G2) > high(G1)
            //
            // IMPORTANTE: aqui AINDA NÃO vale a regra especial
            // do rompimento do topo da referência; ela só entra
            // na etapa de busca do G3.
            // -------------------------------
            int j = g1Index + 1;
            while (j < candles.size() - 1) { // deixa pelo menos 1 candle depois pra ser o G3
                Candle c = candles.get(j);

                boolean isG2 = false;

                // Caso 1: comprador dentro da região da referência
                boolean compradorDentroRef =
                        isBullish(c)
                        && c.getHigh() <= ref.getHigh()
                        && c.getLow() >= ref.getLow();

                // Caso 2: vendedor rompendo com pavio o topo do G1
                // (pavio rompe topo do G1)
                boolean vendedorRompeTopoG1 =
                        isBearish(c)
                        && c.getHigh() > g1.getHigh();

                if (compradorDentroRef || vendedorRompeTopoG1) {
                    isG2 = true;
                }

                if (isG2) {
                    g2Index = j;
                    break;
                }

                j++;
            }

            // Se não achou G2, avança e segue a busca
            if (g2Index == -1) {
                i++;
                continue;
            }

            Candle g2 = candles.get(g2Index);

            // -------------------------------
            // 5) Procurar G3
            //
            // Gatilho tipo 3:
            // - Candle vendedor
            // - topo < topo do candle de referência
            // - rompe o fundo do G2 (low(G3) < low(G2))
            //
            // REGRA ESPECIAL:
            // "Se houver o rompimento do topo do referência,
            //  não considerar mais os gatilhos definidos anteriormente."
            //
            // Ou seja: durante a busca do G3, se QUALQUER candle tiver
            // high > high(ref), invalida essa sequência.
            // -------------------------------
            boolean invalidSequence = false;
            int k = g2Index + 1;
            while (k < candles.size()) {
                Candle c = candles.get(k);

                // Regra especial: rompimento do topo da referência
                if (c.getHigh() > ref.getHigh()) {
                    invalidSequence = true;
                    break;
                }

                if (isBearish(c)
                        && c.getHigh() < ref.getHigh()
                        && c.getLow() < g2.getLow()) {
                    g3Index = k;
                    break;
                }

                k++;
            }

            if (invalidSequence) {
                // sequência descartada, segue a partir do candle
                // que rompeu o topo da referência
                i = k + 1;
                continue;
            }

            if (g3Index != -1) {
                patterns.add(new TriggerPattern(refIndex, g1Index, g2Index, g3Index));
                // pula pra depois do G3 pra evitar muitos padrões sobrepostos
                i = g3Index + 1;
            } else {
                // não houve G3, segue a partir do próximo candle após G1
                i = g1Index + 1;
            }
        }

        return patterns;
    }

    // ---------- Funções auxiliares ----------

    private boolean isBullish(Candle c) {
        return c.getClose() > c.getOpen();
    }

    private boolean isBearish(Candle c) {
        return c.getClose() < c.getOpen();
    }

    /**
     * Tendência de alta simples:
     * close[ref] > close[ref-1] > close[ref-2]
     */

    private boolean isUptrend(List<Candle> candles, int refIndex) {
        if (refIndex < 2) {
            return false;
        }
        double c0 = candles.get(refIndex).getClose();
        double c1 = candles.get(refIndex - 1).getClose();
        double c2 = candles.get(refIndex - 2).getClose();
        return c0 > c1 && c1 > c2;
    }

}