Subversion Repositories Integrator Subversion

Rev

Rev 782 | Details | Compare with Previous | Last modification | View Log | RSS feed

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