Subversion Repositories Integrator Subversion

Rev

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

package br.com.sl.domain.util;

import java.math.BigDecimal;
import java.math.RoundingMode;

public final class CandleMathUtils {

    private static final BigDecimal ZERO = BigDecimal.ZERO;
    private static final BigDecimal CEM  = BigDecimal.valueOf(100);

    private CandleMathUtils() {}

    /* ============================================================
       HELPERS
       ============================================================ */


    private static BigDecimal n(BigDecimal v) {
        return v == null ? ZERO : v;
    }

    private static BigDecimal max(BigDecimal a, BigDecimal b) {
        return n(a).compareTo(n(b)) >= 0 ? n(a) : n(b);
    }

    private static BigDecimal min(BigDecimal a, BigDecimal b) {
        return n(a).compareTo(n(b)) <= 0 ? n(a) : n(b);
    }

    /* ============================================================
       OPERAÇÕES BÁSICAS DO CANDLE
       ============================================================ */


    /**
     * Corpo do candle (fechamento - abertura).
     * Pode ser positivo (comprador), negativo (vendedor) ou zero (doji).
     */

    public static BigDecimal corpo(BigDecimal abertura, BigDecimal fechamento) {
        return n(fechamento).subtract(n(abertura));
    }

    /**
     * Módulo do corpo (tamanho absoluto).
     */

    public static BigDecimal corpoAbs(BigDecimal abertura, BigDecimal fechamento) {
        return corpo(abertura, fechamento).abs();
    }

    /**
     * Amplitude total do candle: máxima - mínima.
     */

    public static BigDecimal amplitude(BigDecimal maxima, BigDecimal minima) {
        return n(maxima).subtract(n(minima)).abs();
    }

    /**
     * Pavio superior: máxima - max(abertura, fechamento).
     */

    public static BigDecimal pavioSuperior(BigDecimal abertura, BigDecimal fechamento, BigDecimal maxima) {
        BigDecimal topoCorpo = max(abertura, fechamento);
        return n(maxima).subtract(topoCorpo).max(ZERO);
    }

    /**
     * Pavio inferior: min(abertura, fechamento) - mínima.
     */

    public static BigDecimal pavioInferior(BigDecimal abertura, BigDecimal fechamento, BigDecimal minima) {
        BigDecimal baseCorpo = min(abertura, fechamento);
        return baseCorpo.subtract(n(minima)).max(ZERO);
    }

    /* ============================================================
       CLASSIFICAÇÃO DO CANDLE
       ============================================================ */


    /**
     * Candle comprador (fechamento > abertura).
     */

    public static boolean isComprador(BigDecimal abertura, BigDecimal fechamento) {
        return n(fechamento).compareTo(n(abertura)) > 0;
    }

    /**
     * Candle vendedor (fechamento < abertura).
     */

    public static boolean isVendedor(BigDecimal abertura, BigDecimal fechamento) {
        return n(fechamento).compareTo(n(abertura)) < 0;
    }

    /**
     * Candle neutro (fechamento == abertura).
     */

    public static boolean isNeutro(BigDecimal abertura, BigDecimal fechamento) {
        return n(fechamento).compareTo(n(abertura)) == 0;
    }

    /**
     * Doji: corpo muito pequeno em relação à amplitude.
     * limiarPercentual é o máximo de % do corpo em relação à amplitude (ex: 10 → 10%).
     */

    public static boolean isDoji(BigDecimal abertura,
                                 BigDecimal fechamento,
                                 BigDecimal maxima,
                                 BigDecimal minima,
                                 BigDecimal limiarPercentual) {

        BigDecimal amp = amplitude(maxima, minima);
        if (amp.compareTo(ZERO) == 0) {
            // Candle "travado" (sem range)
            return true;
        }

        BigDecimal corpoAbs = corpoAbs(abertura, fechamento);
        // (corpo / amplitude) * 100
        BigDecimal perc = corpoAbs
                .multiply(CEM)
                .divide(amp, 6, RoundingMode.HALF_UP);

        return perc.compareTo(n(limiarPercentual)) <= 0;
    }

    /* ============================================================
       POSIÇÃO DO FECHAMENTO DENTRO DO CANDLE
       ============================================================ */


    /**
     * Retorna o percentual do fechamento dentro da amplitude do candle.
     * 0% = na mínima, 100% = na máxima.
     */

    public static BigDecimal percentualFechamentoNaAmplitude(BigDecimal abertura,
                                                             BigDecimal fechamento,
                                                             BigDecimal maxima,
                                                             BigDecimal minima) {
        BigDecimal amp = amplitude(maxima, minima);
        if (amp.compareTo(ZERO) == 0) {
            return ZERO;
        }

        // (fechamento - minima) / amplitude * 100
        BigDecimal num = n(fechamento).subtract(n(minima));
        return num
                .multiply(CEM)
                .divide(amp, 6, RoundingMode.HALF_UP);
    }

    /**
     * Retorna o percentual de retração do fechamento a partir da máxima até a mínima.
     * 0% = na máxima, 100% = na mínima.
     */

    public static BigDecimal percentualRetraidoDaMaxima(BigDecimal fechamento,
                                                        BigDecimal maxima,
                                                        BigDecimal minima) {
        BigDecimal amp = amplitude(maxima, minima);
        if (amp.compareTo(ZERO) == 0) {
            return ZERO;
        }

        // (maxima - fechamento) / amplitude * 100
        BigDecimal num = n(maxima).subtract(n(fechamento));
        return num
                .multiply(CEM)
                .divide(amp, 6, RoundingMode.HALF_UP);
    }

    /* ============================================================
       VERSÕES EM double (para gráficos, logs, etc.)
       ============================================================ */


    public static double corpoDouble(BigDecimal abertura, BigDecimal fechamento) {
        return corpo(abertura, fechamento).doubleValue();
    }

    public static double amplitudeDouble(BigDecimal maxima, BigDecimal minima) {
        return amplitude(maxima, minima).doubleValue();
    }

    public static double pavioSuperiorDouble(BigDecimal abertura, BigDecimal fechamento, BigDecimal maxima) {
        return pavioSuperior(abertura, fechamento, maxima).doubleValue();
    }

    public static double pavioInferiorDouble(BigDecimal abertura, BigDecimal fechamento, BigDecimal minima) {
        return pavioInferior(abertura, fechamento, minima).doubleValue();
    }

    public static double percentualFechamentoNaAmplitudeDouble(BigDecimal abertura,
                                                               BigDecimal fechamento,
                                                               BigDecimal maxima,
                                                               BigDecimal minima) {
        return percentualFechamentoNaAmplitude(abertura, fechamento, maxima, minima).doubleValue();
    }

    public static double percentualRetraidoDaMaximaDouble(BigDecimal fechamento,
                                                          BigDecimal maxima,
                                                          BigDecimal minima) {
        return percentualRetraidoDaMaxima(fechamento, maxima, minima).doubleValue();
    }
   
}