Subversion Repositories Integrator Subversion

Rev

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

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