diff --git a/src/main/kotlin/database/DatabaseFactory.kt b/src/main/kotlin/database/DatabaseFactory.kt index 20d8b1b..952cb7e 100644 --- a/src/main/kotlin/database/DatabaseFactory.kt +++ b/src/main/kotlin/database/DatabaseFactory.kt @@ -444,5 +444,10 @@ object TradingLogStore { } } + fun clear() { + synchronized(this) { + decisionLogs.clear() + } + } } diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index 8789673..65d0e5c 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -3,6 +3,7 @@ package service import AutoTradeItem import network.TradingDecision import TradingLogStore +import TradingLogStore.decisionLogs import getLlamaBinPath import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -395,9 +396,13 @@ object AutoTradingManager { } catch (e: Exception) {} } + var onMarketClosed: (() -> Unit)? = null + var now = LocalTime.now(ZoneId.of("Asia/Seoul")) var currentTimeMillis = System.currentTimeMillis() var waitTime = 0.2 + val H16 = LocalTime.of(16, 0) + val H08M50 = LocalTime.of(8, 50) private fun runDiscoveryLoop(tradeService: KisTradeService, callback: TradingDecisionCallback) { discoveryJob = scope.launch { println("πŸš€ [AutoTrading] 발꡴ 루프 μ‹œμž‘: ${LocalDateTime.now()}") @@ -406,9 +411,22 @@ object AutoTradingManager { now = LocalTime.now(ZoneId.of("Asia/Seoul")) currentTimeMillis = System.currentTimeMillis() lastTickTime.set(System.currentTimeMillis()) // 생쑴 μ‹ κ³  + // [μˆ˜μ •] 16μ‹œ μ΄ν›„μ΄κ±°λ‚˜ 8μ‹œ 30λΆ„ 이전이면 λͺ¨λ“  둜직 쀑단 및 μ΄ˆκΈ°ν™” + if (now.isAfter(LocalTime.of(16, 0)) || now.isBefore(LocalTime.of(8, 30))) { + println("πŸŒ™ [System] 마감 μ‹œκ°„ 도달. μžμ› 정리 ν›„ λŒ€κΈ° λͺ¨λ“œ(μ„€μ • ν™”λ©΄)둜 μ „ν™˜ν•©λ‹ˆλ‹€.") + SystemSleepPreventer.sleepDisplay() // λͺ¨λ‹ˆν„° 끄기 + KisWebSocketManager.disconnect() + BrowserManager.closeIfIdle(0) + LlamaServerManager.stopAll() // AI μ„œλ²„ μ™„μ „ μ’…λ£Œ + TradingLogStore.clear() + onMarketClosed?.invoke() // Main.kt에 μ„€μ • ν™”λ©΄μœΌλ‘œ 가라고 μ‹ ν˜Έ 전솑 + stopDiscovery() // 발꡴ 루프 μ™„μ „ 폭파 (내일 8μ‹œ 30뢄에 λ‹€μ‹œ 켜짐) + return@launch + } when { + //μž₯쀑 - now.isBefore(LocalTime.of(16, 0)) && now.isAfter(LocalTime.of(8, 50)) -> { + now.isBefore(H16) && now.isAfter(H08M50) -> { waitTime = 0.2 if (now.isAfter(LocalTime.of(8, 0)) && now.isBefore(LocalTime.of(15, 30))) { if (!KisSession.isMarketTokenValid() || !KisSession.isTradeTokenValid()) { @@ -491,7 +509,7 @@ object AutoTradingManager { } //μž₯μ™Έ - now.isAfter(LocalTime.of(18, 0)) || now.isBefore(LocalTime.of(8, 50)) -> { + now.isAfter(H16) || now.isBefore(H08M50) -> { when { (now.hour == 0 && now.minute == 0 && (isSystemReadyToday || isSystemCleanedUpToday)) -> { waitTime = 10.0 diff --git a/src/main/kotlin/ui/SettingsScreen.kt b/src/main/kotlin/ui/SettingsScreen.kt index 356dc83..f092896 100644 --- a/src/main/kotlin/ui/SettingsScreen.kt +++ b/src/main/kotlin/ui/SettingsScreen.kt @@ -35,6 +35,7 @@ import network.KisTradeService import org.jetbrains.exposed.sql.deleteAll import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.transactions.transaction +import service.SystemSleepPreventer // src/main/kotlin/ui/SettingsScreen.kt @@ -45,6 +46,61 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) { var config by remember { mutableStateOf(KisSession.config) } var statusMessage by remember { mutableStateOf("정보λ₯Ό μž…λ ₯ν•˜μ„Έμš”.") } + val authenticateAndStart: suspend () -> Unit = { + var retryCount = 0 + val maxRetries = 3 + val totalDelaySeconds = 90 + var isAuthCompleted = false + + while (retryCount <= maxRetries && !isAuthCompleted) { + if (retryCount > 0) { + for (secondsLeft in totalDelaySeconds downTo 1) { + statusMessage = "⚠️ 인증 μ‹€νŒ¨. ${secondsLeft}초 ν›„ μžλ™μœΌλ‘œ λ‹€μ‹œ μ‹œλ„ν•©λ‹ˆλ‹€. (μ‹œλ„ ${retryCount}/${maxRetries})" + delay(1000L) + } + } + statusMessage = if (retryCount == 0) "⏳ 인증 μ‹œλ„ 쀑..." else "⏳ ${retryCount}μ°¨ μž¬μ‹œλ„ 쀑..." + + KisSession.config = config + DatabaseFactory.saveConfig(config) + + DartCodeManager.updateCorpCodes(HttpClient(CIO) { + install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } + }) + + val authSuccess = KisAuthService.refreshAllTokens() + val wsKeySuccess = KisTradeService.refreshWebsocketKey() + + if (authSuccess && wsKeySuccess) { + statusMessage = "βœ… 인증 성곡! LLM μ‹œμž‘ 쀑..." + isAuthCompleted = true + onAuthSuccess() // μ—¬κΈ°μ„œ λŒ€μ‹œλ³΄λ“œλ‘œ λ„˜μ–΄κ° + } else { + retryCount++ + if (retryCount > maxRetries) { + statusMessage = "❌ 인증 μ‹€νŒ¨. 3회 μž¬μ‹œλ„ ν›„ μ€‘λ‹¨λ˜μ—ˆμŠ΅λ‹ˆλ‹€." + } + } + } + } + + LaunchedEffect(config) { + while (true) { + val now = java.time.LocalTime.now(java.time.ZoneId.of("Asia/Seoul")) + // 08:30 ~ 15:30 사이이고, 킀값이 μ΅œμ†Œν•œ ν•˜λ‚˜λΌλ„ μ‘΄μž¬ν•  λ•Œ μžλ™ μ‹€ν–‰ + if (now.isAfter(java.time.LocalTime.of(8, 30)) && now.isBefore(java.time.LocalTime.of(15, 30))) { + if (config.realAppKey.isNotEmpty() || config.vtsAppKey.isNotEmpty()) { + SystemSleepPreventer.wakeDisplay() // λͺ¨λ‹ˆν„° 켜기 + statusMessage = "⏰ μžλ™ μ‹€ν–‰ μ‹œκ°„(08:30)μž…λ‹ˆλ‹€. μ‹œμŠ€ν…œμ„ κ°€λ™ν•©λ‹ˆλ‹€." + authenticateAndStart() + break // μ„±κ³΅ν•˜λ©΄ 루프 νƒˆμΆœ + } + } + delay(60_000 * 2) // 1λΆ„λ§ˆλ‹€ μ‹œκ°„ 체크 + } + } + + // κ³„μ’Œλ²ˆν˜Έ μž…λ ₯ μ‹œ 데이터 μžλ™ λ‘œλ“œ ν•¨μˆ˜ fun checkAndLoadConfig(accountNo: String, isReal: Boolean) { val loaded = DatabaseFactory.findConfigByAccount(accountNo) @@ -190,47 +246,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) { modifier = Modifier.fillMaxWidth().height(50.dp), onClick = { scope.launch { - var retryCount = 0 - val maxRetries = 3 - val totalDelaySeconds = 90 // 1λΆ„ 30초 = 90초 - var isAuthCompleted = false - - while (retryCount <= maxRetries && !isAuthCompleted) { - // μž¬μ‹œλ„ μ‹œ λŒ€κΈ° 및 μΉ΄μš΄νŠΈλ‹€μš΄ ν‘œμ‹œ - if (retryCount > 0) { - for (secondsLeft in totalDelaySeconds downTo 1) { - statusMessage = "⚠️ 인증 μ‹€νŒ¨. ${secondsLeft}초 ν›„ μžλ™μœΌλ‘œ λ‹€μ‹œ μ‹œλ„ν•©λ‹ˆλ‹€. (μ‹œλ„ ${retryCount}/${maxRetries})" - delay(1000L) // 1초 λŒ€κΈ° - } - } - - statusMessage = if (retryCount == 0) "⏳ 인증 μ‹œλ„ 쀑..." else "⏳ ${retryCount}μ°¨ μž¬μ‹œλ„ 쀑..." - - // 1. μ„€μ •κ°’ μ €μž₯ - KisSession.config = config - DatabaseFactory.saveConfig(config) - - // 2. λ²•μΈμ½”λ“œ μ—…λ°μ΄νŠΈ - DartCodeManager.updateCorpCodes(HttpClient(CIO) { - install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } - }) - - // 3. 토큰 및 μ›Ήμ†ŒμΌ“ ν‚€ κ°±μ‹  - val authSuccess = KisAuthService.refreshAllTokens() - val wsKeySuccess = KisTradeService.refreshWebsocketKey() - - if (authSuccess && wsKeySuccess) { - statusMessage = "βœ… 인증 성곡! LLM μ‹œμž‘ 쀑..." - isAuthCompleted = true - onAuthSuccess() - } else { - retryCount++ - if (retryCount > maxRetries) { - statusMessage = "❌ 인증 μ‹€νŒ¨. 3회 μž¬μ‹œλ„ ν›„ μ€‘λ‹¨λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν‚€ 정보λ₯Ό ν™•μΈν•˜μ„Έμš”." - } - // λ‹€μŒ λ£¨ν”„μ—μ„œ μΉ΄μš΄νŠΈλ‹€μš΄ μ§„μž… - } - } + authenticateAndStart() } } ) { Text("μ„€μ • μ €μž₯ 및 μ‹€ν–‰") }