package br.com.kronus.core;
import java.util.ArrayList;
import java.util.List;
import br.com.sl.domain.model.Candle;
import br.com.sl.domain.util.BigDecimalUtils;
/**
* Detector de padrões de gatilhos (GR, G1, G2, G3)
* trabalhando tanto em modo backtest quanto em modo tempo real.
*
* IMPORTANTE:
* - Este detector FINALIZA o padrão assim que identifica o G3.
* - O G4 será identificado apenas na camada de sinais de trade, fora desta classe.
*
* REGRAS IMPLEMENTADAS (versão alinhada)
* ======================================
*
* 1) PADRÃO COMPRADOR
* --------------------
* Ponto de partida:
* - Um candle COMPRADOR é tomado como CANDIDATO À REFERÊNCIA.
*
* GR DINÂMICO (compra):
* - Enquanto NÃO houver G1:
* * Qualquer candle DIRECIONAL posterior (comprador ou vendedor)
* que fizer uma MÁXIMA MAIOR que a do candidato atual
* passa a ser o NOVO candidato à referência.
*
* G1 (compra):
* - Pode ser QUALQUER candle vendedor subsequente (não precisa ser o primeiro),
* desde que ROMPA o FUNDO do candidato:
* mínima(candle vendedor) < mínima(candidatoRef).
* - No momento em que isso ocorrer:
* * Esse candle vendedor é o G1.
* * O candidato vigente passa a ser o GR (gatilho referência).
*
* G2 (compra):
* - Após o G1, com GR definido:
* * Procurar uma nova tendência COMPRADORA:
* - Achar o primeiro candle COMPRADOR após o G1.
* - Seguir a sequência compradora (ignorando INSIDE e neutros)
* até aparecer candle vendedor.
* * O ÚLTIMO candle dessa tendência compradora será candidato a G2.
* * Para ser G2:
* - O FECHAMENTO deve ficar DENTRO da região [mínGR, máxGR].
* * Se mudar para vendedor sem nenhum candle fechar dentro da região do GR:
* - descarta o padrão.
* - Regra global comprador:
* * Se qualquer candle, após o GR, ROMPER o TOPO do GR
* (máxima > máximaGR) em qualquer ponto antes da conclusão:
* - descarta o padrão.
*
* G3 (compra) – NOVA REGRA:
* - Após o G2:
* * Se ALGUM candle vendedor subsequente:
* - romper o FUNDO do G2 (mínima < mínimaG2) E
* - tiver TOPO menor ou igual ao TOPO do GR (máxima ≤ máximaGR)
* então esse candle será o G3.
* - Não precisa ser o primeiro vendedor após o G2; pode haver mudança de tendência,
* candles inside, etc., desde que a regra acima seja satisfeita e não ocorra:
* * outside antes do G3, ou
* * rompimento do topo do GR (regra global).
* - Ao identificar G3, o padrão é finalizado.
*
* 2) PADRÃO VENDEDOR
* -------------------
* Ponto de partida:
* - Um candle VENDEDOR é tomado como CANDIDATO À REFERÊNCIA.
*
* GR DINÂMICO (venda):
* - Enquanto NÃO houver G1:
* * Qualquer candle DIRECIONAL posterior (comprador ou vendedor)
* que fizer uma MÍNIMA MENOR que a do candidato atual
* passa a ser o NOVO candidato à referência.
*
* G1 (venda):
* - Pode ser QUALQUER candle comprador subsequente (não precisa ser o primeiro),
* desde que ROMPA o TOPO do candidato:
* máxima(candle comprador) > máxima(candidatoRef).
* - No momento em que isso ocorrer:
* * Esse candle comprador é o G1.
* * O candidato vigente passa a ser o GR (gatilho referência).
*
* Regra global vendedor:
* - Se qualquer candle posterior ao GR ROMPER o FUNDO do GR
* (mínima < mínimaGR) em qualquer momento antes da conclusão:
* - descarta o padrão.
*
* G2 (venda):
* - Após o G1, com GR definido:
* * Procurar uma nova tendência VENDEDORA:
* - Achar o primeiro VENDEDOR após o G1.
* - Seguir a sequência vendedora (ignorando INSIDE e neutros)
* até aparecer candle comprador.
* * O ÚLTIMO candle dessa tendência vendedora será candidato a G2.
* * Para ser G2:
* - O FECHAMENTO deve ficar DENTRO da região [mínGR, máxGR].
* * Se mudar para comprador sem nenhum candle fechar dentro da região do GR:
* - descarta o padrão.
* * Se durante essa fase algum candle romper o FUNDO do GR
* (mínima < mínimaGR) → descarta o padrão (regra global).
*
* G3 (venda) – NOVA REGRA:
* - Após o G2:
* * Se ALGUM candle comprador subsequente:
* - romper o TOPO do G2 (máxima > máximaG2) E
* - tiver FUNDO maior ou igual ao FUNDO do GR (mínima ≥ mínimaGR)
* então esse candle será o G3.
* - Não precisa ser o primeiro comprador após o G2; pode haver mudança de tendência,
* candles inside, etc., desde que a regra acima seja satisfeita e não ocorra:
* * outside antes do G3, ou
* * rompimento do fundo do GR (regra global).
* - Ao identificar G3, o padrão é finalizado.
*
* 3) OUTSIDE (ambos os lados)
* ---------------------------
* - Candle cuja:
* máxima > máxima(candle anterior)
* E mínima < mínima(candle anterior).
* - Se houver OUTSIDE em qualquer estágio antes de G3:
* - o padrão atual (GR/G1/G2) é descartado.
*
* 4) ORDEM CRONOLÓGICA
* ---------------------
* - Sempre GR -> G1 -> G2 -> G3.
* - Todos em candles diferentes, na ordem do tempo.
*/
public class DetectorGatilhos
{
private final boolean logAtivo
;
private int idxProximaAnaliseTempoReal =
0;
public DetectorGatilhos
() {
this(false);
}
public DetectorGatilhos
(boolean logAtivo
) {
this.
logAtivo = logAtivo
;
}
private void log
(String msg
) {
if (logAtivo
) {
System.
out.
println(msg
);
}
}
// -------------------------------------------------------------
// Estrutura interna de retorno
// -------------------------------------------------------------
private static class ResultadoPadrao
{
PadraoGatilho padrao
;
int lastIndex
;
ResultadoPadrao
(PadraoGatilho padrao,
int lastIndex
) {
this.
padrao = padrao
;
this.
lastIndex = lastIndex
;
}
}
private ResultadoPadrao criarResultadoParcialComG2
(Candle gr,
Candle g1,
Candle g2,
int lastIndex
) {
PadraoGatilho padrao =
new PadraoGatilho
();
padrao.
setReferencia(gr
);
padrao.
setGatilho1(g1
);
padrao.
setGatilho2(g2
);
padrao.
setGatilho3(null);
padrao.
setGatilho4(null); // G4 só na camada de trade
return new ResultadoPadrao
(padrao, lastIndex
);
}
// =====================================================================
// API PRINCIPAL – BACKTEST
// =====================================================================
public List<PadraoGatilho
> identificarPadroes
(List<Candle
> candles
) {
List<PadraoGatilho
> padroes =
new ArrayList<>();
int n = candles.
size();
if (n
< 4) return padroes
;
int idxRef =
0;
while (idxRef
< n -
3) {
ResultadoPadrao resultado = detectarPadraoAPartir
(candles, idxRef
);
if (resultado ==
null) {
idxRef++
;
continue;
}
if (resultado.
padrao !=
null) {
padroes.
add(resultado.
padrao);
}
idxRef =
Math.
max(resultado.
lastIndex +
1, idxRef +
1);
}
return padroes
;
}
private ResultadoPadrao detectarPadraoAPartir
(List<Candle
> candles,
int idxRef
) {
Candle ref = candles.
get(idxRef
);
if (ref.
isCandleComprador()) {
return detectarPadraoComprador
(candles, idxRef
);
} else if (ref.
isCandleVendedor()) {
return detectarPadraoVendedor
(candles, idxRef
);
} else {
return new ResultadoPadrao
(null, idxRef
);
}
}
// =====================================================================
// HELPERS
// =====================================================================
private boolean isInside
(List<Candle
> candles,
int idx
) {
if (idx
<=
0) return false;
Candle atual = candles.
get(idx
);
Candle anterior = candles.
get(idx -
1);
return BigDecimalUtils.
ehMenorOuIgualQue(atual.
getMaxima(), anterior.
getMaxima())
&& BigDecimalUtils.
ehMaiorOuIgualQue(atual.
getMinima(), anterior.
getMinima());
}
private boolean isOutside
(List<Candle
> candles,
int idx
) {
if (idx
<=
0) return false;
Candle atual = candles.
get(idx
);
Candle anterior = candles.
get(idx -
1);
return BigDecimalUtils.
ehMaiorQue(atual.
getMaxima(), anterior.
getMaxima())
&& BigDecimalUtils.
ehMenorQue(atual.
getMinima(), anterior.
getMinima());
}
private boolean isDirecional
(Candle c
) {
return c.
isCandleComprador() || c.
isCandleVendedor();
}
// =====================================================================
// CASO COMPRADOR – GR DINÂMICO PELA MÁXIMA
// =====================================================================
private ResultadoPadrao detectarPadraoComprador
(List<Candle
> candles,
int idxInicio
) {
int n = candles.
size();
int lastIndex = idxInicio
;
Candle candidatoRef = candles.
get(idxInicio
);
if (!candidatoRef.
isCandleComprador()) {
return new ResultadoPadrao
(null, idxInicio
);
}
int idxCandidatoRef = idxInicio
;
// --------------------
// 1) Buscar G1: QUALQUER vendedor subsequente que rompa o fundo do candidatoRef
// (com GR dinâmico pela MÁXIMA antes de G1)
// --------------------
Candle g1 =
null;
int idxG1 = -
1;
Candle gr =
null;
int idxGR = -
1;
for (int i = idxInicio +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
)) {
continue;
}
// OUTSIDE antes de G1 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = i
;
log
(String.
format("Comprador: OUTSIDE em [%d] antes de G1. Abortando padrão.", i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// GR dinâmico (compra): atualiza candidatoRef pelo TOPO mais alto
// enquanto não existir G1
if (BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), candidatoRef.
getMaxima())) {
candidatoRef = c
;
idxCandidatoRef = i
;
lastIndex = i
;
continue;
}
// G1: QUALQUER vendedor subsequente que rompa o fundo do candidatoRef
if (c.
isCandleVendedor()
&& BigDecimalUtils.
ehMenorQue(c.
getMinima(), candidatoRef.
getMinima())) {
g1 = c
;
idxG1 = i
;
gr = candidatoRef
;
idxGR = idxCandidatoRef
;
lastIndex = i
;
break;
}
lastIndex = i
;
}
if (g1 ==
null || gr ==
null) {
log
(String.
format("Comprador: não foi possível formar G1 a partir de idx %d.", idxInicio
));
return new ResultadoPadrao
(null, lastIndex
);
}
log
(String.
format("GR COMPRADOR em [%d], G1 (vendedor) em [%d].", idxGR, idxG1
));
// --------------------
// 2) Buscar G2 – tendência COMPRADORA com fechamento dentro do GR
// --------------------
Candle g2 =
null;
int idxG2 = -
1;
int idxPrimeiroComprador = -
1;
for (int i = idxG1 +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
))
continue;
// OUTSIDE antes de G2 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] antes da tendência compradora de G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global comprador: se romper topo do GR => descarta
if (BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), gr.
getMaxima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu TOPO do GR antes/na formação de G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleComprador()) {
idxPrimeiroComprador = i
;
break;
} else {
lastIndex = i
;
}
}
if (idxPrimeiroComprador == -
1) {
log
(String.
format("GR[%d], G1[%d]: não houve nova tendência compradora para G2.", idxGR, idxG1
));
return new ResultadoPadrao
(null, lastIndex
);
}
Candle ultimoComprador =
null;
int idxUltimoComprador = -
1;
for (int i = idxPrimeiroComprador
; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
))
continue;
// OUTSIDE durante G2 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (comprador).", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global comprador: se romper topo do GR => descarta
if (BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), gr.
getMaxima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu TOPO do GR durante G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleComprador()) {
ultimoComprador = c
;
idxUltimoComprador = i
;
lastIndex = i
;
} else if (c.
isCandleVendedor()) {
lastIndex = i -
1;
break;
}
}
if (ultimoComprador ==
null) {
log
(String.
format("GR[%d], G1[%d]: não houve comprador válido para G2.", idxGR, idxG1
));
return new ResultadoPadrao
(null, lastIndex
);
}
boolean fechamentoDentroRegiaoGR =
BigDecimalUtils.
ehMaiorOuIgualQue(ultimoComprador.
getFechamento(), gr.
getMinima()) &&
BigDecimalUtils.
ehMenorOuIgualQue(ultimoComprador.
getFechamento(), gr.
getMaxima());
if (!fechamentoDentroRegiaoGR
) {
log
(String.
format(
"GR[%d], G1[%d]: último comprador[%d] NÃO fechou dentro da região do GR.",
idxGR, idxG1, idxUltimoComprador
));
return new ResultadoPadrao
(null, lastIndex
);
}
g2 = ultimoComprador
;
idxG2 = idxUltimoComprador
;
log
(String.
format("GR[%d], G1[%d] => G2 (comprador) em [%d].", idxGR, idxG1, idxG2
));
// --------------------
// 3) Buscar G3 – NOVA REGRA:
// QUALQUER vendedor subsequente que rompa fundo de G2
// e tenha topo <= topo do GR.
// --------------------
Candle g3 =
null;
int idxG3 = -
1;
for (int i = idxG2 +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (!isDirecional
(c
) || isInside
(candles, i
)) {
continue;
}
// OUTSIDE antes/durante busca de G3 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] durante busca de G3 (vendedor).", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global comprador: romper topo do GR => descarta
if (BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), gr.
getMaxima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu TOPO do GR durante busca de G3.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleVendedor()) {
boolean rompeFundoG2 = BigDecimalUtils.
ehMenorQue(c.
getMinima(), g2.
getMinima());
boolean topoMenorOuIgualGR = BigDecimalUtils.
ehMenorOuIgualQue(c.
getMaxima(), gr.
getMaxima());
if (rompeFundoG2
&& topoMenorOuIgualGR
) {
g3 = c
;
idxG3 = i
;
lastIndex = i
;
break;
}
}
}
if (g3 ==
null) {
log
(String.
format("GR[%d], G1[%d], G2[%d]: não houve G3 (comprador -> padrão parcial).",
idxGR, idxG1, idxG2
));
return criarResultadoParcialComG2
(gr, g1, g2, lastIndex
);
}
log
(String.
format("GR[%d], G1[%d], G2[%d] => G3 (vendedor) em [%d].",
idxGR, idxG1, idxG2, idxG3
));
PadraoGatilho padrao =
new PadraoGatilho
();
padrao.
setReferencia(gr
);
padrao.
setGatilho1(g1
);
padrao.
setGatilho2(g2
);
padrao.
setGatilho3(g3
);
padrao.
setGatilho4(null);
return new ResultadoPadrao
(padrao, lastIndex
);
}
// =====================================================================
// CASO VENDEDOR – GR DINÂMICO PELA MÍNIMA
// =====================================================================
private ResultadoPadrao detectarPadraoVendedor
(List<Candle
> candles,
int idxInicio
) {
int n = candles.
size();
int lastIndex = idxInicio
;
Candle candidatoRef = candles.
get(idxInicio
);
if (!candidatoRef.
isCandleVendedor()) {
return new ResultadoPadrao
(null, idxInicio
);
}
int idxCandidatoRef = idxInicio
;
// --------------------
// 1) Buscar G1: QUALQUER comprador subsequente que rompa o topo do candidatoRef
// (com GR dinâmico pela MÍNIMA antes de G1)
// --------------------
Candle g1 =
null;
int idxG1 = -
1;
Candle gr =
null;
int idxGR = -
1;
for (int i = idxInicio +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
)) {
continue;
}
// OUTSIDE antes de G1 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = i
;
log
(String.
format("Vendedor: OUTSIDE em [%d] antes de G1. Abortando padrão.", i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// GR dinâmico (venda): atualiza candidatoRef pelo FUNDO mais baixo
// enquanto não existir G1
if (BigDecimalUtils.
ehMenorQue(c.
getMinima(), candidatoRef.
getMinima())) {
candidatoRef = c
;
idxCandidatoRef = i
;
lastIndex = i
;
continue;
}
// G1: QUALQUER comprador subsequente que rompa o topo do candidatoRef
if (c.
isCandleComprador()
&& BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), candidatoRef.
getMaxima())) {
g1 = c
;
idxG1 = i
;
gr = candidatoRef
;
idxGR = idxCandidatoRef
;
lastIndex = i
;
break;
}
lastIndex = i
;
}
if (g1 ==
null || gr ==
null) {
log
(String.
format("Vendedor: não foi possível formar G1 a partir de idx %d.", idxInicio
));
return new ResultadoPadrao
(null, lastIndex
);
}
log
(String.
format("GR VENDEDOR em [%d], G1 (comprador) em [%d].", idxGR, idxG1
));
// --------------------
// 2) Buscar G2 – tendência VENDEDORA com fechamento dentro do GR
// --------------------
Candle g2 =
null;
int idxG2 = -
1;
int idxPrimeiroVendedor = -
1;
for (int i = idxG1 +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
)) continue;
// OUTSIDE antes de G2 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] antes da tendência vendedora de G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global vendedor: romper fundo do GR => descarta
if (BigDecimalUtils.
ehMenorQue(c.
getMinima(), gr.
getMinima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu FUNDO do GR antes/na formação de G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleVendedor()) {
idxPrimeiroVendedor = i
;
break;
} else {
lastIndex = i
;
}
}
if (idxPrimeiroVendedor == -
1) {
log
(String.
format("GR[%d], G1[%d]: não houve nova tendência vendedora para G2.", idxGR, idxG1
));
return new ResultadoPadrao
(null, lastIndex
);
}
Candle ultimoVendedor =
null;
int idxUltimoVendedor = -
1;
for (int i = idxPrimeiroVendedor
; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (isInside
(candles, i
) ||
!isDirecional
(c
)) continue;
// OUTSIDE durante G2 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] durante formação de G2 (vendedor).", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global vendedor: romper fundo do GR => descarta
if (BigDecimalUtils.
ehMenorQue(c.
getMinima(), gr.
getMinima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu FUNDO do GR durante G2.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleVendedor()) {
ultimoVendedor = c
;
idxUltimoVendedor = i
;
lastIndex = i
;
} else if (c.
isCandleComprador()) {
lastIndex = i -
1;
break;
}
}
if (ultimoVendedor ==
null) {
log
(String.
format("GR[%d], G1[%d]: não houve vendedor válido para G2.", idxGR, idxG1
));
return new ResultadoPadrao
(null, lastIndex
);
}
boolean fechamentoDentroRegiaoGR =
BigDecimalUtils.
ehMaiorOuIgualQue(ultimoVendedor.
getFechamento(), gr.
getMinima()) &&
BigDecimalUtils.
ehMenorOuIgualQue(ultimoVendedor.
getFechamento(), gr.
getMaxima());
if (!fechamentoDentroRegiaoGR
) {
log
(String.
format(
"GR[%d], G1[%d]: último vendedor[%d] NÃO fechou dentro da região do GR.",
idxGR, idxG1, idxUltimoVendedor
));
return new ResultadoPadrao
(null, lastIndex
);
}
g2 = ultimoVendedor
;
idxG2 = idxUltimoVendedor
;
log
(String.
format("GR[%d], G1[%d] => G2 (vendedor) em [%d].", idxGR, idxG1, idxG2
));
// --------------------
// 3) Buscar G3 – NOVA REGRA:
// QUALQUER comprador subsequente que rompa topo de G2
// e tenha fundo >= fundo do GR.
// --------------------
Candle g3 =
null;
int idxG3 = -
1;
for (int i = idxG2 +
1; i
< n
; i++
) {
Candle c = candles.
get(i
);
if (!isDirecional
(c
) || isInside
(candles, i
)) {
continue;
}
// OUTSIDE antes/durante busca de G3 => descarta padrão
if (isOutside
(candles, i
)) {
lastIndex = idxGR
;
log
(String.
format("GR[%d]: OUTSIDE em [%d] durante busca de G3 (comprador).", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
// Regra global vendedor: romper fundo do GR => descarta
if (BigDecimalUtils.
ehMenorQue(c.
getMinima(), gr.
getMinima())) {
lastIndex = i
;
log
(String.
format("GR[%d]: candle[%d] rompeu FUNDO do GR durante busca de G3.", idxGR, i
));
return new ResultadoPadrao
(null, lastIndex
);
}
if (c.
isCandleComprador()) {
boolean rompeTopoG2 = BigDecimalUtils.
ehMaiorQue(c.
getMaxima(), g2.
getMaxima());
boolean fundoMaiorOuIgualGR = BigDecimalUtils.
ehMaiorOuIgualQue(c.
getMinima(), gr.
getMinima());
if (rompeTopoG2
&& fundoMaiorOuIgualGR
) {
g3 = c
;
idxG3 = i
;
lastIndex = i
;
break;
}
}
}
if (g3 ==
null) {
log
(String.
format("GR[%d], G1[%d], G2[%d]: não houve G3 (vendedor -> padrão parcial).",
idxGR, idxG1, idxG2
));
return criarResultadoParcialComG2
(gr, g1, g2, lastIndex
);
}
log
(String.
format("GR[%d], G1[%d], G2[%d] => G3 (comprador) em [%d].",
idxGR, idxG1, idxG2, idxG3
));
PadraoGatilho padrao =
new PadraoGatilho
();
padrao.
setReferencia(gr
);
padrao.
setGatilho1(g1
);
padrao.
setGatilho2(g2
);
padrao.
setGatilho3(g3
);
padrao.
setGatilho4(null);
return new ResultadoPadrao
(padrao, lastIndex
);
}
// =====================================================================
// MODO TEMPO REAL
// =====================================================================
public void resetTempoReal
() {
this.
idxProximaAnaliseTempoReal =
0;
}
/**
* Deve ser chamado SEMPRE que um novo candle for adicionado à lista.
*
* Exemplo:
* candles.add(novoCandle);
* PadraoGatilho padrao = detector.processarCandleTempoReal(candles);
*
* if (padrao != null) {
* // padrão completo (até G3) encontrado
* }
*/
public PadraoGatilho processarCandleTempoReal
(List<Candle
> candles
) {
int n = candles.
size();
if (n
< 4) return null;
while (idxProximaAnaliseTempoReal
< n -
3) {
ResultadoPadrao resultado = detectarPadraoAPartir
(candles, idxProximaAnaliseTempoReal
);
if (resultado ==
null) {
idxProximaAnaliseTempoReal++
;
continue;
}
int proximoInicio = resultado.
lastIndex +
1;
if (proximoInicio
<= idxProximaAnaliseTempoReal
) {
proximoInicio = idxProximaAnaliseTempoReal +
1;
}
idxProximaAnaliseTempoReal = proximoInicio
;
if (resultado.
padrao !=
null) {
return resultado.
padrao;
}
}
return null;
}
}