package br.com.sl.domain.service.impl;
import java.math.BigDecimal;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Singleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.itextpdf.text.log.SysoLogger;
import br.com.ec.core.generic.AbstractService;
import br.com.ec.core.generic.GenericRepository;
import br.com.ec.core.validador.Validador;
import br.com.sl.core.ExcelProfitDataProvider;
import br.com.sl.core.ExcelProfitTempoRealPorSegundoProvider;
import br.com.sl.core.ProfitDataProvider;
import br.com.sl.domain.dto.RoboDTO;
import br.com.sl.domain.dto.robo.CandleState;
import br.com.sl.domain.dto.robo.ProfitTick;
import br.com.sl.domain.service.CandleService;
import br.com.sl.domain.service.RoboColetorService;
@Singleton
@Service
public class RoboColetorServiceImpl
extends AbstractService
<RoboDTO
> implements RoboColetorService
{
private final AtomicBoolean rodando =
new AtomicBoolean(false);
private RoboDTO roboDTO =
new RoboDTO
();
private CandleService candleService
;
private ProfitDataProvider profitDataProvider
;
// estado de candle por ativo
private final Map<String, CandleState
> estados =
new ConcurrentHashMap<>();
// config básica (pode ser lido de properties)
private static final String EXCEL_PATH =
"C:/trade/temporeal.xlsx";
private static final String EXCEL_SHEET =
"Asset";
@Autowired
public RoboColetorServiceImpl
(CandleService candleService, Validador validador
) {
super(validador
);
this.
candleService = candleService
;
}
@
Override
protected GenericRepository
<RoboDTO
> getRepository
() {
return null;
}
public RoboDTO getRoboDTO
() {
return roboDTO
;
}
public void setRoboDTO
(RoboDTO roboDTO
) {
this.
roboDTO = roboDTO
;
}
@
Override
public void iniciarColetor1Minuto
() {
RoboDTO roboDTO =
new RoboDTO
();
roboDTO.
setRodando(true);
setRoboDTO
(roboDTO
);
/*
this.profitDataProvider = new ExcelProfitTempoRealPorSegundoProvider(Paths.get(EXCEL_PATH), EXCEL_SHEET);
if (!rodando.compareAndSet(false, true)) {
// já está rodando
return;
}
*/
Thread t =
new Thread(this::loopColetor,
"CandleCollector-1M");
t.
setDaemon(true);
t.
start();
}
private void loopColetor
() {
System.
out.
println("INICIANDO COLETOR DE DADOS...");
getRoboDTO
().
setObservacaoRobo("INICIANDO COLETOR DE DADOS...");
/*
try {
// INICIA NO PRIMEIRO SEGUNDO DO PRÓXIMO MINUTO HH:mm:01
LocalDateTime agora = LocalDateTime.now();
LocalDateTime proximoMinuto = agora
.truncatedTo(ChronoUnit.MINUTES)
.plusMinutes(1)
.plusSeconds(1);
long millisAteProximaExecucao = Duration.between(agora, proximoMinuto).toMillis();
if (millisAteProximaExecucao > 0) {
System.out.println("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO");
getRoboDTO().setObservacaoRobo("AGUARDANDO PARA INICIAR NO PRÓXIMO MINUTO");
Thread.sleep(millisAteProximaExecucao);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
System.
out.
println("INICIANDO CAPTURA!");
getRoboDTO
().
setObservacaoRobo("INICIANDO CAPTURA!");
while (rodando.
get()) {
try {
/*
// Lê ticks atuais de TODOS os ativos (snapshot)
Map<String, ProfitTick> ticks = profitDataProvider.readCurrentTicks();
// Atualiza estado de cada ativo
for (Map.Entry<String, ProfitTick> entry : ticks.entrySet()) {
String symbol = entry.getKey();
ProfitTick tick = entry.getValue();
processarTick(symbol, tick);
}
*/
// Espera 1 segundo antes da próxima leitura
TimeUnit.
SECONDS.
sleep(1);
} catch (Exception e
) {
e.
printStackTrace();
try {
TimeUnit.
SECONDS.
sleep(1);
} catch (InterruptedException ex
) {
Thread.
currentThread().
interrupt();
}
}
}
System.
out.
println("CAPTURA FINALIZADA!");
getRoboDTO
().
setObservacaoRobo("CAPTURA FINALIZADA!");
}
private void processarTick
(String ativo, ProfitTick tick
) {
CandleState situacaoCandle = estados.
get(ativo
);
LocalDateTime tickTime = tick.
getDateTime();
// Evita processar o mesmo tick de novo (mesma data/hora)
if (situacaoCandle
!=
null && situacaoCandle.
getLastTickTime() !=
null
&& tickTime.
equals(situacaoCandle.
getLastTickTime())) {
return;
}
LocalDateTime minutoTick = tickTime.
truncatedTo(ChronoUnit.
MINUTES);
BigDecimal preco = tick.
getPrice();
System.
out.
println("CAPTURANDO: " + preco +
" [" + tickTime +
"]");
if (situacaoCandle ==
null) {
// Primeiro tick desse ativo
situacaoCandle =
new CandleState
(minutoTick, preco, tickTime
);
estados.
put(ativo, situacaoCandle
);
return;
}
// Se o minuto mudou → fecha candle anterior e salva no banco
if (!minutoTick.
equals(situacaoCandle.
getMinutoAtual())) {
LocalDateTime inicioCandle = situacaoCandle.
getMinutoAtual();
LocalDateTime fimCandle = inicioCandle.
plusMinutes(1);
candleService.
cadastrar(ativo, inicioCandle, fimCandle, situacaoCandle
);
// Inicia o próximo candle (novo minuto) com o tick atual
situacaoCandle =
new CandleState
(minutoTick, preco, tickTime
);
} else {
System.
out.
println("PREÇO: " + preco +
" [" + tickTime +
"]");
// Ainda dentro do mesmo minuto → atualiza OHLC
if (preco.
compareTo(situacaoCandle.
getMaxima()) > 0) {
situacaoCandle.
setMaxima(preco
);
}
if (preco.
compareTo(situacaoCandle.
getMinima()) < 0) {
situacaoCandle.
setMinima(preco
);
}
situacaoCandle.
setFechamento(preco
);
situacaoCandle.
setLastTickTime(tickTime
);
// volume se tiver → state.volume += ...
}
}
@
Override
public void pararColetor
() {
rodando.
set(false);
}
@
Override
public Boolean isRodando
() {
return rodando.
get();
}
@
Override
public RoboDTO statusRobo
() {
getRoboDTO
().
setRodando(isRodando
());
return getRoboDTO
();
}
}