From 62e408230e58dfd5c05cdfe45294706ee305d09d Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Fri, 27 Mar 2026 17:54:21 +0900 Subject: [PATCH] ... --- src/main/kotlin/Defines.kt | 7 ++ src/main/kotlin/Main.kt | 9 +- src/main/kotlin/network/RagService.kt | 6 +- src/main/kotlin/service/AutoTradingManager.kt | 96 ++++++++++++------- src/main/kotlin/service/LlamaServerManager.kt | 13 ++- 5 files changed, 84 insertions(+), 47 deletions(-) create mode 100644 src/main/kotlin/Defines.kt diff --git a/src/main/kotlin/Defines.kt b/src/main/kotlin/Defines.kt new file mode 100644 index 0000000..8cee376 --- /dev/null +++ b/src/main/kotlin/Defines.kt @@ -0,0 +1,7 @@ +object Defines { + val DETAILLOG = false + val LLM_PORT = 8080 + val EMBEDDING_PORT = 8081 + val AUTOSELL = false + val BLACKLISTEDSTOCKCODES = listOf() +} \ No newline at end of file diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index a7a6756..4d406ce 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -9,6 +9,9 @@ import ConfigTable.grade_4_profit import ConfigTable.grade_5_buy import ConfigTable.grade_5_profit import ConfigTable.max_count +import Defines.DETAILLOG +import Defines.EMBEDDING_PORT +import Defines.LLM_PORT import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* @@ -93,7 +96,7 @@ fun initLogger(isDebug: Boolean) { } fun main() = application { - initLogger(AutoTradingManager.DETAILLOG) + initLogger(DETAILLOG) val trayState = rememberTrayState() var isWindowOpen by remember { mutableStateOf(false) } // 창의 표시 상태 관리 @@ -208,10 +211,10 @@ fun main() = application { } if (config.modelPath.isNotEmpty()) { - LlamaServerManager.startServer(binPath, config.modelPath,port = AutoTradingManager.LLM_PORT) + LlamaServerManager.startServer(binPath, config.modelPath,port = LLM_PORT) } if (config.embedModelPath.isNotEmpty()) { - LlamaServerManager.startServer(binPath, config.embedModelPath, port = AutoTradingManager.EMBEDDING_PORT) + LlamaServerManager.startServer(binPath, config.embedModelPath, port = EMBEDDING_PORT) } // 대시보드로 화면 전환 diff --git a/src/main/kotlin/network/RagService.kt b/src/main/kotlin/network/RagService.kt index 208fca7..982c30e 100644 --- a/src/main/kotlin/network/RagService.kt +++ b/src/main/kotlin/network/RagService.kt @@ -1,5 +1,7 @@ package network// src/main/kotlin/network/RagService.kt +import Defines.EMBEDDING_PORT +import Defines.LLM_PORT import TradingLogStore import dev.langchain4j.community.rag.content.retriever.lucene.LuceneEmbeddingStore import dev.langchain4j.data.document.Metadata @@ -56,12 +58,12 @@ object RagService { // 임베딩 모델 (8081) 및 채팅 모델 (8080) 설정 private val embeddingModel = OpenAiEmbeddingModel.builder() - .baseUrl("http://127.0.0.1:${AutoTradingManager.EMBEDDING_PORT}/v1") + .baseUrl("http://127.0.0.1:${EMBEDDING_PORT}/v1") .apiKey("unused") .build() private val chatModel = OpenAiChatModel.builder() - .baseUrl("http://127.0.0.1:${AutoTradingManager.LLM_PORT}/v1") + .baseUrl("http://127.0.0.1:${LLM_PORT}/v1") .apiKey("unused") .temperature(0.0) // [중요] 0.0으로 설정하여 결정론적 응답 유도 .timeout(Duration.ofSeconds(60)) diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index 391c0a4..36336e4 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -1,6 +1,10 @@ package service import AutoTradeItem +import Defines.AUTOSELL +import Defines.BLACKLISTEDSTOCKCODES +import Defines.EMBEDDING_PORT +import Defines.LLM_PORT import network.TradingDecision import TradingLogStore import androidx.compose.runtime.getValue @@ -49,9 +53,8 @@ import kotlin.math.* // service/AutoTradingManager.kt typealias TradingDecisionCallback = (TradingDecision?, Boolean)->Unit object AutoTradingManager { - val DETAILLOG = true - val LLM_PORT = 13080 - val EMBEDDING_PORT = 13081 + + private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) private var discoveryJob: Job? = null @@ -368,34 +371,47 @@ object AutoTradingManager { // 1. DB에서 매도 중(SELLING)이거나 만료(EXPIRED)된 매도 건을 가져옵니다. println("resumePendingSellOrders") balance.holdings.forEach { holding -> - - if (holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 && holding.profitRate.toDouble() > KisSession.config.SELL_PROFIT) { - println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ") - - // 3. 기존 목표가(targetPrice)로 다시 매도 주문 전송 - var targetPrice = holding.currentPrice.toDouble() - targetPrice = MarketUtil.roundToTickSize(targetPrice + MarketUtil.getTickSize(targetPrice)) - - println("🔄 [재주문] ${holding.name} (${holding.code}) 매도 목표 ${targetPrice} 미체결 매도 건 재주문 시도") -// TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문[예상수익 : ${holding.profitRate}] ") - tradeService.postOrder( - stockCode = holding.code, - qty = holding.availOrderCount, - price = targetPrice.toInt().toString(), - isBuy = false - ).onSuccess { newOrderNo -> - // 4. 새로운 주문번호로 DB 업데이트 및 상태를 SELLING으로 유지 -// DatabaseFactory.updateStatusAndOrderNo(item.id!!, TradeStatus.SELLING, newOrderNo) - println("✅ [재주문 완료] ${holding.name}: $newOrderNo") - TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문 완료[예상수익 : ${holding.profitRate}] ") - }.onFailure { - TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문 실패[${it.message}] ") -// println("❌ [재주문 실패] ${holding.name}: ${it.message}") - } + if (BLACKLISTEDSTOCKCODES.contains(holding.code)){ + println("❌ 차단 처리된 주식 : ${holding.name}") } else { + if (holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 && holding.profitRate.toDouble() > KisSession.config.SELL_PROFIT) { + println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ") + // 3. 기존 목표가(targetPrice)로 다시 매도 주문 전송 + var targetPrice = holding.currentPrice.toDouble() + targetPrice = MarketUtil.roundToTickSize(targetPrice + MarketUtil.getTickSize(targetPrice)) + + println("🔄 [재주문] ${holding.name} (${holding.code}) 매도 목표 ${targetPrice} 미체결 매도 건 재주문 시도") +// TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문[예상수익 : ${holding.profitRate}] ") + tradeService.postOrder( + stockCode = holding.code, + qty = holding.availOrderCount, + price = targetPrice.toInt().toString(), + isBuy = false + ).onSuccess { newOrderNo -> + // 4. 새로운 주문번호로 DB 업데이트 및 상태를 SELLING으로 유지 +// DatabaseFactory.updateStatusAndOrderNo(item.id!!, TradeStatus.SELLING, newOrderNo) + println("✅ [재주문 완료] ${holding.name}: $newOrderNo") + TradingLogStore.addSellLog( + holding.code, + targetPrice.toString(), + "SELL", + "🎊 보유 주식 매도 주문 완료[예상수익 : ${holding.profitRate}] " + ) + }.onFailure { + TradingLogStore.addSellLog( + holding.code, + targetPrice.toString(), + "SELL", + "🎊 보유 주식 매도 주문 실패[${it.message}] " + ) +// println("❌ [재주문 실패] ${holding.name}: ${it.message}") + } + } else { + + } + delay(200) // API 호출 부하 방지 } - delay(200) // API 호출 부하 방지 } } var isSystemReadyToday = false @@ -537,7 +553,9 @@ object AutoTradingManager { suspend fun executeMarketLoop() { val balance = KisTradeService.fetchIntegratedBalance().getOrNull() - balance?.let { resumePendingSellOrders(KisTradeService, it) } + + if (AUTOSELL) balance?.let { resumePendingSellOrders(KisTradeService, it) } + val myCash = balance?.deposit?.replace(",", "")?.toLongOrNull() ?: 0L val myHoldings = balance?.holdings?.filter { it.quantity.toInt() > 0 }?.map { it.code }?.toSet() ?: emptySet() val pendingStocks = DatabaseFactory.findAllMonitoringTrades().map { it.code } @@ -582,15 +600,19 @@ object AutoTradingManager { while (iterator.hasNext()) { totalCount-- val stock = iterator.next() - try { - processSingleStock(stock, myCash, KisTradeService, globalCallback) - } catch (e: Exception) { - println("❌ 처리 중 오류 발생 (건너뜀): ${stock.name}") - } finally { - iterator.remove() + if (BLACKLISTEDSTOCKCODES.contains(stock.code)){ + println("❌ 차단 처리된 주식 : ${stock.name}") + } else { + try { + processSingleStock(stock, myCash, KisTradeService, globalCallback) + } catch (e: Exception) { + println("❌ 처리 중 오류 발생 (건너뜀): ${stock.name}") + } finally { + iterator.remove() + } + println("남은 후보군 개수 : ${totalCount}") + delay(100) } - println("남은 후보군 개수 : ${totalCount}") - delay(100) } println("⏱️ [Cycle End] ${LocalTime.now()}") } diff --git a/src/main/kotlin/service/LlamaServerManager.kt b/src/main/kotlin/service/LlamaServerManager.kt index 6b4da8a..b7b039b 100644 --- a/src/main/kotlin/service/LlamaServerManager.kt +++ b/src/main/kotlin/service/LlamaServerManager.kt @@ -1,5 +1,8 @@ package service +import Defines.DETAILLOG +import Defines.EMBEDDING_PORT +import Defines.LLM_PORT import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -94,12 +97,12 @@ object LlamaServerManager { binPath, "-m", modelPath, "--port", port.toString(), - "-c", if (port == AutoTradingManager.EMBEDDING_PORT) "512" else "8192", + "-c", if (port == EMBEDDING_PORT) "512" else "8192", "-ngl", optimalGpuLayers.toString(), "-t", optimalThreads.toString(), "--embedding" ) - if (port != AutoTradingManager.EMBEDDING_PORT) { // 텍스트 생성용 모델에만 적용 + if (port != EMBEDDING_PORT) { // 텍스트 생성용 모델에만 적용 command.addAll(listOf( "-b", "512", // Batch size (토큰 병렬 처리량 제한으로 연산 안정화) "--threads-batch", optimalThreads.toString(), @@ -142,14 +145,14 @@ object LlamaServerManager { var line: String? while (reader.readLine().also { line = it } != null) { // 로그 출력 (디버깅용) - if (AutoTradingManager.DETAILLOG) println("[Server $port] $line") + if (DETAILLOG) println("[Server $port] $line") if (line?.contains("server is listening") == true) { println("🚀 AI 서버 준비 완료 (Port: $port)") - if (port == AutoTradingManager.LLM_PORT){ + if (port == LLM_PORT){ AutoTradingManager.llmAnalyser = true } - if (port == AutoTradingManager.EMBEDDING_PORT){ + if (port == EMBEDDING_PORT){ AutoTradingManager.llmNews = true } if (processes.size > 1) {