모의투자 관리 기능 추가
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package com.goldenchart.service;
|
||||
|
||||
import com.goldenchart.dto.PaperDailySnapshotDto;
|
||||
import com.goldenchart.entity.GcPaperAccount;
|
||||
import com.goldenchart.entity.GcPaperDailySnapshot;
|
||||
import com.goldenchart.entity.GcPaperPosition;
|
||||
import com.goldenchart.repository.GcPaperAccountRepository;
|
||||
import com.goldenchart.repository.GcPaperDailySnapshotRepository;
|
||||
import com.goldenchart.repository.GcPaperPositionRepository;
|
||||
import com.goldenchart.storage.Ta4jStorage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.ta4j.core.Bar;
|
||||
import org.ta4j.core.BarSeries;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Transactional
|
||||
public class PaperSnapshotService {
|
||||
|
||||
private static final ZoneId KST = ZoneId.of("Asia/Seoul");
|
||||
|
||||
private final GcPaperDailySnapshotRepository snapshotRepo;
|
||||
private final GcPaperAccountRepository accountRepo;
|
||||
private final GcPaperPositionRepository positionRepo;
|
||||
private final Ta4jStorage ta4jStorage;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<PaperDailySnapshotDto> list(Long accountId, LocalDate from, LocalDate to) {
|
||||
LocalDate f = from != null ? from : LocalDate.now(KST).minusDays(90);
|
||||
LocalDate t = to != null ? to : LocalDate.now(KST);
|
||||
return snapshotRepo.findByAccountIdAndSnapshotDateBetweenOrderBySnapshotDateAsc(accountId, f, t)
|
||||
.stream().map(this::toDto).toList();
|
||||
}
|
||||
|
||||
public void captureEodForAllAccounts() {
|
||||
LocalDate today = LocalDate.now(KST);
|
||||
for (GcPaperAccount account : accountRepo.findAll()) {
|
||||
try {
|
||||
captureEod(account, today);
|
||||
} catch (Exception e) {
|
||||
log.warn("[PaperSnapshot] account {} failed: {}", account.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void captureEod(GcPaperAccount account, LocalDate date) {
|
||||
List<GcPaperPosition> positions = positionRepo.findByAccountIdOrderBySymbolAsc(account.getId());
|
||||
Map<String, Double> marks = new HashMap<>();
|
||||
double stockEval = 0;
|
||||
for (GcPaperPosition p : positions) {
|
||||
double qty = p.getQuantity().doubleValue();
|
||||
if (qty <= 0) continue;
|
||||
double mark = resolveMark(p.getSymbol(), p.getAvgPrice().doubleValue());
|
||||
marks.put(p.getSymbol(), mark);
|
||||
stockEval += mark * qty;
|
||||
}
|
||||
double cash = account.getCashBalance().doubleValue();
|
||||
double total = cash + stockEval;
|
||||
double initial = account.getInitialCapitalSnapshot() != null
|
||||
? account.getInitialCapitalSnapshot().doubleValue() : cash;
|
||||
double cumRet = initial > 0 ? (total - initial) / initial * 100 : 0;
|
||||
|
||||
GcPaperDailySnapshot prev = snapshotRepo
|
||||
.findByAccountIdAndSnapshotDate(account.getId(), date.minusDays(1))
|
||||
.orElse(null);
|
||||
double prevTotal = prev != null ? prev.getTotalAsset().doubleValue() : initial;
|
||||
double dailyPnl = total - prevTotal;
|
||||
double dailyRet = prevTotal > 0 ? dailyPnl / prevTotal * 100 : 0;
|
||||
|
||||
GcPaperDailySnapshot snap = snapshotRepo.findByAccountIdAndSnapshotDate(account.getId(), date)
|
||||
.orElse(GcPaperDailySnapshot.builder()
|
||||
.accountId(account.getId())
|
||||
.snapshotDate(date)
|
||||
.build());
|
||||
snap.setCashBalance(BigDecimal.valueOf(cash).setScale(2, RoundingMode.HALF_UP));
|
||||
snap.setStockEvalAmount(BigDecimal.valueOf(stockEval).setScale(2, RoundingMode.HALF_UP));
|
||||
snap.setTotalAsset(BigDecimal.valueOf(total).setScale(2, RoundingMode.HALF_UP));
|
||||
snap.setDailyPnl(BigDecimal.valueOf(dailyPnl).setScale(2, RoundingMode.HALF_UP));
|
||||
snap.setDailyReturnPct(BigDecimal.valueOf(dailyRet).setScale(4, RoundingMode.HALF_UP));
|
||||
snap.setCumulativeReturnPct(BigDecimal.valueOf(cumRet).setScale(4, RoundingMode.HALF_UP));
|
||||
snapshotRepo.save(snap);
|
||||
}
|
||||
|
||||
private double resolveMark(String symbol, double fallback) {
|
||||
try {
|
||||
if (!ta4jStorage.exists(symbol, "1m")) return fallback;
|
||||
BarSeries series = ta4jStorage.getOrCreate(symbol, "1m");
|
||||
if (!series.isEmpty()) {
|
||||
Bar last = series.getLastBar();
|
||||
return last.getClosePrice().doubleValue();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private PaperDailySnapshotDto toDto(GcPaperDailySnapshot s) {
|
||||
return PaperDailySnapshotDto.builder()
|
||||
.snapshotDate(s.getSnapshotDate().toString())
|
||||
.cashBalance(s.getCashBalance().doubleValue())
|
||||
.stockEvalAmount(s.getStockEvalAmount().doubleValue())
|
||||
.totalAsset(s.getTotalAsset().doubleValue())
|
||||
.dailyPnl(s.getDailyPnl().doubleValue())
|
||||
.dailyReturnPct(s.getDailyReturnPct().doubleValue())
|
||||
.cumulativeReturnPct(s.getCumulativeReturnPct().doubleValue())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user