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();
}
}