This commit is contained in:
lunaticbum 2026-03-27 17:54:21 +09:00
parent 681472df58
commit 62e408230e
5 changed files with 84 additions and 47 deletions

View File

@ -0,0 +1,7 @@
object Defines {
val DETAILLOG = false
val LLM_PORT = 8080
val EMBEDDING_PORT = 8081
val AUTOSELL = false
val BLACKLISTEDSTOCKCODES = listOf<String>()
}

View File

@ -9,6 +9,9 @@ import ConfigTable.grade_4_profit
import ConfigTable.grade_5_buy import ConfigTable.grade_5_buy
import ConfigTable.grade_5_profit import ConfigTable.grade_5_profit
import ConfigTable.max_count import ConfigTable.max_count
import Defines.DETAILLOG
import Defines.EMBEDDING_PORT
import Defines.LLM_PORT
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
@ -93,7 +96,7 @@ fun initLogger(isDebug: Boolean) {
} }
fun main() = application { fun main() = application {
initLogger(AutoTradingManager.DETAILLOG) initLogger(DETAILLOG)
val trayState = rememberTrayState() val trayState = rememberTrayState()
var isWindowOpen by remember { mutableStateOf(false) } // 창의 표시 상태 관리 var isWindowOpen by remember { mutableStateOf(false) } // 창의 표시 상태 관리
@ -208,10 +211,10 @@ fun main() = application {
} }
if (config.modelPath.isNotEmpty()) { 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()) { if (config.embedModelPath.isNotEmpty()) {
LlamaServerManager.startServer(binPath, config.embedModelPath, port = AutoTradingManager.EMBEDDING_PORT) LlamaServerManager.startServer(binPath, config.embedModelPath, port = EMBEDDING_PORT)
} }
// 대시보드로 화면 전환 // 대시보드로 화면 전환

View File

@ -1,5 +1,7 @@
package network// src/main/kotlin/network/RagService.kt package network// src/main/kotlin/network/RagService.kt
import Defines.EMBEDDING_PORT
import Defines.LLM_PORT
import TradingLogStore import TradingLogStore
import dev.langchain4j.community.rag.content.retriever.lucene.LuceneEmbeddingStore import dev.langchain4j.community.rag.content.retriever.lucene.LuceneEmbeddingStore
import dev.langchain4j.data.document.Metadata import dev.langchain4j.data.document.Metadata
@ -56,12 +58,12 @@ object RagService {
// 임베딩 모델 (8081) 및 채팅 모델 (8080) 설정 // 임베딩 모델 (8081) 및 채팅 모델 (8080) 설정
private val embeddingModel = OpenAiEmbeddingModel.builder() 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") .apiKey("unused")
.build() .build()
private val chatModel = OpenAiChatModel.builder() 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") .apiKey("unused")
.temperature(0.0) // [중요] 0.0으로 설정하여 결정론적 응답 유도 .temperature(0.0) // [중요] 0.0으로 설정하여 결정론적 응답 유도
.timeout(Duration.ofSeconds(60)) .timeout(Duration.ofSeconds(60))

View File

@ -1,6 +1,10 @@
package service package service
import AutoTradeItem import AutoTradeItem
import Defines.AUTOSELL
import Defines.BLACKLISTEDSTOCKCODES
import Defines.EMBEDDING_PORT
import Defines.LLM_PORT
import network.TradingDecision import network.TradingDecision
import TradingLogStore import TradingLogStore
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -49,9 +53,8 @@ import kotlin.math.*
// service/AutoTradingManager.kt // service/AutoTradingManager.kt
typealias TradingDecisionCallback = (TradingDecision?, Boolean)->Unit typealias TradingDecisionCallback = (TradingDecision?, Boolean)->Unit
object AutoTradingManager { object AutoTradingManager {
val DETAILLOG = true
val LLM_PORT = 13080
val EMBEDDING_PORT = 13081
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private var discoveryJob: Job? = null private var discoveryJob: Job? = null
@ -368,7 +371,9 @@ object AutoTradingManager {
// 1. DB에서 매도 중(SELLING)이거나 만료(EXPIRED)된 매도 건을 가져옵니다. // 1. DB에서 매도 중(SELLING)이거나 만료(EXPIRED)된 매도 건을 가져옵니다.
println("resumePendingSellOrders") println("resumePendingSellOrders")
balance.holdings.forEach { holding -> balance.holdings.forEach { holding ->
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) { 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} ") println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ")
@ -387,9 +392,19 @@ object AutoTradingManager {
// 4. 새로운 주문번호로 DB 업데이트 및 상태를 SELLING으로 유지 // 4. 새로운 주문번호로 DB 업데이트 및 상태를 SELLING으로 유지
// DatabaseFactory.updateStatusAndOrderNo(item.id!!, TradeStatus.SELLING, newOrderNo) // DatabaseFactory.updateStatusAndOrderNo(item.id!!, TradeStatus.SELLING, newOrderNo)
println("✅ [재주문 완료] ${holding.name}: $newOrderNo") println("✅ [재주문 완료] ${holding.name}: $newOrderNo")
TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문 완료[예상수익 : ${holding.profitRate}] ") TradingLogStore.addSellLog(
holding.code,
targetPrice.toString(),
"SELL",
"🎊 보유 주식 매도 주문 완료[예상수익 : ${holding.profitRate}] "
)
}.onFailure { }.onFailure {
TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문 실패[${it.message}] ") TradingLogStore.addSellLog(
holding.code,
targetPrice.toString(),
"SELL",
"🎊 보유 주식 매도 주문 실패[${it.message}] "
)
// println("❌ [재주문 실패] ${holding.name}: ${it.message}") // println("❌ [재주문 실패] ${holding.name}: ${it.message}")
} }
} else { } else {
@ -398,6 +413,7 @@ object AutoTradingManager {
delay(200) // API 호출 부하 방지 delay(200) // API 호출 부하 방지
} }
} }
}
var isSystemReadyToday = false var isSystemReadyToday = false
var isSystemCleanedUpToday = false var isSystemCleanedUpToday = false
private var lastRetryTime = 0L private var lastRetryTime = 0L
@ -537,7 +553,9 @@ object AutoTradingManager {
suspend fun executeMarketLoop() { suspend fun executeMarketLoop() {
val balance = KisTradeService.fetchIntegratedBalance().getOrNull() 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 myCash = balance?.deposit?.replace(",", "")?.toLongOrNull() ?: 0L
val myHoldings = balance?.holdings?.filter { it.quantity.toInt() > 0 }?.map { it.code }?.toSet() ?: emptySet() val myHoldings = balance?.holdings?.filter { it.quantity.toInt() > 0 }?.map { it.code }?.toSet() ?: emptySet()
val pendingStocks = DatabaseFactory.findAllMonitoringTrades().map { it.code } val pendingStocks = DatabaseFactory.findAllMonitoringTrades().map { it.code }
@ -582,6 +600,9 @@ object AutoTradingManager {
while (iterator.hasNext()) { while (iterator.hasNext()) {
totalCount-- totalCount--
val stock = iterator.next() val stock = iterator.next()
if (BLACKLISTEDSTOCKCODES.contains(stock.code)){
println("❌ 차단 처리된 주식 : ${stock.name}")
} else {
try { try {
processSingleStock(stock, myCash, KisTradeService, globalCallback) processSingleStock(stock, myCash, KisTradeService, globalCallback)
} catch (e: Exception) { } catch (e: Exception) {
@ -592,6 +613,7 @@ object AutoTradingManager {
println("남은 후보군 개수 : ${totalCount}") println("남은 후보군 개수 : ${totalCount}")
delay(100) delay(100)
} }
}
println("⏱️ [Cycle End] ${LocalTime.now()}") println("⏱️ [Cycle End] ${LocalTime.now()}")
} }

View File

@ -1,5 +1,8 @@
package service package service
import Defines.DETAILLOG
import Defines.EMBEDDING_PORT
import Defines.LLM_PORT
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -94,12 +97,12 @@ object LlamaServerManager {
binPath, binPath,
"-m", modelPath, "-m", modelPath,
"--port", port.toString(), "--port", port.toString(),
"-c", if (port == AutoTradingManager.EMBEDDING_PORT) "512" else "8192", "-c", if (port == EMBEDDING_PORT) "512" else "8192",
"-ngl", optimalGpuLayers.toString(), "-ngl", optimalGpuLayers.toString(),
"-t", optimalThreads.toString(), "-t", optimalThreads.toString(),
"--embedding" "--embedding"
) )
if (port != AutoTradingManager.EMBEDDING_PORT) { // 텍스트 생성용 모델에만 적용 if (port != EMBEDDING_PORT) { // 텍스트 생성용 모델에만 적용
command.addAll(listOf( command.addAll(listOf(
"-b", "512", // Batch size (토큰 병렬 처리량 제한으로 연산 안정화) "-b", "512", // Batch size (토큰 병렬 처리량 제한으로 연산 안정화)
"--threads-batch", optimalThreads.toString(), "--threads-batch", optimalThreads.toString(),
@ -142,14 +145,14 @@ object LlamaServerManager {
var line: String? var line: String?
while (reader.readLine().also { line = it } != null) { 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) { if (line?.contains("server is listening") == true) {
println("🚀 AI 서버 준비 완료 (Port: $port)") println("🚀 AI 서버 준비 완료 (Port: $port)")
if (port == AutoTradingManager.LLM_PORT){ if (port == LLM_PORT){
AutoTradingManager.llmAnalyser = true AutoTradingManager.llmAnalyser = true
} }
if (port == AutoTradingManager.EMBEDDING_PORT){ if (port == EMBEDDING_PORT){
AutoTradingManager.llmNews = true AutoTradingManager.llmNews = true
} }
if (processes.size > 1) { if (processes.size > 1) {