This commit is contained in:
2026-02-03 18:07:18 +09:00
parent d4926646f9
commit a51ffb6193
9 changed files with 251 additions and 61 deletions
+5 -3
View File
@@ -41,7 +41,7 @@ object KisTradeService {
// [수정] 모든 로그(Headers + Body)를 찍도록 설정
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.BODY
level = LogLevel.NONE
}
}
@@ -84,10 +84,12 @@ object KisTradeService {
val totalAmt = (domRes?.output2?.firstOrNull()?.tot_evlu_amt?.toLongOrNull() ?: 0L) +
(ovsRes?.output2?.firstOrNull()?.tot_evlu_amt?.toLongOrNull() ?: 0L)
val depositAmt = domRes?.output2?.firstOrNull()?.dnca_tot_amt?.toLongOrNull() ?: 0L
Result.success(UnifiedBalance(
totalAsset = String.format("%,d", totalAmt),
totalProfitRate = domRes?.output2?.firstOrNull()?.evlu_pfls_rt ?: "0.0",
deposit = String.format("%,d", depositAmt),
holdings = combinedHoldings
))
} catch (e: Exception) { Result.failure(e) }
@@ -220,7 +222,7 @@ object KisTradeService {
val body = response.body<JsonObject>()
val output2 = body["output2"]?.jsonArray
println("output2 ${output2}")
// println("output2 ${output2}")
val candles = output2?.map { element ->
val obj = element.jsonObject
CandleData(
@@ -394,7 +396,7 @@ object KisTradeService {
val cano = pureAccount.take(8)
val acntPrdtCd = pureAccount.takeLast(2)
return try {
println("orgNo")
// println("orgNo")
val response = client.post("$baseUrl/uapi/domestic-stock/v1/trading/order-rvsecncl") {
header("authorization", "Bearer ${config.tradeToken}")
header("appkey", if (config.isSimulation) config.vtsAppKey else config.realAppKey)
+1 -1
View File
@@ -29,7 +29,7 @@ object NewsService {
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
level = LogLevel.NONE
}
}
+48 -30
View File
@@ -52,9 +52,9 @@ object RagService {
fun active() {
println("[Cache] Active")
// println("[Cache] Active")
if (UrlCacheManager.isInitialized()) return
println("[Cache] initialize")
// println("[Cache] initialize")
UrlCacheManager.initialize(embeddingStore, embeddingModel)
}
@@ -130,32 +130,39 @@ object RagService {
suspend fun processStock(stockName: String,stockCode: String,result : TradingDecisionCallback) {
// 1. 10분간의 데이터 가져오기 (API 호출)
coroutineScope {
var tradingDecision : TradingDecision = TradingDecision()
tradingDecision.stockCode = stockCode
var corpInfo = DartCodeManager.getCorpCode(stockCode)
corpInfo?.stockName = stockName
corpInfo?.let { NewsService.fetchAndIngestNews(it) }
try {
val financialDataDeferred = async { NewsService.fetchFinancialGrowth(corpInfo?.cCode ?: "") }
tradingDecision.financialData = financialDataDeferred.await()
result(tradingDecision,false)
var tradingDecision: TradingDecision = TradingDecision()
tradingDecision.stockCode = stockCode
var corpInfo = DartCodeManager.getCorpCode(stockCode)
corpInfo?.stockName = stockName
tradingDecision.stockName = stockName
tradingDecision.corpName = corpInfo?.cName ?: ""
corpInfo?.let { NewsService.fetchAndIngestNews(it) }
tradingDecision.techSummary = TechnicalAnalyzer.generateComprehensiveReport()
result(tradingDecision,false)
val financialDataDeferred = async { NewsService.fetchFinancialGrowth(corpInfo?.cCode ?: "") }
val question = "${corpInfo?.cName} $stockName[$stockCode]의 향후 실적 전망과 관련된 핵심 뉴스"
val questionEmbedding = embeddingModel.embed(question).content()
val searchResult = embeddingStore.search(
EmbeddingSearchRequest.builder()
.queryEmbedding(questionEmbedding)
.maxResults(3)
.build()
)
tradingDecision.newsContext = searchResult.matches().joinToString("\n") { it.embedded().text() }
result(tradingDecision,false)
result(decideTrading(stockCode, tradingDecision),true)
tradingDecision.financialData = financialDataDeferred.await()
result(tradingDecision, false)
tradingDecision.techSummary = TechnicalAnalyzer.generateComprehensiveReport()
result(tradingDecision, false)
val question = "${corpInfo?.cName} $stockName[$stockCode]의 향후 실적 전망과 관련된 핵심 뉴스"
val questionEmbedding = embeddingModel.embed(question).content()
val searchResult = embeddingStore.search(
EmbeddingSearchRequest.builder()
.queryEmbedding(questionEmbedding)
.maxResults(3)
.build()
)
tradingDecision.newsContext = searchResult.matches().joinToString("\n") { it.embedded().text() }
result(tradingDecision, false)
result(decideTrading(stockCode, tradingDecision), true)
}catch (e: Exception) {
e.printStackTrace()
}
}
}
@@ -220,7 +227,7 @@ object RagService {
""".trimIndent()
val response = chatModel.chat(UserMessage.from(finalPrompt))
println(response)
// println(response)
return response.aiMessage().text()
}
@@ -271,7 +278,7 @@ object RagService {
val response = chatModel.chat(UserMessage.from(prompt))
val rawResponse = response.aiMessage().text()
val jsonResponse = JsonSanitizer.formatJson(rawResponse)
println("📥 [AI Raw JSON]:\n$jsonResponse")
// println("📥 [AI Raw JSON]:\n$jsonResponse")
// 2. 유연한 파서 설정 (소수점 및 예외 상황 대응)
val lenientJson = Json {
@@ -283,12 +290,14 @@ object RagService {
// JSON 파싱 (Kotlinx Serialization 활용)
return try {
println(jsonResponse)
// println(jsonResponse)
val decision = lenientJson.decodeFromString<TradingDecision>(jsonResponse)
decision.financialData = tempDecision.financialData
decision.newsContext = tempDecision.newsContext
decision.techSummary = tempDecision.techSummary
decision.stockCode = tempDecision.stockCode
decision.corpName = tempDecision.corpName
decision.stockName = tempDecision.stockName
decision
} catch (e: dev.langchain4j.exception.InternalServerException) {
// 서버 에러 (컨텍스트 초과 등) 발생 시 로그 남기고 null 반환 혹은 커스텀 에러 처리
@@ -308,11 +317,14 @@ object RagService {
}
@Serializable
class TradingDecision {
var corpName : String = ""
var stockName : String = ""
val ultraShortScore: Double = 0.0 // 초단기 (분봉/에너지)
val shortTermScore: Double = 0.0 // 단기 (일봉/뉴스)
val midTermScore: Double = 0.0 // 중기 (주봉/재무)
val longTermScore: Double = 0.0
// [추가] 화면 전환용 종목명
var currentPrice: Double = 0.0
var stockCode: String = ""
var decision: String? = null
var reason: String? = null
@@ -323,12 +335,18 @@ class TradingDecision {
fun profitPossible() =
listOf<Double>(ultraShortScore,
shortTermScore,
midTermScore,
longTermScore).average()
shortTermScore,
midTermScore,
longTermScore).average()
fun safePossible() =
listOf<Double>(
midTermScore,
longTermScore).average()
override fun toString(): String {
return """
$corpName($stockName)
수익실현 가능성 : ${profitPossible()}
ultraShortScore :$ultraShortScore
shortTermScore :$shortTermScore