Rev 764 | 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 | |||
| 783 | blopes | 75 | /* |
| 764 | blopes | 76 | this.profitDataProvider = new ExcelProfitTempoRealPorSegundoProvider(Paths.get(EXCEL_PATH), EXCEL_SHEET); |
| 761 | blopes | 77 | if (!rodando.compareAndSet(false, true)) { |
| 78 | // já está rodando |
||
| 79 | return; |
||
| 80 | } |
||
| 783 | blopes | 81 | */ |
| 761 | blopes | 82 | |
| 83 | Thread t = new Thread(this::loopColetor, "CandleCollector-1M"); |
||
| 84 | t.setDaemon(true); |
||
| 85 | t.start(); |
||
| 86 | } |
||
| 87 | |||
| 88 | private void loopColetor() { |
||
| 764 | blopes | 89 | System.out.println("INICIANDO COLETOR DE DADOS..."); |
| 90 | getRoboDTO().setObservacaoRobo("INICIANDO COLETOR DE DADOS..."); |
||
| 91 | |||
| 92 | /* |
||
| 93 | try { |
||
| 94 | // INICIA NO PRIMEIRO SEGUNDO DO PRÓXIMO MINUTO HH:mm:01 |
||
| 95 | LocalDateTime agora = LocalDateTime.now(); |
||
| 96 | LocalDateTime proximoMinuto = agora |
||
| 97 | .truncatedTo(ChronoUnit.MINUTES) |
||
| 98 | .plusMinutes(1) |
||
| 99 | .plusSeconds(1); |
||
| 100 | |||
| 101 | long millisAteProximaExecucao = Duration.between(agora, proximoMinuto).toMillis(); |
||
| 102 | if (millisAteProximaExecucao > 0) { |
||
| 103 | System.out.println("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO"); |
||
| 104 | getRoboDTO().setObservacaoRobo("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO"); |
||
| 105 | Thread.sleep(millisAteProximaExecucao); |
||
| 106 | } |
||
| 107 | |||
| 108 | } catch (InterruptedException e) { |
||
| 109 | // TODO Auto-generated catch block |
||
| 110 | e.printStackTrace(); |
||
| 111 | } |
||
| 112 | */ |
||
| 113 | |||
| 114 | System.out.println("INICIANDO CAPTURA!"); |
||
| 115 | getRoboDTO().setObservacaoRobo("INICIANDO CAPTURA!"); |
||
| 116 | |||
| 761 | blopes | 117 | while (rodando.get()) { |
| 118 | try { |
||
| 783 | blopes | 119 | /* |
| 761 | blopes | 120 | // Lê ticks atuais de TODOS os ativos (snapshot) |
| 121 | Map<String, ProfitTick> ticks = profitDataProvider.readCurrentTicks(); |
||
| 122 | |||
| 123 | // Atualiza estado de cada ativo |
||
| 124 | for (Map.Entry<String, ProfitTick> entry : ticks.entrySet()) { |
||
| 125 | String symbol = entry.getKey(); |
||
| 126 | ProfitTick tick = entry.getValue(); |
||
| 127 | processarTick(symbol, tick); |
||
| 128 | } |
||
| 783 | blopes | 129 | */ |
| 761 | blopes | 130 | // Espera 1 segundo antes da próxima leitura |
| 764 | blopes | 131 | TimeUnit.SECONDS.sleep(1); |
| 761 | blopes | 132 | } catch (Exception e) { |
| 133 | e.printStackTrace(); |
||
| 134 | try { |
||
| 135 | TimeUnit.SECONDS.sleep(1); |
||
| 136 | } catch (InterruptedException ex) { |
||
| 137 | Thread.currentThread().interrupt(); |
||
| 138 | } |
||
| 139 | } |
||
| 140 | } |
||
| 764 | blopes | 141 | System.out.println("CAPTURA FINALIZADA!"); |
| 142 | getRoboDTO().setObservacaoRobo("CAPTURA FINALIZADA!"); |
||
| 761 | blopes | 143 | } |
| 144 | |||
| 145 | private void processarTick(String ativo, ProfitTick tick) { |
||
| 146 | CandleState situacaoCandle = estados.get(ativo); |
||
| 147 | LocalDateTime tickTime = tick.getDateTime(); |
||
| 148 | |||
| 149 | // Evita processar o mesmo tick de novo (mesma data/hora) |
||
| 150 | if (situacaoCandle != null && situacaoCandle.getLastTickTime() != null |
||
| 151 | && tickTime.equals(situacaoCandle.getLastTickTime())) { |
||
| 152 | return; |
||
| 153 | } |
||
| 154 | |||
| 155 | LocalDateTime minutoTick = tickTime.truncatedTo(ChronoUnit.MINUTES); |
||
| 156 | BigDecimal preco = tick.getPrice(); |
||
| 764 | blopes | 157 | |
| 158 | System.out.println("CAPTURANDO: " + preco + " [" + tickTime + "]"); |
||
| 761 | blopes | 159 | |
| 160 | if (situacaoCandle == null) { |
||
| 161 | // Primeiro tick desse ativo |
||
| 162 | situacaoCandle = new CandleState(minutoTick, preco, tickTime); |
||
| 163 | estados.put(ativo, situacaoCandle); |
||
| 164 | return; |
||
| 165 | } |
||
| 166 | |||
| 167 | // Se o minuto mudou → fecha candle anterior e salva no banco |
||
| 168 | if (!minutoTick.equals(situacaoCandle.getMinutoAtual())) { |
||
| 169 | LocalDateTime inicioCandle = situacaoCandle.getMinutoAtual(); |
||
| 170 | LocalDateTime fimCandle = inicioCandle.plusMinutes(1); |
||
| 171 | |||
| 172 | candleService.cadastrar(ativo, inicioCandle, fimCandle, situacaoCandle); |
||
| 173 | |||
| 174 | // Inicia o próximo candle (novo minuto) com o tick atual |
||
| 175 | situacaoCandle = new CandleState(minutoTick, preco, tickTime); |
||
| 176 | } else { |
||
| 764 | blopes | 177 | System.out.println("PREÇO: " + preco + " [" + tickTime + "]"); |
| 178 | |||
| 761 | blopes | 179 | // Ainda dentro do mesmo minuto → atualiza OHLC |
| 180 | if (preco.compareTo(situacaoCandle.getMaxima()) > 0) { |
||
| 181 | situacaoCandle.setMaxima(preco); |
||
| 182 | } |
||
| 183 | if (preco.compareTo(situacaoCandle.getMinima()) < 0) { |
||
| 184 | situacaoCandle.setMinima(preco); |
||
| 185 | } |
||
| 186 | situacaoCandle.setFechamento(preco); |
||
| 187 | situacaoCandle.setLastTickTime(tickTime); |
||
| 188 | // volume se tiver → state.volume += ... |
||
| 189 | } |
||
| 190 | } |
||
| 191 | |||
| 192 | @Override |
||
| 193 | public void pararColetor() { |
||
| 194 | rodando.set(false); |
||
| 195 | } |
||
| 196 | |||
| 197 | @Override |
||
| 198 | public Boolean isRodando() { |
||
| 199 | return rodando.get(); |
||
| 200 | } |
||
| 201 | |||
| 202 | @Override |
||
| 203 | public RoboDTO statusRobo() { |
||
| 204 | getRoboDTO().setRodando(isRodando()); |
||
| 205 | return getRoboDTO(); |
||
| 206 | } |
||
| 207 | |||
| 208 | } |