Rev 762 | 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.domain.service.impl; |
| 2 | |||
| 3 | import java.math.BigDecimal; |
||
| 4 | import java.nio.file.Paths; |
||
| 764 | blopes | 5 | import java.time.Duration; |
| 761 | blopes | 6 | import java.time.LocalDateTime; |
| 7 | import java.time.temporal.ChronoUnit; |
||
| 8 | import java.util.Map; |
||
| 9 | import java.util.concurrent.ConcurrentHashMap; |
||
| 10 | import java.util.concurrent.TimeUnit; |
||
| 11 | import java.util.concurrent.atomic.AtomicBoolean; |
||
| 12 | |||
| 13 | import javax.inject.Singleton; |
||
| 14 | |||
| 15 | import org.springframework.beans.factory.annotation.Autowired; |
||
| 16 | import org.springframework.stereotype.Service; |
||
| 17 | |||
| 764 | blopes | 18 | import com.itextpdf.text.log.SysoLogger; |
| 19 | |||
| 761 | blopes | 20 | import br.com.ec.core.generic.AbstractService; |
| 21 | import br.com.ec.core.generic.GenericRepository; |
||
| 22 | import br.com.ec.core.validador.Validador; |
||
| 23 | import br.com.sl.core.ExcelProfitDataProvider; |
||
| 764 | blopes | 24 | import br.com.sl.core.ExcelProfitTempoRealPorSegundoProvider; |
| 761 | blopes | 25 | import br.com.sl.core.ProfitDataProvider; |
| 26 | import br.com.sl.domain.dto.RoboDTO; |
||
| 27 | import br.com.sl.domain.dto.robo.CandleState; |
||
| 28 | import br.com.sl.domain.dto.robo.ProfitTick; |
||
| 29 | import br.com.sl.domain.service.CandleService; |
||
| 30 | import br.com.sl.domain.service.RoboColetorService; |
||
| 31 | |||
| 32 | @Singleton |
||
| 33 | @Service |
||
| 34 | public class RoboColetorServiceImpl extends AbstractService<RoboDTO> implements RoboColetorService { |
||
| 35 | |||
| 36 | private final AtomicBoolean rodando = new AtomicBoolean(false); |
||
| 37 | |||
| 762 | blopes | 38 | private RoboDTO roboDTO = new RoboDTO(); |
| 761 | blopes | 39 | |
| 40 | private CandleService candleService; |
||
| 41 | |||
| 42 | private ProfitDataProvider profitDataProvider; |
||
| 43 | |||
| 44 | // estado de candle por ativo |
||
| 45 | private final Map<String, CandleState> estados = new ConcurrentHashMap<>(); |
||
| 46 | |||
| 47 | // config básica (pode ser lido de properties) |
||
| 48 | private static final String EXCEL_PATH = "C:/trade/temporeal.xlsx"; |
||
| 49 | private static final String EXCEL_SHEET = "Asset"; |
||
| 50 | |||
| 51 | @Autowired |
||
| 52 | public RoboColetorServiceImpl(CandleService candleService, Validador validador) { |
||
| 53 | super(validador); |
||
| 54 | this.candleService = candleService; |
||
| 55 | } |
||
| 56 | |||
| 57 | @Override |
||
| 58 | protected GenericRepository<RoboDTO> getRepository() { |
||
| 59 | return null; |
||
| 60 | } |
||
| 61 | |||
| 62 | public RoboDTO getRoboDTO() { |
||
| 63 | return roboDTO; |
||
| 64 | } |
||
| 65 | public void setRoboDTO(RoboDTO roboDTO) { |
||
| 66 | this.roboDTO = roboDTO; |
||
| 67 | } |
||
| 68 | |||
| 69 | @Override |
||
| 70 | public void iniciarColetor1Minuto() { |
||
| 71 | RoboDTO roboDTO = new RoboDTO(); |
||
| 72 | roboDTO.setRodando(true); |
||
| 73 | setRoboDTO(roboDTO); |
||
| 74 | |||
| 764 | blopes | 75 | this.profitDataProvider = new ExcelProfitTempoRealPorSegundoProvider(Paths.get(EXCEL_PATH), EXCEL_SHEET); |
| 761 | blopes | 76 | if (!rodando.compareAndSet(false, true)) { |
| 77 | // já está rodando |
||
| 78 | return; |
||
| 79 | } |
||
| 80 | |||
| 81 | Thread t = new Thread(this::loopColetor, "CandleCollector-1M"); |
||
| 82 | t.setDaemon(true); |
||
| 83 | t.start(); |
||
| 84 | } |
||
| 85 | |||
| 86 | private void loopColetor() { |
||
| 764 | blopes | 87 | System.out.println("INICIANDO COLETOR DE DADOS..."); |
| 88 | getRoboDTO().setObservacaoRobo("INICIANDO COLETOR DE DADOS..."); |
||
| 89 | |||
| 90 | /* |
||
| 91 | try { |
||
| 92 | // INICIA NO PRIMEIRO SEGUNDO DO PRÓXIMO MINUTO HH:mm:01 |
||
| 93 | LocalDateTime agora = LocalDateTime.now(); |
||
| 94 | LocalDateTime proximoMinuto = agora |
||
| 95 | .truncatedTo(ChronoUnit.MINUTES) |
||
| 96 | .plusMinutes(1) |
||
| 97 | .plusSeconds(1); |
||
| 98 | |||
| 99 | long millisAteProximaExecucao = Duration.between(agora, proximoMinuto).toMillis(); |
||
| 100 | if (millisAteProximaExecucao > 0) { |
||
| 101 | System.out.println("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO"); |
||
| 102 | getRoboDTO().setObservacaoRobo("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO"); |
||
| 103 | Thread.sleep(millisAteProximaExecucao); |
||
| 104 | } |
||
| 105 | |||
| 106 | } catch (InterruptedException e) { |
||
| 107 | // TODO Auto-generated catch block |
||
| 108 | e.printStackTrace(); |
||
| 109 | } |
||
| 110 | */ |
||
| 111 | |||
| 112 | System.out.println("INICIANDO CAPTURA!"); |
||
| 113 | getRoboDTO().setObservacaoRobo("INICIANDO CAPTURA!"); |
||
| 114 | |||
| 761 | blopes | 115 | while (rodando.get()) { |
| 116 | try { |
||
| 117 | // Lê ticks atuais de TODOS os ativos (snapshot) |
||
| 118 | Map<String, ProfitTick> ticks = profitDataProvider.readCurrentTicks(); |
||
| 119 | |||
| 120 | // Atualiza estado de cada ativo |
||
| 121 | for (Map.Entry<String, ProfitTick> entry : ticks.entrySet()) { |
||
| 122 | String symbol = entry.getKey(); |
||
| 123 | ProfitTick tick = entry.getValue(); |
||
| 124 | processarTick(symbol, tick); |
||
| 125 | } |
||
| 126 | |||
| 127 | // Espera 1 segundo antes da próxima leitura |
||
| 764 | blopes | 128 | TimeUnit.SECONDS.sleep(1); |
| 761 | blopes | 129 | } catch (Exception e) { |
| 130 | e.printStackTrace(); |
||
| 131 | try { |
||
| 132 | TimeUnit.SECONDS.sleep(1); |
||
| 133 | } catch (InterruptedException ex) { |
||
| 134 | Thread.currentThread().interrupt(); |
||
| 135 | } |
||
| 136 | } |
||
| 137 | } |
||
| 764 | blopes | 138 | System.out.println("CAPTURA FINALIZADA!"); |
| 139 | getRoboDTO().setObservacaoRobo("CAPTURA FINALIZADA!"); |
||
| 761 | blopes | 140 | } |
| 141 | |||
| 142 | private void processarTick(String ativo, ProfitTick tick) { |
||
| 143 | CandleState situacaoCandle = estados.get(ativo); |
||
| 144 | LocalDateTime tickTime = tick.getDateTime(); |
||
| 145 | |||
| 146 | // Evita processar o mesmo tick de novo (mesma data/hora) |
||
| 147 | if (situacaoCandle != null && situacaoCandle.getLastTickTime() != null |
||
| 148 | && tickTime.equals(situacaoCandle.getLastTickTime())) { |
||
| 149 | return; |
||
| 150 | } |
||
| 151 | |||
| 152 | LocalDateTime minutoTick = tickTime.truncatedTo(ChronoUnit.MINUTES); |
||
| 153 | BigDecimal preco = tick.getPrice(); |
||
| 764 | blopes | 154 | |
| 155 | System.out.println("CAPTURANDO: " + preco + " [" + tickTime + "]"); |
||
| 761 | blopes | 156 | |
| 157 | if (situacaoCandle == null) { |
||
| 158 | // Primeiro tick desse ativo |
||
| 159 | situacaoCandle = new CandleState(minutoTick, preco, tickTime); |
||
| 160 | estados.put(ativo, situacaoCandle); |
||
| 161 | return; |
||
| 162 | } |
||
| 163 | |||
| 164 | // Se o minuto mudou → fecha candle anterior e salva no banco |
||
| 165 | if (!minutoTick.equals(situacaoCandle.getMinutoAtual())) { |
||
| 166 | LocalDateTime inicioCandle = situacaoCandle.getMinutoAtual(); |
||
| 167 | LocalDateTime fimCandle = inicioCandle.plusMinutes(1); |
||
| 168 | |||
| 169 | candleService.cadastrar(ativo, inicioCandle, fimCandle, situacaoCandle); |
||
| 170 | |||
| 171 | // Inicia o próximo candle (novo minuto) com o tick atual |
||
| 172 | situacaoCandle = new CandleState(minutoTick, preco, tickTime); |
||
| 173 | } else { |
||
| 764 | blopes | 174 | System.out.println("PREÇO: " + preco + " [" + tickTime + "]"); |
| 175 | |||
| 761 | blopes | 176 | // Ainda dentro do mesmo minuto → atualiza OHLC |
| 177 | if (preco.compareTo(situacaoCandle.getMaxima()) > 0) { |
||
| 178 | situacaoCandle.setMaxima(preco); |
||
| 179 | } |
||
| 180 | if (preco.compareTo(situacaoCandle.getMinima()) < 0) { |
||
| 181 | situacaoCandle.setMinima(preco); |
||
| 182 | } |
||
| 183 | situacaoCandle.setFechamento(preco); |
||
| 184 | situacaoCandle.setLastTickTime(tickTime); |
||
| 185 | // volume se tiver → state.volume += ... |
||
| 186 | } |
||
| 187 | } |
||
| 188 | |||
| 189 | @Override |
||
| 190 | public void pararColetor() { |
||
| 191 | rodando.set(false); |
||
| 192 | } |
||
| 193 | |||
| 194 | @Override |
||
| 195 | public Boolean isRodando() { |
||
| 196 | return rodando.get(); |
||
| 197 | } |
||
| 198 | |||
| 199 | @Override |
||
| 200 | public RoboDTO statusRobo() { |
||
| 201 | getRoboDTO().setRodando(isRodando()); |
||
| 202 | return getRoboDTO(); |
||
| 203 | } |
||
| 204 | |||
| 205 | } |