Compare commits

...

5 Commits
main ... g

Author SHA1 Message Date
b1d334a6cd bug fix 2026-05-11 09:57:39 +09:00
93907c2dac scrolling bug fix 2026-05-08 16:03:54 +09:00
acd1b13760 스케줄로 안뜨게 2026-05-08 13:52:28 +09:00
81ce68e07b 버튼 눌러서 띄우기 2026-05-08 13:43:56 +09:00
95f43e105d 판매 로그 > 손절 처리 로그 수정 2026-05-08 10:59:13 +09:00
4 changed files with 80 additions and 23 deletions

View File

@ -103,6 +103,32 @@ object LocalReportGenerator {
} }
} }
fun generateAndOpenAsyncDirectly(
summary: RawSummaryData,
rawHoldings: List<RawHoldingData>,
rawTrades: List<RawTradeData>
) {
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<RawHoldingData>, trades: List<RawTradeData>): DashboardStats { private fun calculateDashboardStats(holdings: List<RawHoldingData>, trades: List<RawTradeData>): DashboardStats {
val tradesByStock = trades.groupBy { it.stockCode } val tradesByStock = trades.groupBy { it.stockCode }

View File

@ -60,6 +60,7 @@ object TradingReportManager : TradingReportService {
override fun recordAssetSnapshot(type: SnapshotType, balance: UnifiedBalance, remark: String?) { override fun recordAssetSnapshot(type: SnapshotType, balance: UnifiedBalance, remark: String?) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
println("❌ [Report] 리포트 비동기 생성 중 오류 발생: gggg")
val todayDate = LocalDate.now().toString() val todayDate = LocalDate.now().toString()
// 1. 중복 없는 전체 종목 코드 리스트 추출 // 1. 중복 없는 전체 종목 코드 리스트 추출
@ -229,7 +230,7 @@ object TradingReportManager : TradingReportService {
} }
// 6. 코루틴 기반 제너레이터 호출 // 6. 코루틴 기반 제너레이터 호출
LocalReportGenerator.generateAndOpenAsync(summaryData, holdingLogs, tradeLogs) LocalReportGenerator.generateAndOpenAsyncDirectly(summaryData, holdingLogs, tradeLogs)
} }
} }
} }

View File

@ -538,15 +538,21 @@ object AutoTradingManager {
isBuy = false, isBuy = false,
).onSuccess { newOrderNo -> ).onSuccess { newOrderNo ->
println("✅ [보유 주식 손절 처리] 수익률($profit%) -> ${holding.valuationProfitAmount} 손해 중이며 현제 손절 가이드에 적합함 시장가 매도.") println("✅ [보유 주식 손절 처리] 수익률($profit%) -> ${holding.valuationProfitAmount} 손해 중이며 현제 손절 가이드에 적합함 시장가 매도.")
TradingLogStore.addSellLog(
holding.code,
targetPrice.toString(),
"SELL",
"☠️ 보유 주식 손절 처리 [수익률 : ${profit}%] ${holding.valuationProfitAmount} 손해 중이며 현시세{${holding.currentPrice}}로 기준 호가 위 매도[$targetPrice] 주문 완료"
)
}.onFailure { err-> }.onFailure { err->
println("✅ [보유 주식 손절 처리] 실패 ${err.message}") println("✅ [보유 주식 손절 처리] 실패 ${err.message}")
} }
TradingLogStore.addNotice( // TradingLogStore.addNotice(
"보유주식[${holding.name}]", // "보유주식[${holding.name}]",
holding.code, // holding.code,
"수익률($profit%) -> ${holding.valuationProfitAmount} 손해 중이며 현제 손절 가이드에 적합함 시장가 매도." // "수익률($profit%) -> ${holding.valuationProfitAmount} 손해 중이며 현제 손절 가이드에 적합함 시장가 매도."
) // )
} }
analyzeDeepLossHoldingsAfterMarket(holding , true) analyzeDeepLossHoldingsAfterMarket(holding , true)
} }
@ -762,14 +768,14 @@ object AutoTradingManager {
suspend fun checkBalance(isMorning: Boolean = true) { suspend fun checkBalance(isMorning: Boolean = true) {
if (isMorning) { if (isMorning) {
currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull() currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull()
currentBalance?.let { currentBalance -> // currentBalance?.let { currentBalance ->
if (LocalTime.now().isBefore(LocalTime.of(18,1))) { // if (LocalTime.now().isBefore(LocalTime.of(18,1))) {
TradingReportManager.recordAssetSnapshot( // TradingReportManager.recordAssetSnapshot(
if (LocalTime.now().isAfter(LocalTime.of(18, 0)) // if (LocalTime.now().isAfter(LocalTime.of(18, 0))
) SnapshotType.END else SnapshotType.MIDDLE, currentBalance, "" // ) SnapshotType.END else SnapshotType.MIDDLE, currentBalance, ""
) // )
} // }
} // }
if (KisSession.config.take_profit) currentBalance?.let { resumePendingSellOrders(KisTradeService, it) } if (KisSession.config.take_profit) currentBalance?.let { resumePendingSellOrders(KisTradeService, it) }
if (KisSession.tradeConfig.auto_cancel_pending_buy) { checkAndCancelPendingBuyOrders() } if (KisSession.tradeConfig.auto_cancel_pending_buy) { checkAndCancelPendingBuyOrders() }

View File

@ -50,9 +50,13 @@ import model.KisSession
import network.KisTradeService import network.KisTradeService
import network.NewsService import network.NewsService
import network.StockUniverseLoader import network.StockUniverseLoader
import report.SnapshotType
import report.TradingReportManager
import service.AutoTradingManager import service.AutoTradingManager
import service.AutoTradingManager.currentBalance
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.time.LocalTime
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
@ -105,15 +109,35 @@ fun TradingDecisionLog() {
Row(modifier = Modifier.fillMaxSize().background(Color(0xFFF2F2F2))) { Row(modifier = Modifier.fillMaxSize().background(Color(0xFFF2F2F2))) {
Column(modifier = Modifier.weight(1f).padding(8.dp).fillMaxHeight().background(Color.White)) { Column(modifier = Modifier.weight(1f).padding(8.dp).fillMaxHeight().background(Color.White)) {
Row(modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Button( Button(
onClick = { onClick = {
coroutineScope.launch { coroutineScope.launch {
// index 0으로 부드럽게 스크롤 (즉시 이동은 scrollToItem(0)) // index 0으로 부드럽게 스크롤 (즉시 이동은 scrollToItem(0))
listState.animateScrollToItem(filteredLogs.size - 1) listState.animateScrollToItem(if (filteredLogs.size - 1 >= 0) filteredLogs.size - 1 else 0)
} }
} }
) { 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( Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
horizontalArrangement = Arrangement.Start horizontalArrangement = Arrangement.Start
@ -224,9 +248,9 @@ fun TradingDecisionLog() {
Text( Text(
text = log.decision, text = log.decision,
color = when (log.decision) { color = when (log.decision) {
"BUY" -> Color.Red "BUY" -> Color(0xFF800080)
"SETTING" -> Color(0xFFFFA500) "SETTING" -> Color(0xFFFFA500)
"SELL" -> Color(0xFF800080) "SELL" -> if (log.reason.contains("손절 처리")) Color.Blue else Color.Red
"HOLD" -> Color.DarkGray "HOLD" -> Color.DarkGray
"ANALYZER" -> Color.Green "ANALYZER" -> Color.Green
"PASS" -> Color.Yellow "PASS" -> Color.Yellow