Subversion Repositories Integrator Subversion

Rev

Rev 775 | Rev 779 | Go to most recent revision | 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;
16
import java.util.List;
17
import java.util.Locale;
18
import java.util.Map;
775 blopes 19
import java.util.stream.Stream;
761 blopes 20
 
21
import org.apache.poi.EncryptedDocumentException;
22
import org.apache.poi.ss.usermodel.Cell;
23
import org.apache.poi.ss.usermodel.Row;
24
import org.apache.poi.ss.usermodel.Sheet;
25
import org.apache.poi.ss.usermodel.Workbook;
767 blopes 26
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
761 blopes 27
 
28
import br.com.kronus.core.Timeframe;
29
import br.com.sl.domain.dto.robo.ProfitTick;
30
import br.com.sl.domain.model.Candle;
31
import br.com.sl.domain.model.tipos.TipoPeriodoCandle;
767 blopes 32
import br.com.sl.shared.ExcelDataUtils;
761 blopes 33
 
34
public class ExcelProfitHistoricoDataProvider implements ProfitDataProvider {
35
 
775 blopes 36
    private final Path excelFile; // agora pode ser arquivo OU pasta
761 blopes 37
    private final String sheetName;
38
 
775 blopes 39
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy");
40
    private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
41
 
767 blopes 42
    public ExcelProfitHistoricoDataProvider(Path excelFile, String sheetName) {
43
        this.excelFile = excelFile;
761 blopes 44
        this.sheetName = sheetName;
45
    }
775 blopes 46
 
761 blopes 47
    @Override
48
    public Map<String, ProfitTick> readCurrentTicks() {
775 blopes 49
        // TODO Auto-generated method stub
50
        return null;
761 blopes 51
    }
775 blopes 52
 
761 blopes 53
    /**
775 blopes 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.
761 blopes 57
     */
58
    public List<Candle> lerCandles() throws IOException {
59
        List<Candle> candles = new ArrayList<>();
60
 
775 blopes 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
 
761 blopes 110
            int numberOfSheets = workbook.getNumberOfSheets();
111
 
112
            for (int i = 0; i < numberOfSheets; i++) {
113
                Sheet sheet = workbook.getSheetAt(i);
775 blopes 114
 
771 blopes 115
                for (Row row : sheet) {
116
 
117
                    // 0 = Ativo
118
                    // 1 = Dia
119
                    // 2 = Hora
120
                    // 3 = Abertura
121
                    // 4 = Máxima
122
                    // 5 = Mínima
123
                    // 6 = Fechamento
124
                    // 7 = Volume
125
                    // 8 = ...
126
 
127
                    Cell ativoCell      = row.getCell(0);
128
                    Cell dataCell       = row.getCell(1);
129
                    Cell horaCell       = row.getCell(2);
130
                    Cell aberturaCell   = row.getCell(3);
131
                    Cell maximaCell     = row.getCell(4);
132
                    Cell minimaCell     = row.getCell(5);
133
                    Cell fechamentoCell = row.getCell(6);
134
 
135
                    if (!ExcelDataUtils.isNumeric(aberturaCell) || !ExcelDataUtils.isNumeric(maximaCell)
775 blopes 136
                            || !ExcelDataUtils.isNumeric(minimaCell) || !ExcelDataUtils.isNumeric(fechamentoCell)) {
771 blopes 137
                        continue;
138
                    }
775 blopes 139
 
771 blopes 140
                    LocalDate data = ExcelDataUtils.lerData(dataCell);
141
                    LocalTime hora = ExcelDataUtils.lerHora(horaCell);
142
                    hora = hora.plusMinutes(6).plusSeconds(28);
143
                    LocalDateTime dataHora = LocalDateTime.of(data, hora);
775 blopes 144
 
771 blopes 145
                    String ativoDescricao = ativoCell.getStringCellValue();
146
                    BigDecimal abertura = BigDecimal.valueOf(aberturaCell.getNumericCellValue());
147
                    BigDecimal topo = BigDecimal.valueOf(maximaCell.getNumericCellValue());
148
                    BigDecimal fundo = BigDecimal.valueOf(minimaCell.getNumericCellValue());
149
                    BigDecimal fechamento = BigDecimal.valueOf(fechamentoCell.getNumericCellValue());
150
 
775 blopes 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);
761 blopes 162
                }
163
            }
164
        } catch (EncryptedDocumentException e) {
775 blopes 165
            e.printStackTrace();
767 blopes 166
        }
761 blopes 167
    }
775 blopes 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
                }
776 blopes 196
 
197
                String ativoDescricao = "";
198
                String dataStr        = "";
199
                String horaStr        = "";
200
                String aberturaStr    = "";
201
                String maximaStr      = "";
202
                String minimaStr      = "";
203
                String fechamentoStr  = "";
204
 
205
                ativoDescricao = parts[0].trim();
206
                dataStr        = parts[1].trim();
207
                if (parts.length >= 9) {
208
                        horaStr        = parts[2].trim();
209
                        aberturaStr    = parts[3].trim();
210
                        maximaStr      = parts[4].trim();
211
                        minimaStr      = parts[5].trim();
212
                        fechamentoStr  = parts[6].trim();
213
                } else {
214
                        horaStr        = "18:00:00";
215
                        aberturaStr    = parts[2].trim();
216
                        maximaStr      = parts[3].trim();
217
                        minimaStr      = parts[4].trim();
218
                        fechamentoStr  = parts[5].trim();
219
                }
775 blopes 220
 
221
                // Ignora header, caso exista
222
                if (ativoDescricao.equalsIgnoreCase("ativo")) {
223
                    continue;
224
                }
225
 
226
                if (!isNumericString(aberturaStr) ||
227
                    !isNumericString(maximaStr)   ||
228
                    !isNumericString(minimaStr)   ||
229
                    !isNumericString(fechamentoStr)) {
230
                    continue;
231
                }
232
 
233
                LocalDate data = LocalDate.parse(dataStr, DATE_FORMAT);
234
                LocalTime hora = LocalTime.parse(horaStr, TIME_FORMAT);
235
                LocalDateTime dataHora = LocalDateTime.of(data, hora);
236
 
237
                BigDecimal abertura   = parseBigDecimal(aberturaStr);
238
                BigDecimal topo       = parseBigDecimal(maximaStr);
239
                BigDecimal fundo      = parseBigDecimal(minimaStr);
240
                BigDecimal fechamento = parseBigDecimal(fechamentoStr);
241
 
242
                Candle candle = new Candle(
243
                        null,
244
                        ativoDescricao,
245
                        dataHora,
246
                        abertura,
247
                        topo,
248
                        fundo,
249
                        fechamento,
250
                        TipoPeriodoCandle.M1.getValor()
251
                );
252
                candles.add(candle);
253
            }
254
        }
255
    }
256
 
257
 
258
    private BigDecimal parseBigDecimal(String value) {
259
        if (value == null) return null;
260
        String normalized = value.trim().replace(".", "").replace(",", ".");
261
        // Se o Profit já exportar com ponto como separador decimal, remova o replace(".","")
262
        // e deixe apenas: value.trim().replace(",", ".")
263
        if (normalized.isEmpty()) return null;
264
        return new BigDecimal(normalized);
265
    }
266
 
267
    private boolean isNumericString(String value) {
268
        if (value == null) return false;
269
        String normalized = value.trim().replace(".", "").replace(",", ".");
270
        if (normalized.isEmpty()) return false;
271
        try {
272
            new BigDecimal(normalized);
273
            return true;
274
        } catch (NumberFormatException e) {
275
            return false;
276
        }
277
    }
278
 
761 blopes 279
    public static List<Candle> inverterLista(List<Candle> candles) {
280
        List<Candle> invertida = new ArrayList<>(candles);
281
        Collections.reverse(invertida);
282
        return invertida;
283
    }
284
 
285
    public static List<Candle> adicionarContadores(List<Candle> candles) {
776 blopes 286
        // Contador separado para cada ativo
287
        Map<String, Integer> contadorPorAtivo = new java.util.HashMap<>();
288
 
775 blopes 289
        List<Candle> comContadores = new ArrayList<>();
776 blopes 290
 
761 blopes 291
        for (Candle candle : candles) {
776 blopes 292
 
293
            // Ajuste aqui o getter conforme estiver na sua classe Candle
294
            // Ex.: getAtivo(), getAtivoDescricao(), getTicker()...
295
            String ativo = candle.getNomeAtivo();
296
 
297
            Integer contadorAtual = contadorPorAtivo.get(ativo);
298
            if (contadorAtual == null) {
299
                contadorAtual = 0;
300
            }
301
 
302
            contadorAtual++;
303
            contadorPorAtivo.put(ativo, contadorAtual);
304
 
305
            candle.setContadorCandle(contadorAtual);
775 blopes 306
            comContadores.add(candle);
761 blopes 307
        }
776 blopes 308
 
761 blopes 309
        return comContadores;
310
    }
775 blopes 311
 
776 blopes 312
 
761 blopes 313
    /**
314
     * 1 minuto = 1, 5 minutos = 2, 15 minutos = 3, 1 dia = 4
315
     */
316
    private Timeframe resolveTipoTemporizador(String sheetName) {
317
        if (sheetName == null) return null;
318
 
319
        String name = sheetName.toUpperCase(Locale.ROOT);
320
 
321
        if (name.startsWith("1 MIN"))  return Timeframe.M1;
322
        if (name.startsWith("5 MIN"))  return Timeframe.M5;
323
        if (name.startsWith("15 MIN")) return Timeframe.M15;
324
        if (name.startsWith("1 DIA"))  return Timeframe.D1;
325
 
326
        return null;
327
    }
328
 
775 blopes 329
}