리포팅 테스트

This commit is contained in:
2026-04-16 15:48:23 +09:00
parent 68a7a05961
commit ed12d07bc2
9 changed files with 890 additions and 162 deletions
+48 -53
View File
@@ -5,7 +5,6 @@ import Defines.AUTOSELL
import Defines.BLACKLISTEDSTOCKCODES
import Defines.EMBEDDING_PORT
import Defines.LLM_PORT
import network.TradingDecision
import TradingLogStore
import analyzer.AdvancedTradeAssistant
import analyzer.TechnicalAnalyzer
@@ -30,6 +29,7 @@ import model.ExecutionData
import model.KisSession
import model.RankingStock
import model.RankingType
import model.TradingDecision
import model.UnifiedBalance
import model.UnifiedStockHolding
import network.DartCodeManager
@@ -38,6 +38,8 @@ import network.KisTradeService
import network.KisWebSocketManager
import network.RagService
import network.StockUniverseLoader
import report.SnapshotType
import report.TradingReportManager
import util.MarketUtil
import java.time.LocalDate
import java.time.LocalDateTime
@@ -222,6 +224,7 @@ object AutoTradingManager {
TradingLogStore.addLog(decision,"WATCH","매수 실패 : 최대 보유 종목 도달로 신규 매수 일시 중단 => 재분석 대기열에 추가")
} else {
println("basePrice : $basePrice, oneTickLowerPrice : $oneTickLowerPrice, finalPrice : $finalPrice")
KisTradeService.postOrder(stockCode, orderQty, finalPrice.toLong().toString(), isBuy = true)
.onSuccess { realOrderNo ->
// 💡 [개선 1] 첫 번째 성공 로그에 등급 이름 추가
@@ -250,6 +253,17 @@ object AutoTradingManager {
status = "PENDING_BUY",
isDomestic = true
))
TradingReportManager.recordTradeDecision(
orderNo = realOrderNo,
stockCode = stockCode,
stockName = stockName,
isBuy = true,
orderQty = inputQty,
reason = decision.reason ?: "", // AI 이유
decision = decision // AI 객체 통째로 전달
)
syncAndExecute(realOrderNo)
// 💡 [개선 3] 감시 설정 로그에도 등급 정보 노출
@@ -296,7 +310,7 @@ object AutoTradingManager {
// 3. 실제 체결가 기준 익절 가격 재계산 및 틱 사이즈 보정
val finalTargetPrice = MarketUtil.roundToTickSize(actualBuyPrice * (1 + finalProfitRate / 100.0))
TradingReportManager.updateExecution(orderNo,finalTargetPrice,dbItem.quantity)
println("🎯 [매칭 성공] 익절 주문 실행: ${dbItem.name} | 매수가: ${actualBuyPrice.toInt()} -> 목표가: ${finalTargetPrice.toInt()} (${String.format("%.2f", finalProfitRate)}% 적용)")
KisTradeService.postOrder(
@@ -306,6 +320,15 @@ object AutoTradingManager {
isBuy = false
).onSuccess { newSellOrderNo ->
// 익절가 업데이트 및 상태 변경
TradingReportManager.recordTradeDecision(
orderNo = newSellOrderNo,
stockCode = dbItem.code,
stockName = dbItem.name,
isBuy = false,
orderQty = dbItem.quantity,
reason = "🎯 [매칭 성공] 익절 주문 실행: ${dbItem.name} | 매수가: ${actualBuyPrice.toInt()} -> 목표가: ${finalTargetPrice.toInt()} (${String.format("%.2f", finalProfitRate)}% 적용)", // AI 이유
decision = null // AI 객체 통째로 전달
)
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.SELLING, newSellOrderNo)
TradingLogStore.addSellLog(dbItem.name,finalTargetPrice.toString(),"SELL","🎯 [매칭 성공] 익절 주문 실행: ${dbItem.name} | 매수가: ${actualBuyPrice.toInt()} -> 목표가: ${finalTargetPrice.toInt()} (${String.format("%.2f", finalProfitRate)}% 적용)")
executionCache.remove(orderNo)
@@ -316,6 +339,8 @@ object AutoTradingManager {
} else if (dbItem.status == TradeStatus.SELLING) {
println("🎊 [매칭 성공] 매도 완료 처리: ${dbItem.name}")
myOredsAndBalanceCodes.remove(dbItem.code)
TradingReportManager.updateExecution(orderNo,execData.price.toDouble(),execData.qty.toInt())
TradingLogStore.addSellLog(dbItem.name,execData.price,"SELL","🎊 [매칭 성공] 매도 완료 처리")
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.COMPLETED)
executionCache.remove(orderNo)
@@ -350,7 +375,7 @@ object AutoTradingManager {
}
suspend fun sellingAfterMarketOnePrice(tradeService: KisTradeService,balance : UnifiedBalance,marketCode : String = "Y") {
balance.holdings.forEach { holding ->
balance.getHolldings().forEach { holding ->
if (BLACKLISTEDSTOCKCODES.contains(holding.code)){
println("❌ 차단 처리된 주식 : ${holding.name}")
TradingLogStore.addAnalyzer(
@@ -389,6 +414,19 @@ object AutoTradingManager {
"SELL",
"🎊 ${if(marketCode.equals("Y"))"시간외 단일가" else "대체거래소"} 주식 재고털이 주문 완료"
)
DatabaseFactory.saveAutoTrade(AutoTradeItem(
orderNo = newOrderNo,
code = holding.code,
name = holding.name,
quantity = holding.quantity.toInt(),
profitRate = 0.0,
stopLossRate = 0.0,
targetPrice = targetPrice.toDouble(),
stopLossPrice = 0.0,
status = "SELLING",
isDomestic = true
))
syncAndExecute(newOrderNo)
}.onFailure {
TradingLogStore.addSellLog(
"${holding.name}[${holding.code}]",
@@ -415,7 +453,7 @@ object AutoTradingManager {
println("resumePendingSellOrders")
balance.holdings.forEach { holding ->
balance.getHolldings().forEach { holding ->
if (BLACKLISTEDSTOCKCODES.contains(holding.code)){
println("❌ 차단 처리된 주식 : ${holding.name}")
TradingLogStore.addAnalyzer(
@@ -678,6 +716,10 @@ object AutoTradingManager {
suspend fun checkBalance(isMorning: Boolean = true) {
if (isMorning) {
currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull()
currentBalance?.let { currentBalance ->
TradingReportManager.recordAssetSnapshot(if (LocalTime.now().isAfter(LocalTime.of(17,58))) SnapshotType.END else SnapshotType.MIDDLE ,currentBalance,"")
}
if (AUTOSELL) currentBalance?.let { resumePendingSellOrders(KisTradeService, it) }
} else {
}
@@ -687,7 +729,7 @@ object AutoTradingManager {
myOredsAndBalanceCodes.clear()
checkBalance()
val myCash = currentBalance?.deposit?.replace(",", "")?.toLongOrNull() ?: 0L
val myHoldings = currentBalance?.holdings?.filter { it.quantity.toInt() > 0 }?.map {
val myHoldings = currentBalance?.getHolldings()?.map {
myOredsAndBalanceCodes.add(it.code)
it.code }?.toSet() ?: emptySet()
val pendingStocks = DatabaseFactory.findAllMonitoringTrades().map {
@@ -790,53 +832,6 @@ object AutoTradingManager {
}
}
suspend fun finalizeMarketClose(now: LocalTime) {
when {
(AutoTradingManager.now.hour == 0 && AutoTradingManager.now.minute == 0 && (isSystemReadyToday || isSystemCleanedUpToday)) -> {
waitTime = 10.0
isSystemReadyToday = false
isSystemCleanedUpToday = false
}
(AutoTradingManager.now.isAfter(LocalTime.of(8, 0)) && !isSystemReadyToday) -> {
waitTime = 3.0
if (!KisSession.isMarketTokenValid() || !KisSession.isTradeTokenValid()) {
KisWebSocketManager.disconnect()
tryRefreshToken()
}
}
(AutoTradingManager.now.isAfter(LocalTime.of(18, 20))) -> {
try {
waitTime = 5.0
println("current SystemCleanedUpToday is $isSystemCleanedUpToday")
if (!isSystemCleanedUpToday) {
println("🌙 [System] 업무 종료 및 자원 정리 시작...")
SystemSleepPreventer.sleepDisplay() // 모니터 끄기
KisWebSocketManager.disconnect()
BrowserManager.closeIfIdle(0) // 즉시 닫기
if (LlamaServerManager.stopAll()) {
isSystemCleanedUpToday = true
}
}
println("✅ [System] 오늘의 모든 정리가 완료되었습니다.")
} catch (e: Exception) {
}
}
(AutoTradingManager.now.isAfter(LocalTime.of(18, 15)) && AutoTradingManager.now.minute % 15 == 0) -> {
try {
waitTime = 5.0
SystemSleepPreventer.sleepDisplay() // 모니터 끄기
} catch (e: Exception) {
}
}
else -> {
waitTime = 5.0
}
}
}
fun addToReanalysis(stock: RankingStock) {
val count = retryCountMap.getOrDefault(stock.code, 0)
@@ -953,7 +948,7 @@ object AutoTradingManager {
private suspend fun executeClosingLiquidation(tradeService: KisTradeService) {
val activeTrades = DatabaseFactory.findAllMonitoringTrades()
val balanceResult = tradeService.fetchIntegratedBalance().getOrNull()
val realHoldings = balanceResult?.holdings?.associateBy { it.code } ?: emptyMap()
val realHoldings = balanceResult?.getHolldings()?.associateBy { it.code } ?: emptyMap()
activeTrades.forEach { trade ->
try {