From e94869b9e7139072033f102b5aead0bf434fb773 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Wed, 20 May 2026 17:18:15 +0900 Subject: [PATCH] ... --- src/main/kotlin/database/DatabaseFactory.kt | 46 ++++++++++++------- src/main/kotlin/model/AppConfig.kt | 1 + src/main/kotlin/service/AutoTradingManager.kt | 19 +++++--- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/database/DatabaseFactory.kt b/src/main/kotlin/database/DatabaseFactory.kt index f5f0e2f..b8e0103 100644 --- a/src/main/kotlin/database/DatabaseFactory.kt +++ b/src/main/kotlin/database/DatabaseFactory.kt @@ -614,6 +614,22 @@ object TradingLogStore { } fun addNotice(name : String, code : String, log: String) { + var isSendable = false + val current = System.currentTimeMillis() + + if (KisSession.tradeConfig.useTagsShare.contains("NOTICE") && + KisSession.tradeConfig.useLogKeywordsShare.any { log.contains(it) }) { + + // 대소문자 구분 없이 key를 찾기 위해 원본 code를 가공하거나 그대로 사용 + val lastSentTime = noticeFilter[code.uppercase()] + + // 기록이 없거나(처음 보냄), 마지막 발송 기준 30분이 지났다면 + if (lastSentTime == null || (current - lastSentTime > 1000 * 60 * 30L)) { + isSendable = true + noticeFilter[code.uppercase()] = current // 즉시 발송 시간 갱신하여 중복 방지 + } + } + synchronized(this) { if (decisionLogs.size > 1000) decisionLogs.removeAt(0) decisionLogs.add( @@ -623,28 +639,24 @@ object TradingLogStore { decision = "NOTICE", confidence = 100.0, reason = log - ).apply { - if (KisSession.tradeConfig.useTagsShare.contains(decision) && KisSession.tradeConfig.useLogKeywordsShare.any { - log.contains( - it - ) - }) { - var current = System.currentTimeMillis() - var sendable = noticeFilter.filter { it.key.equals(code, true) && ((current - it.value) > 1000 * 60 * 30L)}.isNotEmpty() - if (sendable) { - CoroutineScope(Dispatchers.Default).launch { - NewsService.sendTelegramMessage("${this@apply.decision}$name[$code] ${log}") - - }} - noticeFilter[code] = current - } - } + ) ) } + if (isSendable) { + applicationScope.launch { + try { + NewsService.sendTelegramMessage("NOTICE $name[$code] $log") + } catch (e: Exception) { + // 발송 실패 시 원상복구를 원한다면 주석 해제 (단, 일시적 네트웍 장애 시 도배 위험 있음) + // noticeFilter.remove(code.uppercase()) + e.printStackTrace() + } + } + } } - + private val applicationScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) var noticeFilter = hashMapOf() fun addNotice(name : String, code : String, log: String, qty: Int? = null) { synchronized(this) { diff --git a/src/main/kotlin/model/AppConfig.kt b/src/main/kotlin/model/AppConfig.kt index 5084f45..547eb9e 100644 --- a/src/main/kotlin/model/AppConfig.kt +++ b/src/main/kotlin/model/AppConfig.kt @@ -254,6 +254,7 @@ class TradeConfig { var plusFilter : Double = 15.0 var excuteCountOnMin : Int = 2 var autoSellOrder : Boolean = false + var excuteMinCheck : Int = 2 } diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index d284c58..e0d4263 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -347,7 +347,7 @@ object AutoTradingManager { TradingReportManager.updateExecution(orderNo, actualSellPrice, actualSellQty) println("🎊 [매칭 성공] 매도 완료: ${dbItem.name} | 매도가: ${actualSellPrice.toInt()}") - + TradingLogStore.addSellLog(dbItem.name,actualSellPrice.toString(),"SELL","매도 완료") myOredsAndBalanceCodes.remove(dbItem.code) TradingReportManager.closePositionCycle(dbItem.code) // 사이클 종료 알림 @@ -532,6 +532,8 @@ object AutoTradingManager { ) } } else { + var errMsg = "" + var isSuccess = false if (KisSession.tradeConfig.autoSellOrder && holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 @@ -546,15 +548,17 @@ object AutoTradingManager { price = targetPrice.toInt().toString(), isBuy = false, ).onSuccess { newOrderNo -> - println("✅ [보유 주식 손절 처리] 매수가 기준 (${holding.avgPrice.toDouble()} 3호가 위[${targetPrice}] 매도 주문") + println("✅ [보유 주식 손절 처리] ${holding.name} 매수가 기준 (${holding.avgPrice.toDouble()} 3호가 위[${targetPrice}] 매도 주문") + isSuccess = true }.onFailure { err-> - println("✅ [보유 주식 손절 처리] 실패 ${err.message}") + println("✅ [보유 주식 손절 처리] ${holding.name} 실패 ${targetPrice} ${err.message}") + errMsg = err.message.toString() } TradingLogStore.addNotice( "보유주식[${holding.name}]", holding.code, - "매수가 기준 (${holding.avgPrice.toDouble()} 3호가 위[${targetPrice}] 매도 주문" + "매수가 기준 (${holding.avgPrice.toDouble()} 3호가 위[${targetPrice}] 매도 주문 ${if (isSuccess) "성공" else "실패[${errMsg}]"}" ) } else if (KisSession.config.stop_Loss && holding != null && holding.quantity.toInt() > 0 @@ -785,7 +789,8 @@ object AutoTradingManager { var loadedTops = mutableListOf>() fun poll100Stocks(): List> { - val count = minOf(loadedTops.size, 150) + loadedTops.shuffle() + val count = minOf(loadedTops.size, 200) if (count == 0) return emptyList() // 앞의 100개를 복사 @@ -881,7 +886,7 @@ object AutoTradingManager { } if (remainingCandidates.isEmpty()) { - if (loadedTops.size < 100) { + if (loadedTops.size < 200) { loadedTops.addAll(StockUniverseLoader.loadUniverse()) loadedTops.shuffle() println("✅ 총 ${loadedTops.size}개의 종목이 로드되있음.") @@ -966,7 +971,7 @@ object AutoTradingManager { } } isExecuted = true - } else if (now.hour == 9) { + } else if (now.hour == 9 && now.minute % KisSession.tradeConfig.excuteMinCheck == 0) { TradingLogStore.addAnalyzer( " - ", " - ",