투자관리 화면 수정

This commit is contained in:
Macbook
2026-05-31 17:34:26 +09:00
parent d6c2f3e451
commit 4f2d98d4ba
20 changed files with 696 additions and 56 deletions
@@ -290,41 +290,85 @@ public class PaperTradingService {
.filter(m -> m != null && !m.isBlank())
.findFirst()
.orElse("LONG_ONLY");
if ("LONG_ONLY".equals(positionMode)) {
GcPaperPosition posNow = positionRepo.findByAccountIdAndSymbol(account.getId(), market)
.orElse(null);
double held = posNow != null ? posNow.getQuantity().doubleValue() : 0;
BigDecimal defaultMax = account.getInitialCapitalSnapshot() != null
? account.getInitialCapitalSnapshot()
: (app.getPaperInitialCapital() != null
? app.getPaperInitialCapital() : BigDecimal.valueOf(10_000_000));
GcPaperSymbolAllocation alloc = allocationService.getOrDefaultEntity(
account.getId(), market, defaultMax);
boolean staged = PaperTradeStageService.usesStagedTrading(alloc);
GcPaperPosition posNow = positionRepo.findByAccountIdAndSymbol(account.getId(), market)
.orElse(null);
double held = posNow != null ? posNow.getQuantity().doubleValue() : 0;
if ("LONG_ONLY".equals(positionMode) && !staged) {
if ("BUY".equals(signalType) && held > 0) return;
if ("SELL".equals(signalType) && held <= 0) return;
}
double feeRate = pct(app.getPaperFeeRatePct(), 0.05);
double slip = pct(app.getPaperSlippagePct(), 0);
double minOrder = app.getPaperMinOrderKrw() != null
? app.getPaperMinOrderKrw().doubleValue() : 5000;
if ("BUY".equals(signalType)) {
double budgetPct = pct(app.getPaperAutoTradeBudgetPct(), 95);
double cash = account.getCashBalance().doubleValue();
double reserved = account.getReservedCash() != null
? account.getReservedCash().doubleValue() : 0;
double budget = (cash - reserved) * budgetPct / 100.0;
double execPrice = price * (1 + slip / 100.0);
double denom = execPrice * (1 + feeRate / 100.0);
if (denom <= 0 || budget < minOrder) return;
double qty = budget / denom;
if (qty * execPrice < minOrder) return;
executeTrade(uid, app, market, "BUY", "market", "STRATEGY",
strategyId, price, qty, null, true);
log.info("[Paper] STRATEGY BUY {} qty≈{} @ {}", market, qty, price);
if (staged) {
if (held <= 0) {
allocationService.resetTradeStages(account.getId(), market);
alloc = allocationService.getOrDefaultEntity(account.getId(), market, defaultMax);
} else if ("LONG_ONLY".equals(positionMode)
&& alloc.getBuyStageDone() != null && alloc.getBuyStageDone() >= 3) {
return;
}
double stageKrw = PaperTradeStageService.nextBuyStageKrw(alloc);
if (stageKrw <= 0) return;
if (stageKrw < minOrder) return;
double execPrice = price * (1 + slip / 100.0);
double denom = execPrice * (1 + feeRate / 100.0);
if (denom <= 0) return;
double qty = stageKrw / denom;
if (qty * execPrice < minOrder) return;
executeTrade(uid, app, market, "BUY", "market", "STRATEGY",
strategyId, price, qty, null, true);
allocationService.advanceBuyStageAfterTrade(account.getId(), market);
log.info("[Paper] STRATEGY BUY (staged) {} qty≈{} @ {}", market, qty, price);
} else {
double budgetPct = pct(app.getPaperAutoTradeBudgetPct(), 95);
double cash = account.getCashBalance().doubleValue();
double reserved = account.getReservedCash() != null
? account.getReservedCash().doubleValue() : 0;
double budget = (cash - reserved) * budgetPct / 100.0;
double execPrice = price * (1 + slip / 100.0);
double denom = execPrice * (1 + feeRate / 100.0);
if (denom <= 0 || budget < minOrder) return;
double qty = budget / denom;
if (qty * execPrice < minOrder) return;
executeTrade(uid, app, market, "BUY", "market", "STRATEGY",
strategyId, price, qty, null, true);
log.info("[Paper] STRATEGY BUY {} qty≈{} @ {}", market, qty, price);
}
} else {
GcPaperPosition pos = positionRepo.findByAccountIdAndSymbol(account.getId(), market)
.orElse(null);
if (pos == null || pos.getQuantity().doubleValue() <= 0) return;
double avail = availableSellQty(account.getId(), market, pos.getQuantity().doubleValue());
if (held <= 0) return;
double avail = availableSellQty(account.getId(), market, held);
if (avail <= 0) return;
executeTrade(uid, app, market, "SELL", "market", "STRATEGY",
strategyId, price, avail, null, true);
log.info("[Paper] STRATEGY SELL {} qty={} @ {}", market, avail, price);
if (staged) {
double stageKrw = PaperTradeStageService.nextSellStageKrw(alloc);
if (stageKrw <= 0) return;
if (stageKrw < minOrder) return;
double execPrice = price * (1 - slip / 100.0);
double qty = PaperTradeStageService.sellQtyForStageKrw(stageKrw, avail, execPrice);
if (qty * execPrice < minOrder) return;
executeTrade(uid, app, market, "SELL", "market", "STRATEGY",
strategyId, price, qty, null, true);
allocationService.advanceSellStageAfterTrade(account.getId(), market);
log.info("[Paper] STRATEGY SELL (staged) {} qty={} @ {}", market, qty, price);
} else {
executeTrade(uid, app, market, "SELL", "market", "STRATEGY",
strategyId, price, avail, null, true);
log.info("[Paper] STRATEGY SELL {} qty={} @ {}", market, avail, price);
}
}
} catch (Exception e) {
log.warn("[Paper] auto trade failed {} {}: {}", market, signalType, e.getMessage());
@@ -591,6 +635,9 @@ public class PaperTradingService {
.positionQtyAfter(qtyAfter)
.build());
ledgerService.recordSellSettle(account, trade, netProceeds);
if ("STRATEGY".equals(source) && qtyAfter.compareTo(BigDecimal.valueOf(0.00000001)) <= 0) {
allocationService.resetTradeStages(account.getId(), market);
}
return toTradeDto(trade);
}