package br.com.sl.core;
import java.io.BufferedReader;
import java.io.FileInputStream;
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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import br.com.ec.core.util.VerificadorUtil;
import br.com.kronus.core.Timeframe;
import br.com.sl.domain.dto.robo.ProfitTick;
import br.com.sl.domain.model.Candle;
import br.com.sl.domain.model.tipos.TipoPeriodoCandle;
import br.com.sl.domain.util.BigDecimalUtils;
import br.com.sl.shared.ExcelDataUtils;
public class ExcelProfitDataProvider
implements ProfitDataProvider
{
private final Path excelFile
;
private final String sheetName
;
// índices de coluna (0 = A, 1 = B, etc.)
private final int colAsset =
0;
private final int colDate =
1;
private final int colTime =
2;
private final int colLast =
3;
private static final DateTimeFormatter DATE_FORMAT_CSV = DateTimeFormatter.
ofPattern("dd/MM/yyyy");
private static final DateTimeFormatter TIME_FORMAT_CSV = DateTimeFormatter.
ofPattern("HH:mm:ss");
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.
ofPattern("dd/MM/yyyy HH:mm:ss");
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.
ofPattern("dd-MM-yyyy HH:mm:ss");
public ExcelProfitDataProvider
(Path excelFile,
String sheetName
) {
this.
excelFile = excelFile
;
this.
sheetName = sheetName
;
}
/**
* Lê um ou vários arquivos:
* - Se excelFile for um arquivo: lê apenas esse arquivo (.xlsx, .xls ou .csv)
* - Se excelFile for uma pasta: lê todos os .xlsx, .xls e .csv da pasta.
*/
public List<Candle
> lerCandles
() throws IOException {
List<Candle
> candles =
new ArrayList<>();
if (Files.
isDirectory(excelFile
)) {
// Percorre todos os arquivos da pasta
try (Stream
<Path
> stream = Files.
list(excelFile
)) {
stream
.
filter(Files::isRegularFile
)
.
filter(p -
> {
String name = p.
getFileName().
toString().
toLowerCase(Locale.
ROOT);
return name.
endsWith(".xlsm") || name.
endsWith(".xlsx") || name.
endsWith(".xls") || name.
endsWith(".csv");
})
.
forEach(path -
> {
try {
lerCandlesDeArquivo
(path, candles
);
} catch (IOException e
) {
// aqui você pode trocar por log
e.
printStackTrace();
}
});
}
} else {
// Apenas um arquivo
lerCandlesDeArquivo
(excelFile, candles
);
}
return adicionarContadores
(candles
);
}
/**
* Decide se o arquivo é Excel ou CSV e delega para o método correto.
*/
private void lerCandlesDeArquivo
(Path arquivo,
List<Candle
> candles
) throws IOException {
String name = arquivo.
getFileName().
toString().
toLowerCase(Locale.
ROOT);
if (name.
endsWith(".xlsm") || name.
endsWith(".xlsx") || name.
endsWith(".xls")) {
lerCandlesDeArquivoExcel
(arquivo, candles
);
} else if (name.
endsWith(".csv")) {
lerCandlesDeArquivoCsv
(arquivo, candles
);
} else {
// Tipo não suportado, ignora ou loga
}
}
/**
* Lê os candles de UM arquivo Excel e adiciona na lista passada.
* (É basicamente o seu código original, só movido para cá)
*/
private void lerCandlesDeArquivoExcel
(Path arquivoExcel,
List<Candle
> candles
) throws IOException {
try (FileInputStream fis =
new FileInputStream(arquivoExcel.
toFile());
Workbook workbook =
new XSSFWorkbook
(fis
)) {
int numberOfSheets = workbook.
getNumberOfSheets();
// LER APENAS OS ATIVOS
List<String> ativos =
new ArrayList<String>();
for (int i =
0; i
< numberOfSheets
; i++
) {
Sheet sheet = workbook.
getSheetAt(i
);
if (sheet.
getSheetName().
equals(sheetName
)) {
boolean firstRow =
true;
for (Row row : sheet
) {
if (firstRow
) {
firstRow =
false; // pula o cabeçalho
continue;
}
Cell ativoCell = row.
getCell(1);
if (VerificadorUtil.
naoEstaNulo(ativoCell
)) {
String ativoString = ativoCell.
getStringCellValue();
if (VerificadorUtil.
naoEstaNuloOuVazio(ativoString
)) {
if (!ativos.
contains(ativoString
)) {
ativos.
add(ativoString
);
}
}
}
}
}
}
for (String ativo : ativos
) {
for (int i =
0; i
< numberOfSheets
; i++
) {
Sheet sheet = workbook.
getSheetAt(i
);
if (sheet.
getSheetName().
equals(sheetName
)) {
boolean firstRow =
true;
for (Row row : sheet
) {
if (firstRow
) {
firstRow =
false; // pula o cabeçalho
continue;
}
// 0 = Contador
// 1 = Ativo
// 2 = Dia
// 3 = Hora
// 4 = Abertura
// 5 = Máxima
// 6 = Mínima
// 7 = Fechamento
Cell contadorCell = row.
getCell(0);
Cell ativoCell = row.
getCell(1);
Cell dataCell = row.
getCell(2);
Cell horaCell = row.
getCell(3);
Cell aberturaCell = row.
getCell(4);
Cell maximaCell = row.
getCell(5);
Cell minimaCell = row.
getCell(6);
Cell fechamentoCell = row.
getCell(7);
Cell finalizadoCell = row.
getCell(8);
if (!ExcelDataUtils.
isNumeric(aberturaCell
) ||
!ExcelDataUtils.
isNumeric(maximaCell
)
||
!ExcelDataUtils.
isNumeric(minimaCell
) ||
!ExcelDataUtils.
isNumeric(fechamentoCell
)) {
continue;
}
String ativoDescricao = ativoCell.
getStringCellValue();
if (!ativo.
equals(ativoDescricao
)) {
continue;
}
Integer contador =
BigDecimal.
valueOf(contadorCell.
getNumericCellValue()).
intValue();
LocalDate data = ExcelDataUtils.
lerData(dataCell
);
LocalTime hora = ExcelDataUtils.
lerHora(horaCell
);
LocalDateTime dataHora = LocalDateTime.
of(data, hora
);
BigDecimal abertura =
BigDecimal.
valueOf(aberturaCell.
getNumericCellValue());
BigDecimal topo =
BigDecimal.
valueOf(maximaCell.
getNumericCellValue());
BigDecimal fundo =
BigDecimal.
valueOf(minimaCell.
getNumericCellValue());
BigDecimal fechamento =
BigDecimal.
valueOf(fechamentoCell.
getNumericCellValue());
String finalizado = finalizadoCell.
getStringCellValue();
if (finalizado.
equals("S")) {
Candle candle =
new Candle
(contador, ativoDescricao, dataHora, abertura, topo, fundo, fechamento, TipoPeriodoCandle.
M1.
getValor());
candles.
add(candle
);
}
}
}
}
}
} catch (EncryptedDocumentException e
) {
e.
printStackTrace();
}
}
/**
* Lê os candles de UM arquivo CSV e adiciona na lista passada.
* Espera layout:
* 0 = Ativo
* 1 = Dia (dd/MM/yyyy)
* 2 = Hora (HH:mm:ss)
* 3 = Abertura
* 4 = Máxima
* 5 = Mínima
* 6 = Fechamento
*/
private void lerCandlesDeArquivoCsv
(Path arquivoCsv,
List<Candle
> candles
) throws IOException {
try (BufferedReader br = Files.
newBufferedReader(arquivoCsv, StandardCharsets.
UTF_8)) {
String line
;
while ((line = br.
readLine()) !=
null) {
// Substitui line.isBlank() por trim().isEmpty()
String trimmed = line.
trim();
if (trimmed.
isEmpty()) {
continue;
}
// Ajuste aqui se o separador do seu CSV for vírgula
String[] parts = trimmed.
split(";", -
1);
if (parts.
length < 7) {
continue;
}
String ativoDescricao =
"";
String dataStr =
"";
String horaStr =
"";
String aberturaStr =
"";
String maximaStr =
"";
String minimaStr =
"";
String fechamentoStr =
"";
ativoDescricao = parts
[0].
trim();
dataStr = parts
[1].
trim();
if (parts.
length >=
9) {
horaStr = parts
[2].
trim();
aberturaStr = parts
[3].
trim();
maximaStr = parts
[4].
trim();
minimaStr = parts
[5].
trim();
fechamentoStr = parts
[6].
trim();
} else {
horaStr =
"18:00:00";
aberturaStr = parts
[2].
trim();
maximaStr = parts
[3].
trim();
minimaStr = parts
[4].
trim();
fechamentoStr = parts
[5].
trim();
}
// Ignora header, caso exista
if (ativoDescricao.
equalsIgnoreCase("ativo")) {
continue;
}
if (!isNumericString
(aberturaStr
) ||
!isNumericString
(maximaStr
) ||
!isNumericString
(minimaStr
) ||
!isNumericString
(fechamentoStr
)) {
continue;
}
LocalDate data = LocalDate.
parse(dataStr, DATE_FORMAT_CSV
);
LocalTime hora = LocalTime.
parse(horaStr, TIME_FORMAT_CSV
);
LocalDateTime dataHora = LocalDateTime.
of(data, hora
);
BigDecimal abertura = BigDecimalUtils.
converterStringEmBigDecimal(aberturaStr
);
BigDecimal topo = BigDecimalUtils.
converterStringEmBigDecimal(maximaStr
);
BigDecimal fundo = BigDecimalUtils.
converterStringEmBigDecimal(minimaStr
);
BigDecimal fechamento = BigDecimalUtils.
converterStringEmBigDecimal(fechamentoStr
);
Candle candle =
new Candle
(
null,
ativoDescricao,
dataHora,
abertura,
topo,
fundo,
fechamento,
TipoPeriodoCandle.
M1.
getValor()
);
candles.
add(candle
);
}
}
}
private boolean isNumericString
(String value
) {
if (value ==
null) return false;
String normalized = value.
trim().
replace(".",
"").
replace(",",
".");
if (normalized.
isEmpty()) return false;
try {
new BigDecimal(normalized
);
return true;
} catch (NumberFormatException e
) {
return false;
}
}
public static List<Candle
> inverterLista
(List<Candle
> candles
) {
List<Candle
> invertida =
new ArrayList<>(candles
);
Collections.
reverse(invertida
);
return invertida
;
}
// Contador separado para cada ativo iniciando pelo primeiro contador encontrado
public static List<Candle
> adicionarContadores
(List<Candle
> candles
) {
Map<String,
Integer> contadorPorAtivo =
new HashMap<>();
List<Candle
> comContadores =
new ArrayList<>();
for (Candle candle : candles
) {
String ativo = candle.
getNomeAtivo();
// Se for o primeiro candle do ativo, usar o contador original dele como ponto de partida
if (!contadorPorAtivo.
containsKey(ativo
)) {
int contadorInicial = candle.
getContadorCandle() !=
null
? candle.
getContadorCandle()
:
1; // fallback, se vier nulo
contadorPorAtivo.
put(ativo, contadorInicial
);
candle.
setContadorCandle(contadorInicial
);
comContadores.
add(candle
);
continue;
}
// Se não é o primeiro candle do ativo, incrementar baseado no último
int proximoContador = contadorPorAtivo.
get(ativo
) +
1;
contadorPorAtivo.
put(ativo, proximoContador
);
candle.
setContadorCandle(proximoContador
);
comContadores.
add(candle
);
}
return comContadores
;
}
/**
* 1 minuto = 1, 5 minutos = 2, 15 minutos = 3, 1 dia = 4
*/
private Timeframe resolveTipoTemporizador
(String sheetName
) {
if (sheetName ==
null) return null;
String name = sheetName.
toUpperCase(Locale.
ROOT);
if (name.
startsWith("1 MIN")) return Timeframe.
M1;
if (name.
startsWith("5 MIN")) return Timeframe.
M5;
if (name.
startsWith("15 MIN")) return Timeframe.
M15;
if (name.
startsWith("1 DIA")) return Timeframe.
D1;
return null;
}
}