package br.com.kronus.core;
import br.com.ec.core.util.VerificadorUtil;
import br.com.kronus.core.Candle;
import java.util.ArrayList;
import java.util.List;
/**
* Detector + Monitor de Gatilhos (Referência, G1, G2, G3, G4)
*
* REGRAS IMPLEMENTADAS (resumo):
*
* Candle Referência:
* - Candle que demarca uma região (topo ou fundo) e que posteriormente
* será rompido contrariamente à sua tendência pelo Gatilho 1.
* - Ex.: candle comprador -> se um candle posterior romper seu FUNDO,
* esse posterior é G1 e o rompido é a referência.
* Ex.: candle vendedor -> se um candle posterior romper seu TOPO,
* esse posterior é G1 e o rompido é a referência.
*
* Gatilho 1:
* - Primeiro candle que rompe a tendência contrária a algum candle anterior
* (sem insiders).
* - Se G1 for COMPRADOR: deve romper o TOPO do candle referência.
* - Se G1 for VENDEDOR: deve romper o FUNDO do candle referência.
*
* Gatilho 2:
* - Candle que retorna à região do candle referência buscando liquidez.
* - Último candle antes da nova quebra de tendência.
* - Fechamento deve estar dentro da região total (incluindo pavio)
* do candle referência.
* - Se ultrapassar a máxima (ou mínima) do candle referência, deve-se procurar
* por um novo candle referência a partir desse candle.
* - EXCEÇÃO: se o candle for da mesma tendência de G1 e tiver rompido
* sua tendência (novo topo na compra / novo fundo na venda), também é G2.
*
* Gatilho 3:
* - Candle posterior ao G2.
* - Mesma tendência de G1.
* - Se referência for COMPRADORA:
* * topo de G3 < topo da referência
* * G3 rompe o FUNDO do G2
* * se romper o topo da referência após o G2 → invalida padrão.
* - Se referência for VENDEDORA:
* * topo de G3 > topo da referência
* * G3 rompe o TOPO do G2
* * se romper o fundo da referência após o G2 → invalida padrão.
*
* Gatilho 4:
* - Desarma a operação.
* - Se referência COMPRADORA: seu topo será rompido pelo candle do G4.
* - Se referência VENDEDORA: seu fundo será rompido.
* - Após G4 a análise recomeça.
*/
public class DetectorGatilhos_
{
private Candle referencia
;
private Candle gatilho1
;
private Candle gatilho2
;
private Candle gatilho3
;
private Candle gatilho4
;
private int idxRef = -
1;
private int idxG1 = -
1;
private int idxG2 = -
1;
private int idxG3 = -
1;
private int idxG4 = -
1;
private boolean candidatoG4Avaliado =
false;
private PadraoGatilho padraoAtual
;
private boolean logAtivo
;
public DetectorGatilhos_
() {
this(false);
}
public DetectorGatilhos_
(boolean logAtivo
) {
this.
logAtivo = logAtivo
;
}
public boolean isLogAtivo
() {
return logAtivo
;
}
public void setLogAtivo
(boolean logAtivo
) {
this.
logAtivo = logAtivo
;
}
private void log
(String msg
) {
if (logAtivo
) {
System.
out.
println(msg
);
}
}
private void reset
() {
referencia =
null;
gatilho1 =
null;
gatilho2 =
null;
gatilho3 =
null;
gatilho4 =
null;
padraoAtual =
null;
idxRef = -
1;
idxG1 = -
1;
idxG2 = -
1;
idxG3 = -
1;
idxG4 = -
1;
candidatoG4Avaliado =
false;
}
// ============================================================
// A) MODO SIMULADOR / BACKTEST
// -> continua: List<PadraoGatilho> padroes = detector.identificarPadroes(candles);
// ============================================================
public List<PadraoGatilho
> identificarPadroes
(List<Candle
> candles
) {
reset
();
List<PadraoGatilho
> padroes =
new ArrayList<>();
for (int i =
0; i
< candles.
size(); i++
) {
processarCandleInterno
(candles, i,
null, padroes
);
}
return padroes
;
}
// ============================================================
// B) MODO MONITOR (tempo real / replay)
// ============================================================
/**
* Processa o candle idx e retorna os logs deste passo.
* Os logs também são enviados para System.out.println se logAtivo = true.
*/
public List<String> processarCandle
(List<Candle
> candles,
int idx
) {
List<String> logs =
new ArrayList<>();
processarCandleInterno
(candles, idx, logs,
null);
return logs
;
}
// ============================================================
// C) LÓGICA INTERNA (compartilhada entre simulador e monitor)
// ============================================================
private void processarCandleInterno
(List<Candle
> candles,
int idx,
List<String> logs,
List<PadraoGatilho
> padroes
) {
if (idx
< 0 || idx
>= candles.
size()) {
return;
}
Candle atual = candles.
get(idx
);
if (VerificadorUtil.
naoEstaNulo(atual
)) {
if (VerificadorUtil.
estaNulo(atual.
getContador())) {
System.
out.
println("TESTE");
} else {
if (atual.
getContador() ==
11) {
System.
out.
println("TESTE");
}
if (atual.
getContador() ==
57) {
System.
out.
println("TESTE");
}
}
}
// --------------------------------------------------------------------
// 1) Ainda não temos Ref + G1 -> tentar formar com o candle atual
// --------------------------------------------------------------------
if (referencia ==
null || gatilho1 ==
null) {
ParRefG1 par = encontrarReferenciaEGatilho1
(candles, idx
);
if (par
!=
null) {
referencia = par.
referencia;
gatilho1 = par.
g1;
idxRef = candles.
indexOf(referencia
);
idxG1 = candles.
indexOf(gatilho1
);
// Segurança extra: não aceitar se por algum bug der o mesmo índice ou inverso
if (idxRef
< 0 || idxG1
< 0 || idxRef
>= idxG1
) {
// Inconsistente, descarta e segue
referencia =
null;
gatilho1 =
null;
idxRef = -
1;
idxG1 = -
1;
String msg =
String.
format(
"[%d] Par (Ref, G1) inconsistente encontrado. Descartando.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
return;
}
gatilho2 =
null;
gatilho3 =
null;
gatilho4 =
null;
idxG2 = idxG3 = idxG4 = -
1;
padraoAtual =
new PadraoGatilho
();
padraoAtual.
setReferencia(referencia
);
padraoAtual.
setGatilho1(gatilho1
);
String direcao = gatilho1.
isCandleComprador() ? "COMPRA" :
"VENDA";
int idxRef = candles.
indexOf(referencia
);
int idxG1 = candles.
indexOf(gatilho1
);
String msg =
String.
format(
"[%d] Gatilho 1 (%s) identificado. Referência = candle [%d], G1 = candle [%d].",
idx, direcao, idxRef, idxG1
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
} else {
String msg =
"[" + idx +
"] Sem referência/G1 formados ainda.";
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
}
return;
}
// --------------------------------------------------------------------
// 2) Temos Ref + G1, mas ainda não temos G2
// --------------------------------------------------------------------
if (gatilho2 ==
null) {
boolean cEhG2 = isGatilho2
(referencia, gatilho1, atual
);
if (cEhG2
) {
// G2 deve ser posterior a G1 e candle diferente
if (idx
> idxG1
) {
gatilho2 = atual
;
idxG2 = idx
;
if (padraoAtual
!=
null) {
padraoAtual.
setGatilho2(gatilho2
);
}
String msg =
String.
format(
"[%d] Gatilho 2 identificado.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
} else {
// Se por alguma razão o método estiver sendo chamado de forma estranha
String msg =
String.
format(
"[%d] Candle candidato a G2, mas índice %d <= idxG1 %d. Ignorando para manter ordem cronológica.",
idx, idx, idxG1
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
}
return;
}
// Se NÃO é G2, verificamos se:
// 1) ultrapassou a região da referência (topo/fundo)
// 2) houve nova quebra de tendência sem atingir a região da referência
boolean ultrapassaRef = ultrapassaRegiaoReferencia
(referencia, atual
);
boolean novaQuebraSemRegiao = novaQuebraDeTendenciaSemAtingirRegiao
(referencia, gatilho1, atual
);
if (ultrapassaRef || novaQuebraSemRegiao
) {
String motivo = ultrapassaRef
? "ultrapassou a região da referência"
:
"nova quebra de tendência sem atingir a região da referência";
String msg =
String.
format(
"[%d] Candle %s. Procurando novo candle referência a partir deste candle.",
idx, motivo
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
// Reset e tentamos nova Ref+G1 com o candle atual
reset
();
ParRefG1 novo = encontrarReferenciaEGatilho1
(candles, idx
);
if (novo
!=
null) {
referencia = novo.
referencia;
gatilho1 = novo.
g1;
padraoAtual =
new PadraoGatilho
();
padraoAtual.
setReferencia(referencia
);
padraoAtual.
setGatilho1(gatilho1
);
String direcao = gatilho1.
isCandleComprador() ? "COMPRA" :
"VENDA";
int idxRef = candles.
indexOf(referencia
);
int idxG1 = candles.
indexOf(gatilho1
);
String msg2 =
String.
format(
"[%d] Novo Gatilho 1 (%s) identificado após reset. Ref = [%d], G1 = [%d].",
idx, direcao, idxRef, idxG1
);
if (logs
!=
null) logs.
add(msg2
);
log
(msg2
);
}
return;
}
// Nem é G2, nem invalida a referência → seguimos aguardando G2
String msg =
"[" + idx +
"] Aguardando Gatilho 2.";
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
return;
}
// --------------------------------------------------------------------
// 3) Temos Ref + G1 + G2, mas ainda não G3
// --------------------------------------------------------------------
if (gatilho3 ==
null) {
// Invalidação após G2:
if (rompeReferenciaAposG2
(referencia, atual
)) {
String msg =
String.
format(
"[%d] Rompimento da referência após o G2. Padrão invalidado. Resetando.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
reset
();
return;
}
if (isGatilho3
(referencia, gatilho1, gatilho2, atual
)) {
if (idx
> idxG2
) {
gatilho3 = atual
;
idxG3 = idx
;
candidatoG4Avaliado =
false;
if (padraoAtual
!=
null) {
padraoAtual.
setGatilho3(gatilho3
);
}
String msg =
String.
format(
"[%d] Gatilho 3 identificado.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
if (padroes
!=
null && padraoAtual
!=
null && !padroes.
contains(padraoAtual
)) {
padroes.
add(padraoAtual
);
}
} else {
String msg =
String.
format(
"[%d] Candle candidato a G3, mas índice %d <= idxG2 %d. Ignorando.",
idx, idx, idxG2
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
}
return;
}
return;
}
// --------------------------------------------------------------------
// 4) Já temos Ref + G1 + G2 + G3 -> monitorar G4 (desarme) monitorar APENAS o primeiro candle pós G3 para possível G4
// --------------------------------------------------------------------
if (gatilho3
!=
null) {
Candle g3 = gatilho3
;
// Se ainda não avaliamos o candidato a G4
if (!candidatoG4Avaliado
) {
// 4.1) Descartar insiders: candles completamente dentro do range do G3
if (isInsiderEmRelacaoAoG3
(g3, atual
)) {
String msg =
String.
format(
"[%d] Candle insider em relação ao G3. Aguardando próximo candle para possível G4.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
return;
}
// 4.2) Este é o PRIMEIRO candle "útil" pós G3 (não insider)
candidatoG4Avaliado =
true;
// G4 só pode ser depois de G3 e em candle diferente
if (idx
> idxG3
&& isGatilho4
(referencia, atual
)) {
gatilho4 = atual
;
idxG4 = idx
;
if (padraoAtual
!=
null) {
padraoAtual.
setGatilho4(gatilho4
);
}
String msg =
String.
format(
"[%d] Gatilho 4 identificado. Operação desarmada. Padrão encerrado.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
reset
();
} else {
String msg =
String.
format(
"[%d] Primeiro candle pós G3 não configurou Gatilho 4. Padrão encerrado sem G4.",
idx
);
if (logs
!=
null) logs.
add(msg
);
log
(msg
);
reset
();
}
return;
}
// Se já avaliamos o candidato a G4, o padrão já foi encerrado (com ou sem G4),
// então não deveríamos mais cair aqui em condições normais.
return;
}
}
/**
* Um candle é considerado insider em relação ao G3 se estiver completamente
* contido dentro da máxima e mínima do G3.
*/
private boolean isInsiderEmRelacaoAoG3
(Candle g3, Candle c
) {
return c.
getMaxima() <= g3.
getMaxima()
&& c.
getMinima() >= g3.
getMinima();
}
// ============================================================
// BLOCO: REFERÊNCIA + G1
// ============================================================
private static class ParRefG1
{
Candle referencia
;
Candle g1
;
ParRefG1
(Candle referencia, Candle g1
) {
this.
referencia = referencia
;
this.
g1 = g1
;
}
}
/**
* Encontra par (Referência, G1) partindo de um possível candle referência,
* analisando os candles posteriores.
*
* Regras:
*
* Candle Referência:
* - Candle que demarca topo/fundo e que será rompido, posteriormente,
* contrariamente à sua tendência pelo Gatilho 1.
*
* Exemplo:
* - Candle COMPRADOR (referência):
* Se ALGUM candle VENDEDOR subsequente romper o FUNDO dele:
* -> esse VENDEDOR é o Gatilho 1
* -> o COMPRADOR rompido é a Referência.
*
* - Candle VENDEDOR (referência):
* Se ALGUM candle COMPRADOR subsequente romper o TOPO dele:
* -> esse COMPRADOR é o Gatilho 1
* -> o VENDEDOR rompido é a Referência.
*
* A função procura o PRIMEIRO G1 que respeita essas regras.
*/
private ParRefG1 encontrarReferenciaEGatilho1
(List<Candle
> candles,
int idxRef
) {
Candle ref = candles.
get(idxRef
);
boolean refComprador = ref.
isCandleComprador();
boolean refVendedor = ref.
isCandleVendedor();
// Se a referência não tiver tendência clara, ignoramos (doji/indeciso)
if (!refComprador
&& !refVendedor
) {
return null;
}
// Percorre os candles POSTERIORES ao possível candle referência
for (int i = idxRef +
1; i
< candles.
size(); i++
) {
Candle c = candles.
get(i
);
// também ignoramos candles sem tendência clara como candidatos a G1
boolean cComprador = c.
isCandleComprador();
boolean cVendedor = c.
isCandleVendedor();
if (!cComprador
&& !cVendedor
) {
continue;
}
if (refComprador
&& cVendedor
) {
// Referência COMPRADORA -> G1 VENDEDOR deve romper o FUNDO da referência
if (c.
getMinima() < ref.
getMinima()) {
// Achamos o primeiro G1 que rompeu o fundo da referência
return new ParRefG1
(ref, c
);
}
} else if (refVendedor
&& cComprador
) {
// Referência VENDEDORA -> G1 COMPRADOR deve romper o TOPO da referência
if (c.
getMaxima() > ref.
getMaxima()) {
// Achamos o primeiro G1 que rompeu o topo da referência
return new ParRefG1
(ref, c
);
}
}
}
// Nenhum G1 encontrado para esse possível candle referência
return null;
}
// ============================================================
// BLOCO: GATILHO 2
// ============================================================
/**
* Gatilho 2:
* - Candle que RETORNA à região do candle referência (fechamento dentro do range total).
* - É o último candle antes da nova quebra de tendência.
* - EXCEÇÃO: se for da mesma tendência de G1 e "romper a tendência" de G1
* (novo topo na compra / novo fundo na venda), também é G2 mesmo sem tocar a região.
*/
private boolean isGatilho2
(Candle referencia, Candle g1, Candle c
) {
boolean cComprador = c.
isCandleComprador();
boolean cVendedor = c.
isCandleVendedor();
if (!cComprador
&& !cVendedor
) {
return false;
}
// Fechamento dentro da região total do candle referência
boolean fechamentoDentroRegiao =
c.
getFechamento() >= referencia.
getMinima() &&
c.
getFechamento() <= referencia.
getMaxima();
boolean condicaoPrincipal = fechamentoDentroRegiao
;
boolean g1Comprador = g1.
isCandleComprador();
boolean g1Vendedor = g1.
isCandleVendedor();
boolean mesmaTendencia =
(g1Comprador
&& cComprador
) ||
(g1Vendedor
&& cVendedor
);
boolean rompeTendenciaG1 =
(g1Comprador
&& c.
getMaxima() > g1.
getMaxima()) ||
(g1Vendedor
&& c.
getMinima() < g1.
getMinima());
boolean condicaoExcecao = mesmaTendencia
&& rompeTendenciaG1
;
return condicaoPrincipal || condicaoExcecao
;
}
/**
* Se ultrapassar a região do candle referência:
* - Referência compradora: rompe TOPO
* - Referência vendedora: rompe FUNDO
* Neste caso devemos buscar um novo candle referência a partir deste candle.
*/
private boolean ultrapassaRegiaoReferencia
(Candle referencia, Candle c
) {
if (referencia ==
null) return false;
boolean refComprador = referencia.
isCandleComprador();
boolean refVendedor = referencia.
isCandleVendedor();
if (refComprador
) {
return c.
getMaxima() > referencia.
getMaxima();
} else if (refVendedor
) {
return c.
getMinima() < referencia.
getMinima();
}
return false;
}
/**
* Nova quebra de tendência sem atingir a região da referência:
*
* Ideia:
* - Se G1 é comprador:
* -> candle atual é vendedor
* -> mínima do candle atual < mínima do G1 (quebra da perna de compra)
* -> fechamento NÃO está dentro da região da referência
*
* - Se G1 é vendedor:
* -> candle atual é comprador
* -> máxima do candle atual > máxima do G1
* -> fechamento NÃO está dentro da região da referência
*/
private boolean novaQuebraDeTendenciaSemAtingirRegiao
(Candle referencia,
Candle g1,
Candle c
) {
if (referencia ==
null || g1 ==
null) return false;
boolean fechamentoDentroRegiao =
c.
getFechamento() >= referencia.
getMinima() &&
c.
getFechamento() <= referencia.
getMaxima();
if (fechamentoDentroRegiao
) {
return false; // se tocou a região, não é "sem atingir a região"
}
boolean g1Comprador = g1.
isCandleComprador();
boolean g1Vendedor = g1.
isCandleVendedor();
boolean cComprador = c.
isCandleComprador();
boolean cVendedor = c.
isCandleVendedor();
if (g1Comprador
&& cVendedor
) {
// quebra forte contra a compra
return c.
getMinima() < g1.
getMinima();
} else if (g1Vendedor
&& cComprador
) {
// quebra forte contra a venda
return c.
getMaxima() > g1.
getMaxima();
}
return false;
}
// ============================================================
// BLOCO: GATILHO 3
// ============================================================
/**
* G3:
* - Candle posterior ao G2.
* - Mesma tendência de G1.
* - Ref COMPRADORA:
* * topo de G3 < topo da referência
* * mínima de G3 < mínima de G2 (rompe fundo do G2)
* - Ref VENDEDORA:
* * topo de G3 > topo da referência
* * máxima de G3 > máxima de G2 (rompe topo do G2)
* (A invalidação por rompimento da referência é tratada em outro método.)
*/
private boolean isGatilho3
(Candle referencia, Candle g1, Candle g2, Candle g3
) {
boolean g1Comprador = g1.
isCandleComprador();
boolean g1Vendedor = g1.
isCandleVendedor();
boolean g3Comprador = g3.
isCandleComprador();
boolean g3Vendedor = g3.
isCandleVendedor();
boolean mesmaTendenciaG1 =
(g1Comprador
&& g3Comprador
) ||
(g1Vendedor
&& g3Vendedor
);
if (!mesmaTendenciaG1
) {
return false;
}
boolean refComprador = referencia.
isCandleComprador();
boolean refVendedor = referencia.
isCandleVendedor();
if (refComprador
) {
boolean topoMaisBaixo = g3.
getMaxima() < referencia.
getMaxima();
boolean rompeFundoG2 = g3.
getMinima() < g2.
getMinima();
return topoMaisBaixo
&& rompeFundoG2
;
} else if (refVendedor
) {
boolean topoMaiorQueRef = g3.
getMaxima() > referencia.
getMaxima();
boolean rompeTopoG2 = g3.
getMaxima() > g2.
getMaxima();
return topoMaiorQueRef
&& rompeTopoG2
;
}
return false;
}
/**
* Invalidação após o G2:
* - Ref COMPRADORA: se qualquer candle após G2 romper o topo da referência -> invalida.
* - Ref VENDEDORA: se romper o fundo da referência -> invalida.
*/
private boolean rompeReferenciaAposG2
(Candle referencia, Candle c
) {
if (referencia ==
null) return false;
boolean refComprador = referencia.
isCandleComprador();
boolean refVendedor = referencia.
isCandleVendedor();
if (refComprador
) {
return c.
getMaxima() > referencia.
getMaxima();
} else if (refVendedor
) {
return c.
getMinima() < referencia.
getMinima();
}
return false;
}
// ============================================================
// BLOCO: GATILHO 4
// ============================================================
/**
* G4:
* - Desarma a operação.
* - Ref COMPRADORA: topo da referência rompido pelo candle G4.
* - Ref VENDEDORA: fundo da referência rompido pelo candle G4.
*/
private boolean isGatilho4
(Candle referencia, Candle c
) {
if (referencia ==
null) return false;
boolean refComprador = referencia.
isCandleComprador();
boolean refVendedor = referencia.
isCandleVendedor();
if (refComprador
) {
return c.
getMaxima() > referencia.
getMaxima();
} else if (refVendedor
) {
return c.
getMinima() < referencia.
getMinima();
}
return false;
}
// ============================================================
// Getters (se quiser inspecionar estado atual externamente)
// ============================================================
public Candle getReferencia
() {
return referencia
;
}
public Candle getGatilho1
() {
return gatilho1
;
}
public Candle getGatilho2
() {
return gatilho2
;
}
public Candle getGatilho3
() {
return gatilho3
;
}
public Candle getGatilho4
() {
return gatilho4
;
}
public PadraoGatilho getPadraoAtual
() {
return padraoAtual
;
}
}