Rev 771 | Rev 776 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 771 | Rev 775 | ||
|---|---|---|---|
| Line 1... | Line 1... | ||
| 1 | package br.com.sl.core; |
1 | package br.com.sl.core; |
| 2 | 2 | ||
| - | 3 | import java.io.BufferedReader; |
|
| 3 | import java.io.FileInputStream; |
4 | import java.io.FileInputStream; |
| 4 | import java.io.IOException; |
5 | import java.io.IOException; |
| 5 | import java.io.InputStream; |
- | |
| 6 | import java.math.BigDecimal; |
6 | import java.math.BigDecimal; |
| - | 7 | import java.nio.charset.StandardCharsets; |
|
| - | 8 | import java.nio.file.Files; |
|
| 7 | import java.nio.file.Path; |
9 | import java.nio.file.Path; |
| 8 | import java.text.SimpleDateFormat; |
- | |
| 9 | import java.time.LocalDate; |
10 | import java.time.LocalDate; |
| 10 | import java.time.LocalDateTime; |
11 | import java.time.LocalDateTime; |
| 11 | import java.time.LocalTime; |
12 | import java.time.LocalTime; |
| 12 | import java.time.ZoneId; |
- | |
| 13 | import java.time.format.DateTimeFormatter; |
13 | import java.time.format.DateTimeFormatter; |
| 14 | import java.util.ArrayList; |
14 | import java.util.ArrayList; |
| 15 | import java.util.Collections; |
15 | import java.util.Collections; |
| 16 | import java.util.Date; |
- | |
| 17 | import java.util.List; |
16 | import java.util.List; |
| 18 | import java.util.Locale; |
17 | import java.util.Locale; |
| 19 | import java.util.Map; |
18 | import java.util.Map; |
| - | 19 | import java.util.stream.Stream; |
|
| 20 | 20 | ||
| 21 | import org.apache.poi.EncryptedDocumentException; |
21 | import org.apache.poi.EncryptedDocumentException; |
| 22 | import org.apache.poi.openxml4j.exceptions.InvalidFormatException; |
- | |
| 23 | import org.apache.poi.ss.usermodel.Cell; |
22 | import org.apache.poi.ss.usermodel.Cell; |
| 24 | import org.apache.poi.ss.usermodel.CellType; |
- | |
| 25 | import org.apache.poi.ss.usermodel.DateUtil; |
- | |
| 26 | import org.apache.poi.ss.usermodel.Row; |
23 | import org.apache.poi.ss.usermodel.Row; |
| 27 | import org.apache.poi.ss.usermodel.Sheet; |
24 | import org.apache.poi.ss.usermodel.Sheet; |
| 28 | import org.apache.poi.ss.usermodel.Workbook; |
25 | import org.apache.poi.ss.usermodel.Workbook; |
| 29 | import org.apache.poi.ss.usermodel.WorkbookFactory; |
- | |
| 30 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
26 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
| 31 | 27 | ||
| 32 | import br.com.kronus.core.Timeframe; |
28 | import br.com.kronus.core.Timeframe; |
| 33 | import br.com.sl.domain.dto.robo.ProfitTick; |
29 | import br.com.sl.domain.dto.robo.ProfitTick; |
| 34 | import br.com.sl.domain.model.Candle; |
30 | import br.com.sl.domain.model.Candle; |
| 35 | import br.com.sl.domain.model.tipos.TipoPeriodo; |
- | |
| 36 | import br.com.sl.domain.model.tipos.TipoPeriodoCandle; |
31 | import br.com.sl.domain.model.tipos.TipoPeriodoCandle; |
| 37 | import br.com.sl.shared.ExcelDataUtils; |
32 | import br.com.sl.shared.ExcelDataUtils; |
| 38 | 33 | ||
| 39 | public class ExcelProfitHistoricoDataProvider implements ProfitDataProvider { |
34 | public class ExcelProfitHistoricoDataProvider implements ProfitDataProvider { |
| 40 | 35 | ||
| 41 | private final Path excelFile; |
- | |
| - | 36 | private final Path excelFile; // agora pode ser arquivo OU pasta |
|
| 42 | private final String sheetName; |
37 | private final String sheetName; |
| 43 | - | ||
| 44 | private static final DateTimeFormatter STRING_DATA_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); |
- | |
| 45 | private static final DateTimeFormatter STRING_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); |
- | |
| - | 38 | ||
| - | 39 | private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy"); |
|
| - | 40 | private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); |
|
| 46 | 41 | ||
| 47 | public ExcelProfitHistoricoDataProvider(Path excelFile, String sheetName) { |
42 | public ExcelProfitHistoricoDataProvider(Path excelFile, String sheetName) { |
| 48 | this.excelFile = excelFile; |
43 | this.excelFile = excelFile; |
| 49 | this.sheetName = sheetName; |
44 | this.sheetName = sheetName; |
| 50 | }
|
45 | }
|
| 51 | - | ||
| - | 46 | ||
| 52 | @Override |
47 | @Override |
| 53 | public Map<String, ProfitTick> readCurrentTicks() { |
48 | public Map<String, ProfitTick> readCurrentTicks() { |
| 54 | // TODO Auto-generated method stub
|
- | |
| 55 | return null; |
- | |
| - | 49 | // TODO Auto-generated method stub
|
|
| - | 50 | return null; |
|
| 56 | }
|
51 | }
|
| 57 | - | ||
| - | 52 | ||
| 58 | /**
|
53 | /**
|
| 59 | * Lê o arquivo Excel dentro do resources.
|
- | |
| 60 | * Exemplo de uso:
|
- | |
| 61 | * lerCandles("/dados/Dados Trade 20251117.xlsx");
|
- | |
| 62 | * @throws IOException
|
- | |
| - | 54 | * Lê um ou vários arquivos:
|
|
| - | 55 | * - Se excelFile for um arquivo: lê apenas esse arquivo (.xlsx, .xls ou .csv)
|
|
| - | 56 | * - Se excelFile for uma pasta: lê todos os .xlsx, .xls e .csv da pasta.
|
|
| 63 | */
|
57 | */
|
| 64 | public List<Candle> lerCandles() throws IOException { |
58 | public List<Candle> lerCandles() throws IOException { |
| 65 | List<Candle> candles = new ArrayList<>(); |
59 | List<Candle> candles = new ArrayList<>(); |
| 66 | 60 | ||
| 67 | try (FileInputStream fis = new FileInputStream(excelFile.toFile()); Workbook workbook = new XSSFWorkbook(fis)) { |
- | |
| - | 61 | if (Files.isDirectory(excelFile)) { |
|
| - | 62 | // Percorre todos os arquivos da pasta
|
|
| - | 63 | try (Stream<Path> stream = Files.list(excelFile)) { |
|
| - | 64 | stream |
|
| - | 65 | .filter(Files::isRegularFile) |
|
| - | 66 | .filter(p -> { |
|
| - | 67 | String name = p.getFileName().toString().toLowerCase(Locale.ROOT); |
|
| - | 68 | return name.endsWith(".xlsx") || name.endsWith(".xls") || name.endsWith(".csv"); |
|
| - | 69 | }) |
|
| - | 70 | .forEach(path -> { |
|
| - | 71 | try { |
|
| - | 72 | lerCandlesDeArquivo(path, candles); |
|
| - | 73 | } catch (IOException e) { |
|
| - | 74 | // aqui você pode trocar por log
|
|
| - | 75 | e.printStackTrace(); |
|
| - | 76 | }
|
|
| - | 77 | }); |
|
| - | 78 | }
|
|
| - | 79 | } else { |
|
| - | 80 | // Apenas um arquivo
|
|
| - | 81 | lerCandlesDeArquivo(excelFile, candles); |
|
| - | 82 | }
|
|
| - | 83 | ||
| - | 84 | return adicionarContadores(inverterLista(candles)); |
|
| - | 85 | }
|
|
| - | 86 | ||
| - | 87 | /**
|
|
| - | 88 | * Decide se o arquivo é Excel ou CSV e delega para o método correto.
|
|
| - | 89 | */
|
|
| - | 90 | private void lerCandlesDeArquivo(Path arquivo, List<Candle> candles) throws IOException { |
|
| - | 91 | String name = arquivo.getFileName().toString().toLowerCase(Locale.ROOT); |
|
| - | 92 | ||
| - | 93 | if (name.endsWith(".xlsx") || name.endsWith(".xls")) { |
|
| - | 94 | lerCandlesDeArquivoExcel(arquivo, candles); |
|
| - | 95 | } else if (name.endsWith(".csv")) { |
|
| - | 96 | lerCandlesDeArquivoCsv(arquivo, candles); |
|
| - | 97 | } else { |
|
| - | 98 | // Tipo não suportado, ignora ou loga
|
|
| - | 99 | }
|
|
| - | 100 | }
|
|
| - | 101 | ||
| - | 102 | /**
|
|
| - | 103 | * Lê os candles de UM arquivo Excel e adiciona na lista passada.
|
|
| - | 104 | * (É basicamente o seu código original, só movido para cá)
|
|
| - | 105 | */
|
|
| - | 106 | private void lerCandlesDeArquivoExcel(Path arquivoExcel, List<Candle> candles) throws IOException { |
|
| - | 107 | try (FileInputStream fis = new FileInputStream(arquivoExcel.toFile()); |
|
| - | 108 | Workbook workbook = new XSSFWorkbook(fis)) { |
|
| - | 109 | ||
| 68 | int numberOfSheets = workbook.getNumberOfSheets(); |
110 | int numberOfSheets = workbook.getNumberOfSheets(); |
| 69 | 111 | ||
| 70 | for (int i = 0; i < numberOfSheets; i++) { |
112 | for (int i = 0; i < numberOfSheets; i++) { |
| 71 | Sheet sheet = workbook.getSheetAt(i); |
113 | Sheet sheet = workbook.getSheetAt(i); |
| - | 114 | ||
| 72 | for (Row row : sheet) { |
115 | for (Row row : sheet) { |
| 73 | 116 | ||
| 74 | // 0 = Ativo
|
117 | // 0 = Ativo
|
| 75 | // 1 = Dia
|
118 | // 1 = Dia
|
| 76 | // 2 = Hora
|
119 | // 2 = Hora
|
| Line 88... | Line 131... | ||
| 88 | Cell maximaCell = row.getCell(4); |
131 | Cell maximaCell = row.getCell(4); |
| 89 | Cell minimaCell = row.getCell(5); |
132 | Cell minimaCell = row.getCell(5); |
| 90 | Cell fechamentoCell = row.getCell(6); |
133 | Cell fechamentoCell = row.getCell(6); |
| 91 | 134 | ||
| 92 | if (!ExcelDataUtils.isNumeric(aberturaCell) || !ExcelDataUtils.isNumeric(maximaCell) |
135 | if (!ExcelDataUtils.isNumeric(aberturaCell) || !ExcelDataUtils.isNumeric(maximaCell) |
| 93 | || !ExcelDataUtils.isNumeric(minimaCell) || !ExcelDataUtils.isNumeric(fechamentoCell)) { |
- | |
| - | 136 | || !ExcelDataUtils.isNumeric(minimaCell) || !ExcelDataUtils.isNumeric(fechamentoCell)) { |
|
| 94 | continue; |
137 | continue; |
| 95 | }
|
138 | }
|
| 96 | - | ||
| - | 139 | ||
| 97 | LocalDate data = ExcelDataUtils.lerData(dataCell); |
140 | LocalDate data = ExcelDataUtils.lerData(dataCell); |
| 98 | LocalTime hora = ExcelDataUtils.lerHora(horaCell); |
141 | LocalTime hora = ExcelDataUtils.lerHora(horaCell); |
| 99 | hora = hora.plusMinutes(6).plusSeconds(28); |
142 | hora = hora.plusMinutes(6).plusSeconds(28); |
| 100 | LocalDateTime dataHora = LocalDateTime.of(data, hora); |
143 | LocalDateTime dataHora = LocalDateTime.of(data, hora); |
| 101 | - | ||
| - | 144 | ||
| 102 | String ativoDescricao = ativoCell.getStringCellValue(); |
145 | String ativoDescricao = ativoCell.getStringCellValue(); |
| 103 | BigDecimal abertura = BigDecimal.valueOf(aberturaCell.getNumericCellValue()); |
146 | BigDecimal abertura = BigDecimal.valueOf(aberturaCell.getNumericCellValue()); |
| 104 | BigDecimal topo = BigDecimal.valueOf(maximaCell.getNumericCellValue()); |
147 | BigDecimal topo = BigDecimal.valueOf(maximaCell.getNumericCellValue()); |
| 105 | BigDecimal fundo = BigDecimal.valueOf(minimaCell.getNumericCellValue()); |
148 | BigDecimal fundo = BigDecimal.valueOf(minimaCell.getNumericCellValue()); |
| 106 | BigDecimal fechamento = BigDecimal.valueOf(fechamentoCell.getNumericCellValue()); |
149 | BigDecimal fechamento = BigDecimal.valueOf(fechamentoCell.getNumericCellValue()); |
| 107 | 150 | ||
| 108 | Candle candle = new Candle(null, ativoDescricao, dataHora, abertura, topo, fundo, fechamento, TipoPeriodoCandle.M1.getValor()); |
- | |
| 109 | candles.add(candle); |
- | |
| - | 151 | Candle candle = new Candle( |
|
| - | 152 | null,
|
|
| - | 153 | ativoDescricao, |
|
| - | 154 | dataHora, |
|
| - | 155 | abertura, |
|
| - | 156 | topo, |
|
| - | 157 | fundo, |
|
| - | 158 | fechamento, |
|
| - | 159 | TipoPeriodoCandle.M1.getValor() |
|
| - | 160 | ); |
|
| - | 161 | candles.add(candle); |
|
| 110 | }
|
162 | }
|
| 111 | }
|
163 | }
|
| 112 | } catch (EncryptedDocumentException e) { |
164 | } catch (EncryptedDocumentException e) { |
| 113 | // TODO Auto-generated catch block
|
- | |
| 114 | e.printStackTrace(); |
- | |
| - | 165 | e.printStackTrace(); |
|
| 115 | }
|
166 | }
|
| 116 | return adicionarContadores(inverterLista(candles)); |
- | |
| 117 | }
|
167 | }
|
| 118 | - | ||
| - | 168 | ||
| - | 169 | /**
|
|
| - | 170 | * Lê os candles de UM arquivo CSV e adiciona na lista passada.
|
|
| - | 171 | * Espera layout:
|
|
| - | 172 | * 0 = Ativo
|
|
| - | 173 | * 1 = Dia (dd/MM/yyyy)
|
|
| - | 174 | * 2 = Hora (HH:mm:ss)
|
|
| - | 175 | * 3 = Abertura
|
|
| - | 176 | * 4 = Máxima
|
|
| - | 177 | * 5 = Mínima
|
|
| - | 178 | * 6 = Fechamento
|
|
| - | 179 | */
|
|
| - | 180 | private void lerCandlesDeArquivoCsv(Path arquivoCsv, List<Candle> candles) throws IOException { |
|
| - | 181 | try (BufferedReader br = Files.newBufferedReader(arquivoCsv, StandardCharsets.UTF_8)) { |
|
| - | 182 | String line; |
|
| - | 183 | while ((line = br.readLine()) != null) { |
|
| - | 184 | ||
| - | 185 | // Substitui line.isBlank() por trim().isEmpty()
|
|
| - | 186 | String trimmed = line.trim(); |
|
| - | 187 | if (trimmed.isEmpty()) { |
|
| - | 188 | continue; |
|
| - | 189 | }
|
|
| - | 190 | ||
| - | 191 | // Ajuste aqui se o separador do seu CSV for vírgula
|
|
| - | 192 | String[] parts = trimmed.split(";", -1); |
|
| - | 193 | if (parts.length < 7) { |
|
| - | 194 | continue; |
|
| - | 195 | }
|
|
| - | 196 | ||
| - | 197 | String ativoDescricao = parts[0].trim(); |
|
| - | 198 | String dataStr = parts[1].trim(); |
|
| - | 199 | String horaStr = parts[2].trim(); |
|
| - | 200 | String aberturaStr = parts[3].trim(); |
|
| - | 201 | String maximaStr = parts[4].trim(); |
|
| - | 202 | String minimaStr = parts[5].trim(); |
|
| - | 203 | String fechamentoStr = parts[6].trim(); |
|
| - | 204 | ||
| - | 205 | // Ignora header, caso exista
|
|
| - | 206 | if (ativoDescricao.equalsIgnoreCase("ativo")) { |
|
| - | 207 | continue; |
|
| - | 208 | }
|
|
| - | 209 | ||
| - | 210 | if (!isNumericString(aberturaStr) || |
|
| - | 211 | !isNumericString(maximaStr) || |
|
| - | 212 | !isNumericString(minimaStr) || |
|
| - | 213 | !isNumericString(fechamentoStr)) { |
|
| - | 214 | continue; |
|
| - | 215 | }
|
|
| - | 216 | ||
| - | 217 | LocalDate data = LocalDate.parse(dataStr, DATE_FORMAT); |
|
| - | 218 | LocalTime hora = LocalTime.parse(horaStr, TIME_FORMAT); |
|
| - | 219 | LocalDateTime dataHora = LocalDateTime.of(data, hora); |
|
| - | 220 | ||
| - | 221 | BigDecimal abertura = parseBigDecimal(aberturaStr); |
|
| - | 222 | BigDecimal topo = parseBigDecimal(maximaStr); |
|
| - | 223 | BigDecimal fundo = parseBigDecimal(minimaStr); |
|
| - | 224 | BigDecimal fechamento = parseBigDecimal(fechamentoStr); |
|
| - | 225 | ||
| - | 226 | Candle candle = new Candle( |
|
| - | 227 | null,
|
|
| - | 228 | ativoDescricao, |
|
| - | 229 | dataHora, |
|
| - | 230 | abertura, |
|
| - | 231 | topo, |
|
| - | 232 | fundo, |
|
| - | 233 | fechamento, |
|
| - | 234 | TipoPeriodoCandle.M1.getValor() |
|
| - | 235 | ); |
|
| - | 236 | candles.add(candle); |
|
| - | 237 | }
|
|
| - | 238 | }
|
|
| - | 239 | }
|
|
| - | 240 | ||
| - | 241 | ||
| - | 242 | private BigDecimal parseBigDecimal(String value) { |
|
| - | 243 | if (value == null) return null; |
|
| - | 244 | String normalized = value.trim().replace(".", "").replace(",", "."); |
|
| - | 245 | // Se o Profit já exportar com ponto como separador decimal, remova o replace(".","")
|
|
| - | 246 | // e deixe apenas: value.trim().replace(",", ".")
|
|
| - | 247 | if (normalized.isEmpty()) return null; |
|
| - | 248 | return new BigDecimal(normalized); |
|
| - | 249 | }
|
|
| - | 250 | ||
| - | 251 | private boolean isNumericString(String value) { |
|
| - | 252 | if (value == null) return false; |
|
| - | 253 | String normalized = value.trim().replace(".", "").replace(",", "."); |
|
| - | 254 | if (normalized.isEmpty()) return false; |
|
| - | 255 | try { |
|
| - | 256 | new BigDecimal(normalized); |
|
| - | 257 | return true; |
|
| - | 258 | } catch (NumberFormatException e) { |
|
| - | 259 | return false; |
|
| - | 260 | }
|
|
| - | 261 | }
|
|
| - | 262 | ||
| 119 | public static List<Candle> inverterLista(List<Candle> candles) { |
263 | public static List<Candle> inverterLista(List<Candle> candles) { |
| 120 | List<Candle> invertida = new ArrayList<>(candles); |
264 | List<Candle> invertida = new ArrayList<>(candles); |
| 121 | Collections.reverse(invertida); |
265 | Collections.reverse(invertida); |
| 122 | return invertida; |
266 | return invertida; |
| 123 | }
|
267 | }
|
| 124 | 268 | ||
| 125 | public static List<Candle> adicionarContadores(List<Candle> candles) { |
269 | public static List<Candle> adicionarContadores(List<Candle> candles) { |
| 126 | Integer contador = 1; |
270 | Integer contador = 1; |
| 127 | List<Candle> comContadores = new ArrayList<>(); |
- | |
| - | 271 | List<Candle> comContadores = new ArrayList<>(); |
|
| 128 | for (Candle candle : candles) { |
272 | for (Candle candle : candles) { |
| 129 | candle.setContadorCandle(contador); |
- | |
| 130 | comContadores.add(candle); |
- | |
| 131 | contador++;
|
- | |
| - | 273 | candle.setContadorCandle(contador); |
|
| - | 274 | comContadores.add(candle); |
|
| - | 275 | contador++;
|
|
| 132 | }
|
276 | }
|
| 133 | return comContadores; |
277 | return comContadores; |
| 134 | }
|
278 | }
|
| 135 | - | ||
| - | 279 | ||
| 136 | /**
|
280 | /**
|
| 137 | * 1 minuto = 1, 5 minutos = 2, 15 minutos = 3, 1 dia = 4
|
281 | * 1 minuto = 1, 5 minutos = 2, 15 minutos = 3, 1 dia = 4
|
| 138 | */
|
282 | */
|
| 139 | private Timeframe resolveTipoTemporizador(String sheetName) { |
283 | private Timeframe resolveTipoTemporizador(String sheetName) { |
| 140 | if (sheetName == null) return null; |
284 | if (sheetName == null) return null; |
| Line 147... | Line 291... | ||
| 147 | if (name.startsWith("1 DIA")) return Timeframe.D1; |
291 | if (name.startsWith("1 DIA")) return Timeframe.D1; |
| 148 | 292 | ||
| 149 | return null; |
293 | return null; |
| 150 | }
|
294 | }
|
| 151 | 295 | ||
| 152 | }
|
296 | }
|