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.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 ExcelProfitHistoricoDataProvider
implements ProfitDataProvider
{
private final Path excelFile
; // agora pode ser arquivo OU pasta
private final String sheetName
;
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.
ofPattern("dd/MM/yyyy");
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.
ofPattern("HH:mm:ss");
public ExcelProfitHistoricoDataProvider
(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
(inverterLista
(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();
for (int i =
0; i
< numberOfSheets
; i++
) {
Sheet sheet = workbook.
getSheetAt(i
);
for (Row row : sheet
) {
// 0 = Ativo
// 1 = Dia
// 2 = Hora
// 3 = Abertura
// 4 = Máxima
// 5 = Mínima
// 6 = Fechamento
// 7 = Volume
// 8 = ...
Cell ativoCell = row.
getCell(0);
Cell dataCell = row.
getCell(1);
Cell horaCell = row.
getCell(2);
Cell aberturaCell = row.
getCell(3);
Cell maximaCell = row.
getCell(4);
Cell minimaCell = row.
getCell(5);
Cell fechamentoCell = row.
getCell(6);
if (!ExcelDataUtils.
isNumeric(aberturaCell
) ||
!ExcelDataUtils.
isNumeric(maximaCell
)
||
!ExcelDataUtils.
isNumeric(minimaCell
) ||
!ExcelDataUtils.
isNumeric(fechamentoCell
)) {
continue;
}
LocalDate data = ExcelDataUtils.
lerData(dataCell
);
LocalTime hora = ExcelDataUtils.
lerHora(horaCell
);
hora = hora.
plusMinutes(6).
plusSeconds(28);
LocalDateTime dataHora = LocalDateTime.
of(data, hora
);
String ativoDescricao = ativoCell.
getStringCellValue();
BigDecimal abertura =
BigDecimal.
valueOf(aberturaCell.
getNumericCellValue());
BigDecimal topo =
BigDecimal.
valueOf(maximaCell.
getNumericCellValue());
BigDecimal fundo =
BigDecimal.
valueOf(minimaCell.
getNumericCellValue());
BigDecimal fechamento =
BigDecimal.
valueOf(fechamentoCell.
getNumericCellValue());
Candle candle =
new Candle
(
null,
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
);
LocalTime hora = LocalTime.
parse(horaStr, TIME_FORMAT
);
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;
}
}