From 81ce68e07b4e5860c7541837d39df097c62563d8 Mon Sep 17 00:00:00 2001 From: JUNGGWAN KIM Date: Fri, 8 May 2026 13:43:56 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=88=8C=EB=9F=AC?= =?UTF-8?q?=EC=84=9C=20=EB=9D=84=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/report/LocalReportGenerator.kt | 26 ++++++++++++ .../kotlin/report/TradingReportManager.kt | 3 +- src/main/kotlin/ui/TradingDecisionLog.kt | 40 +++++++++++++++---- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/report/LocalReportGenerator.kt b/src/main/kotlin/report/LocalReportGenerator.kt index b04d7e2..9000f93 100644 --- a/src/main/kotlin/report/LocalReportGenerator.kt +++ b/src/main/kotlin/report/LocalReportGenerator.kt @@ -103,6 +103,32 @@ object LocalReportGenerator { } } + fun generateAndOpenAsyncDirectly( + summary: RawSummaryData, + rawHoldings: List, + rawTrades: List + ) { + reportScope.launch { + try { + // 1. [핵심] 대시보드 통계 지표 추출 (Generator가 직접 계산) + val stats = calculateDashboardStats(rawHoldings, rawTrades) + + // 2. 탭 2 & 3 HTML 가공 + val holdingsHtml = processHoldings(rawHoldings) + val tradesHtml = processTrades(rawTrades) + + // 3. 전체 HTML 조립 + val htmlContent = buildHtml(summary, stats, holdingsHtml, tradesHtml) + if (summary.type.equals("END", true) || summary.type.equals("MIDDLE", true)) { + saveAndOpen(summary.type, htmlContent) + } + } catch (e: Exception) { + println("❌ [Report] 리포트 비동기 생성 중 오류 발생: ${e.message}") + e.printStackTrace() + } + } + } + // --- [새로운 통계 계산 로직] --- private fun calculateDashboardStats(holdings: List, trades: List): DashboardStats { val tradesByStock = trades.groupBy { it.stockCode } diff --git a/src/main/kotlin/report/TradingReportManager.kt b/src/main/kotlin/report/TradingReportManager.kt index 7d87826..66f6ac3 100644 --- a/src/main/kotlin/report/TradingReportManager.kt +++ b/src/main/kotlin/report/TradingReportManager.kt @@ -60,6 +60,7 @@ object TradingReportManager : TradingReportService { override fun recordAssetSnapshot(type: SnapshotType, balance: UnifiedBalance, remark: String?) { CoroutineScope(Dispatchers.IO).launch { + println("❌ [Report] 리포트 비동기 생성 중 오류 발생: gggg") val todayDate = LocalDate.now().toString() // 1. 중복 없는 전체 종목 코드 리스트 추출 @@ -229,7 +230,7 @@ object TradingReportManager : TradingReportService { } // 6. 코루틴 기반 제너레이터 호출 - LocalReportGenerator.generateAndOpenAsync(summaryData, holdingLogs, tradeLogs) + LocalReportGenerator.generateAndOpenAsyncDirectly(summaryData, holdingLogs, tradeLogs) } } } diff --git a/src/main/kotlin/ui/TradingDecisionLog.kt b/src/main/kotlin/ui/TradingDecisionLog.kt index e9d128d..1c58c01 100644 --- a/src/main/kotlin/ui/TradingDecisionLog.kt +++ b/src/main/kotlin/ui/TradingDecisionLog.kt @@ -50,9 +50,13 @@ import model.KisSession import network.KisTradeService import network.NewsService import network.StockUniverseLoader +import report.SnapshotType +import report.TradingReportManager import service.AutoTradingManager +import service.AutoTradingManager.currentBalance import java.io.File import java.net.URI +import java.time.LocalTime @OptIn(ExperimentalMaterialApi::class) @Composable @@ -105,14 +109,34 @@ fun TradingDecisionLog() { Row(modifier = Modifier.fillMaxSize().background(Color(0xFFF2F2F2))) { Column(modifier = Modifier.weight(1f).padding(8.dp).fillMaxHeight().background(Color.White)) { - Button( - onClick = { - coroutineScope.launch { - // index 0으로 부드럽게 스크롤 (즉시 이동은 scrollToItem(0)) - listState.animateScrollToItem(filteredLogs.size - 1) + Row(modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Button( + onClick = { + coroutineScope.launch { + // index 0으로 부드럽게 스크롤 (즉시 이동은 scrollToItem(0)) + listState.animateScrollToItem(filteredLogs.size - 1) + } } - } - ) { Text("AI 자동매매 실시간 로그", style = MaterialTheme.typography.h6) } + ) { Text("AI 자동매매 실시간 로그", style = MaterialTheme.typography.h6) } + + Button( + onClick = { + coroutineScope.launch { + currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull() + currentBalance?.let { currentBalance -> + if (LocalTime.now().isBefore(LocalTime.of(18,1))) { + TradingReportManager.recordAssetSnapshot( + if (LocalTime.now().isAfter(LocalTime.of(18, 0)) + ) SnapshotType.END else SnapshotType.MIDDLE, currentBalance, "" + ) + } + } + } + } + ) { Text("Open the report", style = MaterialTheme.typography.body2) } + } Row( modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp), @@ -219,7 +243,7 @@ fun TradingDecisionLog() { elevation = 2.dp ) { Column(modifier = Modifier.padding(12.dp)) { - Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = M odifier.fillMaxWidth()) { + Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text("${log.time} - ${log.stockName}", fontWeight = FontWeight.Bold) Text( text = log.decision,