package br.com.kronus.binance.robos;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import br.com.kronus.core.Candle;
import br.com.kronus.core.DetectorGatilhos;
import br.com.kronus.core.EstrategiaGatilhoTipo3Sinais;
import br.com.kronus.core.PadraoGatilho;
import br.com.kronus.core.SinalTradeGatilho3;
import br.com.kronus.core.TipoPeriodoCandle;
/**
* RoboSinaisMain:
*
* - Monitora o arquivo BTCUSDT_1m.csv
* - A cada 2 segundos verifica se foi modificado
* - Se houve modificação:
* * Carrega TODOS os candles do arquivo
* * Identifica padrões e gera sinais (DetectorGatilhos + Estratégia)
* * Grava os sinais em SINAIS.csv com status 'P' (Pendente)
* * Evita gravar sinais duplicados via ID de sinal
*
* Layout BTCUSDT_1m.csv:
* BTCUSDT;30/11/2025;14:12:00;91390,2;91410,7;91390;91390;226,94;155
* 0 1 2 3 4 5 6 7 8
*
* Layout SINAIS.csv (mesmo do RoboGatilhosSinaisMain):
* ID;Ativo;Data;Hora;TipoOperacao;PrecoEntrada;StopLoss;TakeProfit;Quantidade;Status;ClientOrderId
*/
public class RoboSinaisMain
{
private static final String ARQUIVO_SINAIS =
"D:/Dropbox/BLP/INVESTIMENTOS/DAYTRADE/sinais/SINAIS.csv";
private static final String ARQUIVO_CANDLES =
"D:/Dropbox/BLP/INVESTIMENTOS/DAYTRADE/temporeal/BTCUSDT_5m.csv";
private static final BigDecimal VALOR_ENTRADA =
new BigDecimal(0.005);
private final ZoneId zone = ZoneId.
of("America/Maceio");
private final ScheduledExecutorService scheduler =
Executors.
newSingleThreadScheduledExecutor();
// Data/hora para candles (arquivo BTCUSDT_1m.csv)
private final DateTimeFormatter fmtDataCandle = DateTimeFormatter.
ofPattern("dd/MM/yyyy");
private final DateTimeFormatter fmtHoraCandle = DateTimeFormatter.
ofPattern("HH:mm:ss");
// Data/hora para sinais (arquivo SINAIS.csv)
private final DateTimeFormatter fmtDataSinal = DateTimeFormatter.
ofPattern("dd/MM/yyyy");
private final DateTimeFormatter fmtHoraSinal = DateTimeFormatter.
ofPattern("HH:mm:ss");
// Controle de modificação do arquivo de candles
private long ultimaModificacaoCandles = 0L
;
// IDs de sinais já gravados no arquivo SINAIS.csv (para evitar duplicidade)
private final Set<String> idsSinaisGravados =
new HashSet<>();
// =====================================================================
// MAIN
// =====================================================================
public static void main
(String[] args
) {
try {
RoboSinaisMain r =
new RoboSinaisMain
();
r.
iniciar();
Runtime.
getRuntime().
addShutdownHook(new Thread(r::parar
));
} catch (Exception e
) {
e.
printStackTrace();
}
}
// =====================================================================
// CONSTRUTOR / INÍCIO / FIM
// =====================================================================
public RoboSinaisMain
() {
System.
out.
println("[ROBO-SINAIS] Inicializando RoboSinaisMain...");
carregarIdsSinaisExistentes
();
}
public void iniciar
() {
System.
out.
println("[ROBO-SINAIS] Iniciando monitor de candles (BTCUSDT_1m) a cada 2 segundos...");
scheduler.
scheduleAtFixedRate(() -
> {
try {
ciclo
();
} catch (Exception e
) {
e.
printStackTrace();
}
},
0,
2,
TimeUnit.
SECONDS);
}
public void parar
() {
System.
out.
println("[ROBO-SINAIS] Encerrando RoboSinaisMain.");
scheduler.
shutdownNow();
}
// =====================================================================
// CICLO A CADA 2 SEGUNDOS
// =====================================================================
private void ciclo
() {
if (arquivoCandlesFoiModificado
()) {
System.
out.
println("[ROBO-SINAIS] BTCUSDT_1m.csv modificado. Recarregando candles e gerando sinais...");
List<Candle
> candles = carregarCandlesDoArquivo
();
if (candles.
isEmpty()) {
System.
out.
println("[ROBO-SINAIS] Nenhum candle carregado. Ignorando ciclo.");
return;
}
// 1) Detector de padrões
DetectorGatilhos detector =
new DetectorGatilhos
();
List<PadraoGatilho
> padroes = detector.
identificarPadroes(candles
);
if (padroes ==
null || padroes.
isEmpty()) {
System.
out.
println("[ROBO-SINAIS] Nenhum padrão encontrado.");
return;
}
System.
out.
println("[ROBO-SINAIS] Padrões encontrados: " + padroes.
size());
// 2) Estratégia para gerar sinais
EstrategiaGatilhoTipo3Sinais estrategia =
new EstrategiaGatilhoTipo3Sinais
();
List<SinalTradeGatilho3
> sinais = estrategia.
gerarSinais(padroes
);
if (sinais ==
null || sinais.
isEmpty()) {
System.
out.
println("[ROBO-SINAIS] Nenhum sinal gerado.");
return;
}
// Ordena por data/hora de entrada DESC (mais recente primeiro)
sinais.
sort(Comparator.
comparing(SinalTradeGatilho3::getDataHoraEntrada
).
reversed());
System.
out.
println("[ROBO-SINAIS] Sinais gerados: " + sinais.
size());
// 3) Grava apenas os sinais ainda não registrados em SINAIS.csv
gravarNovosSinais
(sinais
);
}
}
private boolean arquivoCandlesFoiModificado
() {
try {
Path path = Paths.
get(ARQUIVO_CANDLES
);
if (!Files.
exists(path
)) {
System.
out.
println("[ROBO-SINAIS] Arquivo de candles não encontrado: " + path.
toAbsolutePath());
return false;
}
long mod = Files.
getLastModifiedTime(path
).
toMillis();
if (mod
> ultimaModificacaoCandles
) {
ultimaModificacaoCandles = mod
;
return true;
}
} catch (IOException e
) {
System.
err.
println("[ROBO-SINAIS] Erro ao verificar modificação do arquivo de candles: " + e.
getMessage());
}
return false;
}
// =====================================================================
// CARREGAR CANDLES DO ARQUIVO BTCUSDT_1m.csv
// =====================================================================
private List<Candle
> carregarCandlesDoArquivo
() {
List<Candle
> lista =
new ArrayList<>();
Path path = Paths.
get(ARQUIVO_CANDLES
);
if (!Files.
exists(path
)) {
System.
out.
println("[ROBO-SINAIS] Arquivo de candles não encontrado: " + path.
toAbsolutePath());
return lista
;
}
try (BufferedReader reader = Files.
newBufferedReader(path, StandardCharsets.
UTF_8)) {
String linha
;
boolean primeira =
true;
int contador =
0;
while ((linha = reader.
readLine()) !=
null) {
if (primeira
&& linha.
toLowerCase().
contains("data")) {
primeira =
false;
continue; // pula cabeçalho, se houver
}
primeira =
false;
if (linha.
trim().
isEmpty()) continue;
Candle c = parseLinhaParaCandle
(linha, contador
);
if (c
!=
null) {
lista.
add(c
);
contador++
;
}
}
System.
out.
println("[ROBO-SINAIS] Candles carregados: " + lista.
size());
} catch (IOException e
) {
System.
err.
println("[ROBO-SINAIS] Erro ao ler arquivo de candles: " + e.
getMessage());
e.
printStackTrace();
}
return lista
;
}
/**
* Formato BTCUSDT_1m.csv:
* BTCUSDT;30/11/2025;14:12:00;91390,2;91410,7;91390;91390;226,94;155
* 0 1 2 3 4 5 6 7 8
*/
private Candle parseLinhaParaCandle
(String linha,
int contadorCandle
) {
try {
String[] p = linha.
split(";", -
1);
if (p.
length < 7) {
System.
out.
println("[ROBO-SINAIS] Linha inválida em BTCUSDT_1m: " + linha
);
return null;
}
String ativo = p
[0].
trim();
LocalDate data = LocalDate.
parse(p
[1].
trim(), fmtDataCandle
);
LocalTime hora = LocalTime.
parse(p
[2].
trim(), fmtHoraCandle
);
LocalDateTime dt = LocalDateTime.
of(data, hora
);
BigDecimal abertura = parseBigDecimal
(p
[3]);
BigDecimal maxima = parseBigDecimal
(p
[4]);
BigDecimal minima = parseBigDecimal
(p
[5]);
BigDecimal fechamento = parseBigDecimal
(p
[6]);
BigDecimal volume =
(p.
length > 7) ? parseBigDecimal
(p
[7]) :
BigDecimal.
ZERO;
Candle c =
new Candle
(
contadorCandle,
ativo,
dt,
abertura,
maxima,
minima,
fechamento,
TipoPeriodoCandle.
M1.
getValor()
);
c.
setVolume(volume
);
return c
;
} catch (Exception e
) {
System.
out.
println("[ROBO-SINAIS] Erro ao parsear linha de candle: " + linha
);
e.
printStackTrace();
return null;
}
}
// =====================================================================
// GRAVAÇÃO DE SINAIS EM SINAIS.csv (STATUS = P)
// =====================================================================
private void gravarNovosSinais
(List<SinalTradeGatilho3
> sinais
) {
if (sinais ==
null || sinais.
isEmpty()) {
return;
}
Path path = Paths.
get(ARQUIVO_SINAIS
);
boolean novoArquivo =
!Files.
exists(path
);
try {
if (path.
getParent() !=
null && !Files.
exists(path.
getParent())) {
Files.
createDirectories(path.
getParent());
}
try (BufferedWriter writer = Files.
newBufferedWriter(
path,
StandardCharsets.
UTF_8,
StandardOpenOption.
CREATE,
StandardOpenOption.
APPEND)
) {
// Cabeçalho se arquivo recém-criado
if (novoArquivo
) {
writer.
write("ID;Ativo;Data;Hora;TipoOperacao;PrecoEntrada;StopLoss;TakeProfit;Quantidade;Status;ClientOrderId");
writer.
newLine();
}
int novos =
0;
for (SinalTradeGatilho3 s : sinais
) {
if (s ==
null) continue;
if (s.
getDataHoraEntrada() ==
null) continue;
String id = gerarIdSinal
(s
);
// Evita duplicidade
if (idsSinaisGravados.
contains(id
)) {
continue;
}
idsSinaisGravados.
add(id
);
// ==== Mapear campos ====
String ativo = s.
getIdAtivo(); // AJUSTE AQUI se o getter tiver outro nome
if (ativo ==
null || ativo.
trim().
isEmpty()) {
ativo =
"BTCUSDT";
}
Date dataHora = s.
getDataHoraEntrada();
if (dataHora ==
null) {
return; // ou continue; dependendo do contexto
}
LocalDateTime dt = LocalDateTime.
ofInstant(dataHora.
toInstant(), zone
); // use o ZoneId que você já tem na classe
String data = dt.
toLocalDate().
format(fmtDataSinal
);
String hora = dt.
toLocalTime().
format(fmtHoraSinal
);
String tipoOperacao =
(s.
getTipoOperacao() !=
null)? s.
getTipoOperacao().
getValor() :
"";
BigDecimal precoEntrada = s.
getPrecoEntrada1();
BigDecimal stopLoss = s.
getStopMenos100();
BigDecimal takeProfit = s.
getAlvo2();
BigDecimal quantidade = VALOR_ENTRADA
; // AJUSTE se usar outro nome (ex.: getContratos)
String precoEntradaStr = toStr
(precoEntrada
);
String stopLossStr = toStr
(stopLoss
);
String takeProfitStr = toStr
(takeProfit
);
String quantidadeStr = toStr
(quantidade
);
String status =
"P"; // sempre pendente ao gravar
String clientOrderId =
""; // ainda não enviado pra Binance
String linha = id +
";" +
ativo +
";" +
data +
";" +
hora +
";" +
tipoOperacao +
";" +
precoEntradaStr +
";" +
stopLossStr +
";" +
takeProfitStr +
";" +
quantidadeStr +
";" +
status +
";" +
clientOrderId
;
writer.
write(linha
);
writer.
newLine();
novos++
;
}
System.
out.
println("[ROBO-SINAIS] Novos sinais gravados: " + novos
);
}
} catch (IOException e
) {
System.
err.
println("[ROBO-SINAIS] Erro ao gravar SINAIS.csv: " + e.
getMessage());
e.
printStackTrace();
}
}
/**
* Gera um ID único para o sinal, baseado em:
* Ativo + DataHoraEntrada + TipoOperacao + PrecoEntrada
*
* Isso garante que, se o mesmo sinal for recalculado depois,
* não será gravado novamente.
*/
private String gerarIdSinal
(SinalTradeGatilho3 s
) {
String ativo = s.
getIdAtivo();
if (ativo ==
null || ativo.
trim().
isEmpty()) {
ativo =
"BTCUSDT";
}
Date dataHora = s.
getDataHoraEntrada();
LocalDateTime dt = LocalDateTime.
ofInstant(
dataHora.
toInstant(),
zone
);
String tipo =
(s.
getTipoOperacao() !=
null) ? s.
getTipoOperacao().
getValor() :
"";
String preco =
(s.
getAlvo1() !=
null)? s.
getAlvo1().
toPlainString():
"0";
return ativo +
"_" + dt.
toString() +
"_" + tipo +
"_" + preco
;
}
// =====================================================================
// CARREGAR IDs JÁ EXISTENTES EM SINAIS.csv (para não duplicar)
// =====================================================================
private void carregarIdsSinaisExistentes
() {
Path path = Paths.
get(ARQUIVO_SINAIS
);
if (!Files.
exists(path
)) {
return;
}
try (BufferedReader reader = Files.
newBufferedReader(path, StandardCharsets.
UTF_8)) {
String linha
;
boolean primeira =
true;
while ((linha = reader.
readLine()) !=
null) {
if (primeira
) {
primeira =
false;
continue; // cabeçalho
}
if (linha.
trim().
isEmpty()) continue;
String[] p = linha.
split(";", -
1);
if (p.
length < 1) continue;
String id = p
[0].
trim();
if (!id.
isEmpty()) {
idsSinaisGravados.
add(id
);
}
}
System.
out.
println("[ROBO-SINAIS] IDs de sinais existentes carregados: " + idsSinaisGravados.
size());
} catch (IOException e
) {
System.
err.
println("[ROBO-SINAIS] Erro ao carregar IDs de SINAIS.csv: " + e.
getMessage());
e.
printStackTrace();
}
}
// =====================================================================
// UTILS
// =====================================================================
private BigDecimal parseBigDecimal
(String s
) {
if (s ==
null) return BigDecimal.
ZERO;
s = s.
trim();
if (s.
isEmpty()) return BigDecimal.
ZERO;
return new BigDecimal(s.
replace(",",
"."));
}
private String toStr
(BigDecimal v
) {
if (v ==
null) return "";
return v.
toPlainString().
replace(".",
",");
}
}