From 7fa1c3635522ec6b11599283654be34e3551905e Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Fri, 13 Mar 2026 17:34:48 +0900 Subject: [PATCH] ... --- src/main/kotlin/Main.kt | 2 ++ src/main/kotlin/database/DatabaseFactory.kt | 31 ++++++++++++++++++- src/main/kotlin/service/AutoTradingManager.kt | 18 ++++++++--- src/main/kotlin/ui/TradingDecisionLog.kt | 27 ++++++++++++---- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 67a26ce..7affab1 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -37,6 +37,7 @@ import model.AppConfig import model.KisSession import network.DartCodeManager import network.KisTradeService +import network.KisWebSocketManager import service.LlamaServerManager import network.NewsService import org.jetbrains.exposed.sql.selectAll @@ -147,6 +148,7 @@ fun main() = application { AutoTradingManager.isSystemCleanedUpToday = false CoroutineScope(Dispatchers.Default).launch { AutoTradingManager.startAutoDiscoveryLoop() + KisWebSocketManager.onExecutionReceived = AutoTradingManager.onExecutionReceived } if (config.modelPath.isNotEmpty()) { diff --git a/src/main/kotlin/database/DatabaseFactory.kt b/src/main/kotlin/database/DatabaseFactory.kt index cf5c152..8e88d24 100644 --- a/src/main/kotlin/database/DatabaseFactory.kt +++ b/src/main/kotlin/database/DatabaseFactory.kt @@ -1,4 +1,3 @@ -import AutoTradeTable.orderNo import androidx.compose.runtime.mutableStateListOf import kotlinx.serialization.Serializable import model.AppConfig @@ -399,6 +398,21 @@ object TradingLogStore { } } + fun addSellLog(stockName: String,sellPrice : String , decision: String, log: String) { + synchronized(this) { + if (decisionLogs.size > 1000) decisionLogs.removeAt(0) + decisionLogs.add( + LogEntry( + time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")), + stockName = "${stockName}[${sellPrice}][]", + decision = decision, + confidence = 100.0, + reason = log + ) + ) + } + } + fun addLog(tradingDecision: TradingDecision , decision: String, log: String) { synchronized(this) { if (decisionLogs.size > 1000) decisionLogs.removeAt(0) @@ -414,5 +428,20 @@ object TradingLogStore { } } + fun addSettingLog(settingDesc : String, old : String, new : String, log: String) { + synchronized(this) { + if (decisionLogs.size > 1000) decisionLogs.removeAt(0) + decisionLogs.add( + LogEntry( + time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")), + stockName = "설정변경[${settingDesc}][$old]->[$new]", + decision = "SETTING", + confidence = 100.0, + reason = log + ) + ) + } + } + } diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index c259058..d4e83c8 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -2,6 +2,7 @@ package service import AutoTradeItem import TradingDecision +import TradingLogStore import androidx.compose.runtime.remember import getLlamaBinPath import kotlinx.coroutines.CoroutineScope @@ -112,7 +113,7 @@ object AutoTradingManager { 1 } // 5. 매수 실행 (계산된 finalMargin 전달) - AutoTradingManager.excuteTrade( + excuteTrade( decision = completeTradingDecision, orderQty = min(calculatedQty, maxQty).toString(), profitRate1 = finalMargin, @@ -120,7 +121,7 @@ object AutoTradingManager { ) } else if(totalScore >= (minScore * 0.85) && completeTradingDecision.confidence + append >= (MIN_CONFIDENCE * 0.85)) { - AutoTradingManager.addToReanalysis(RankingStock(mksc_shrn_iscd = completeTradingDecision.stockCode,hts_kor_isnm = completeTradingDecision.stockName)) + addToReanalysis(RankingStock(mksc_shrn_iscd = completeTradingDecision.stockCode,hts_kor_isnm = completeTradingDecision.stockName)) TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어 또는 신뢰도 미달 이나 약간의 오차로 재분석 대기열에 추가") } else { TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어(${String.format("%.1f[${minScore}]", totalScore)}) 또는 신뢰도 (${String.format("%.1f[${MIN_CONFIDENCE}]", completeTradingDecision.confidence)}) 미달") @@ -242,7 +243,13 @@ object AutoTradingManager { } } } - + var onExecutionReceived : ((String, String, String, String, Boolean) -> Unit)? = {code, qty, price,orderNo, isBuy -> + scope.launch { + val exec = ExecutionData(orderNo, code, price, qty, isBuy) + executionCache[orderNo] = exec + syncAndExecute(orderNo) + } + } val executionCache = mutableMapOf() val processingIds = mutableSetOf() // 주문번호 기준 잠금 suspend fun syncAndExecute(orderNo: String) { @@ -278,14 +285,15 @@ object AutoTradingManager { ).onSuccess { newSellOrderNo -> // 익절가 업데이트 및 상태 변경 DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.SELLING, newSellOrderNo) - // (선택 사항) 실제 계산된 익절가를 DB에 기록하고 싶다면 별도 update 로직 추가 가능 - + TradingLogStore.addSellLog(dbItem.name,finalTargetPrice.toString(),"SELL","🎯 [매칭 성공] 익절 주문 실행: ${dbItem.name} | 매수가: ${actualBuyPrice.toInt()} -> 목표가: ${finalTargetPrice.toInt()} (${String.format("%.2f", finalProfitRate)}% 적용)") executionCache.remove(orderNo) }.onFailure { println("❌ 익절 주문 실패: ${it.message}") + TradingLogStore.addSellLog(dbItem.name,finalTargetPrice.toString(),"SELL","❌ 익절 주문 실패: ${it.message}") } } else if (dbItem.status == TradeStatus.SELLING) { println("🎊 [매칭 성공] 매도 완료 처리: ${dbItem.name}") + TradingLogStore.addSellLog(dbItem.name,execData.price,"SELL","🎊 [매칭 성공] 매도 완료 처리") DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.COMPLETED) executionCache.remove(orderNo) } diff --git a/src/main/kotlin/ui/TradingDecisionLog.kt b/src/main/kotlin/ui/TradingDecisionLog.kt index 8f442b5..7d0964e 100644 --- a/src/main/kotlin/ui/TradingDecisionLog.kt +++ b/src/main/kotlin/ui/TradingDecisionLog.kt @@ -1,8 +1,6 @@ package ui -import AutoTradeItem -import TradingDecision import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -14,7 +12,6 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color @@ -25,7 +22,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import model.ConfigIndex import model.KisSession -import service.TechnicalAnalyzer @Composable fun TradingDecisionLog() { @@ -45,7 +41,13 @@ fun TradingDecisionLog() { Text("${log.time} - ${log.stockName}", fontWeight = FontWeight.Bold) Text( text = log.decision, - color = if (log.decision == "BUY") Color.Red else Color.Gray, + color = when (log.decision) { + "BUY" -> Color.Red + "SETTING" -> Color(0xFFFFA500) // 주황색 + "SELL" -> Color(0xFF800080) // 보라색 + "HOLD" -> Color.Gray // HOLD는 그레이 + else -> Color.Gray // 그 외 기본값 그레이 + }, fontWeight = FontWeight.ExtraBold ) } @@ -63,6 +65,7 @@ fun TradingDecisionLog() { verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth().fillMaxHeight().background(Color.White) ) { + var firstSet = mutableSetOf() item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함 Text( "💰 거래 기본 설정", @@ -92,9 +95,16 @@ fun TradingDecisionLog() { // 저장 로직을 공통 함수로 분리 val saveAction = { var newValue = localText.toDoubleOrNull() ?: 0.0 + var oldValue = KisSession.config.getValues(configKey) if (configKey.label.contains("PROFIT")) { newValue = newValue / KisSession.config.getValues(ConfigIndex.PROFIT_INDEX) } + if (firstSet.contains(configKey)) { + TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue") + } else { + firstSet.add(configKey) + } + KisSession.config.setValues(configKey, newValue) DatabaseFactory.saveConfig(KisSession.config) println("💾 저장됨: ${configKey.label} = $newValue") @@ -173,11 +183,16 @@ fun TradingDecisionLog() { } val saveAction = { + var oldValue = KisSession.config.getValues(configKey) var newValue = localText.toDoubleOrNull() ?: 0.0 // KisSession.config.setValues(configKey, newValue) DatabaseFactory.saveConfig(KisSession.config) - println("💾 저장됨: ${configKey.label} = $newValue") + if (firstSet.contains(configKey)) { + TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue") + } else { + firstSet.add(configKey) + } labelText = if (configKey.name.contains("PROFIT")) { getRemaining(configKey.label,common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 성향별 비율(${KisSession.config.getValues(configKey)}) + 세금제비용(${KisSession.config.getValues( ConfigIndex.TAX_INDEX)}) = ${(localText.toDouble() * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)) + KisSession.config.getValues(