Subversion Repositories Integrator Subversion

Rev

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