diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index a80a11b..c259058 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -502,10 +502,11 @@ object AutoTradingManager { println("๐ŸŒ™ [System] ์—…๋ฌด ์ข…๋ฃŒ ๋ฐ ์ž์› ์ •๋ฆฌ ์‹œ์ž‘...") SystemSleepPreventer.sleepDisplay() // ๋ชจ๋‹ˆํ„ฐ ๋„๊ธฐ KisWebSocketManager.disconnect() - //isSystemReadyToday = false + BrowserManager.closeIfIdle(0) // ์ฆ‰์‹œ ๋‹ซ๊ธฐ if (LlamaServerManager.stopAll()) { isSystemCleanedUpToday = true } + } println("โœ… [System] ์˜ค๋Š˜์˜ ๋ชจ๋“  ์ •๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.") } catch (e: Exception) { @@ -637,6 +638,7 @@ object AutoTradingManager { private suspend fun waitForNextCycle(minutes: Double) { println("๐Ÿ’ค ๋Œ€๊ธฐ ๋ชจ๋“œ ์ง„์ž…... $minutes") val endWait = System.currentTimeMillis() + (minutes * 60 * 1000L) + BrowserManager.closeIfIdle(0) // ์ฆ‰์‹œ ๋‹ซ๊ธฐ while (System.currentTimeMillis() < endWait && isRunning()) { lastTickTime.set(System.currentTimeMillis()) // ๋Œ€๊ธฐ ์ค‘์—๋„ Watchdog์— ์ƒ์กด ์‹ ๊ณ  println("๐Ÿ’ค ๋Œ€๊ธฐ ๋ชจ๋“œ ์ƒํƒœ ํ™•์ธ...") @@ -645,7 +647,6 @@ object AutoTradingManager { } - private suspend fun executeClosingLiquidation(tradeService: KisTradeService) { val activeTrades = DatabaseFactory.findAllMonitoringTrades() val balanceResult = tradeService.fetchIntegratedBalance().getOrNull() diff --git a/src/main/kotlin/service/DynamicNewsScraper.kt b/src/main/kotlin/service/DynamicNewsScraper.kt index 2b4d2d9..fc2f228 100644 --- a/src/main/kotlin/service/DynamicNewsScraper.kt +++ b/src/main/kotlin/service/DynamicNewsScraper.kt @@ -1,5 +1,6 @@ package service +import com.microsoft.playwright.Browser import com.microsoft.playwright.Playwright import com.microsoft.playwright.BrowserType import com.microsoft.playwright.Page @@ -27,9 +28,9 @@ object BrowserManager { private const val MAX_TOTAL_FAILURES = 3 private val mutex = Mutex() // ๋™์‹œ ์ ‘๊ทผ ์ œ์–ด์šฉ ๋ฎคํ…์Šค - suspend fun getBrowser(): com.microsoft.playwright.Browser { + suspend fun getBrowser(): Browser { return mutex.withLock { - // ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—†๊ฑฐ๋‚˜ ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ๋‹ค๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ + lastAccessTime = System.currentTimeMillis() // ์ ‘๊ทผ ์‹œ๊ฐ„ ๊ฐฑ์‹  if (_browser == null || !_browser!!.isConnected) { startNewBrowser() } @@ -64,7 +65,9 @@ object BrowserManager { } catch (e: Exception) { // ์ข…๋ฃŒ ์—๋Ÿฌ ๋ฌด์‹œ } finally { - startNewBrowser() + if (util.MarketUtil.isKoreanMarketOpen()) { + startNewBrowser() + } failCount = 0 } } @@ -81,6 +84,24 @@ object BrowserManager { println("๐Ÿšจ ๋ธŒ๋ผ์šฐ์ € ์—”์ง„ ์‹œ์ž‘ ์‹คํŒจ: ${e.message}") } } + + private var lastAccessTime = System.currentTimeMillis() + + + + // ๋Œ€๊ธฐ ๋ชจ๋“œ์ผ ๋•Œ ํ˜ธ์ถœํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ + suspend fun closeIfIdle(idleTimeoutMs: Long = 60_000) { + mutex.withLock { + if (_browser != null && System.currentTimeMillis() - lastAccessTime > idleTimeoutMs) { + println("โ™ป๏ธ [SafeScraper] ์žฅ์‹œ๊ฐ„ ๋Œ€๊ธฐ๋กœ ๋ธŒ๋ผ์šฐ์ € ์ž์›์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.") + _browser?.close() + playwright?.close() + _browser = null + playwright = null + } + } + } + } object DynamicNewsScraper {