ㅎㅎㅎ

This commit is contained in:
2026-01-22 16:21:18 +09:00
parent dfc5de7cdc
commit 99804b892a
19 changed files with 714 additions and 178 deletions
@@ -0,0 +1,163 @@
package service
import TradingDecision
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import model.CandleData
import network.KisTradeService
import network.NewsService
import java.time.LocalTime
// service/AutoTradingManager.kt
object AutoTradingManager {
private val scope = CoroutineScope(Dispatchers.Default)
val targetStocks = mutableListOf<String>()
fun addStock(stockCode : String, result :(String, Boolean)->Unit) {
targetStocks.add(stockCode)
startTradingLoop(result)
}
fun startTradingLoop(result :(String, Boolean)->Unit) {
scope.launch {
println("🚀 10분 주기 자동 분석 및 매매 시작: ${LocalTime.now()}")
targetStocks.forEach { stockCode ->
launch { // 종목별 병렬 분석 (M3 Pro 파워 활용)
RagService.processStock(stockCode,result) {code ,decision ->
when (decision?.decision) {
"BUY" -> if (decision.confidence > 70) executeOrder(stockCode, "매수")
"SELL" -> executeOrder(stockCode, "매도")
else -> println("[$stockCode] 관망 유지: ${decision?.reason}")
}
result(decision.toString(),true)
}
}
}
delay(10 * 60 * 1000) // 10분 대기
}
}
private fun executeOrder(code: String, type: String) {
// 실제 증권사 API 호출 로직 (한국투자증권, 키움 등)
println("🔥 [주문 집행] $code $type 완료")
}
}
object TechnicalAnalyzer {
var monthly: List<CandleData> = emptyList()
var weekly: List<CandleData> = emptyList()
var daily: List<CandleData> = emptyList()
var min30: List<CandleData> = emptyList()
fun generateComprehensiveReport(): String {
// [1] 단기 에너지 지표 계산 (최근 30분봉 기준)
val obv = calculateOBV(min30)
val mfi = calculateMFI(min30, 14)
val adLine = calculateADLine(min30)
// [2] 시계열별 가격 변동 및 추세 요약
val m10 = min30.takeLast(10)
val change10 = calculateChange(m10)
val change30 = calculateChange(min30)
val changeDaily = calculateChange(daily.takeLast(2)) // 전일 대비
// [3] 이평선 및 가격 위치
val ma5 = m10.takeLast(5).map { it.stck_prpr.toDouble() }.average()
val currentPrice = min30.last().stck_prpr.toDouble()
// [4] 거래량 강도
val avgVol30 = min30.map { it.cntg_vol.toLong() }.average()
val recentVol5 = m10.takeLast(5).map { it.cntg_vol.toLong() }.average()
val volStrength = if (avgVol30 > 0) recentVol5 / avgVol30 else 1.0
return """
[종합 시계열 및 에너지 분석 보고서]
1. 가격 및 추세 현황
- 월봉/주봉 위치: ${if(calculateChange(monthly) > 0) "장기 상승" else "장기 하락"} / ${if(calculateChange(weekly) > 0) "중기 상승" else "중기 하락"}
- 일봉 대비: ${ "%.2f".format(changeDaily) }% 변동
- 30분 대비: ${ "%.2f".format(change30) }% 변동
- 10분 대비: ${ "%.2f".format(change10) }% 변동
- 이평선 상태: 현재가(${currentPrice.toInt()}) vs MA5(${ma5.toInt()}) -> ${if(currentPrice > ma5) "상단 위치" else "하단 위치"}
2. 자금 흐름 및 에너지 지표
- OBV (누적 거래량 에너지): ${ "%.0f".format(obv) } (${if(obv > 0) "누적 매수 우위" else "누적 매도 우위"})
- MFI (자금 유입 지수): ${ "%.1f".format(mfi) } (과매수 기준: 80 / 과매도 기준: 20)
- A/D (누적 분산 라인): ${ "%.0f".format(adLine) } (종가 형성 위치와 거래량 결합 수치)
- 거래량 강도: 최근 5분 평균이 30분 평균의 ${ "%.1f".format(volStrength) }배 수준
3. 가격 변동 범위
- 30분봉 최고가: ${min30.maxOf { it.stck_hgpr.toInt() }}
- 30분봉 최저가: ${min30.minOf { it.stck_lwpr.toInt() }}
- RSI(14): ${ "%.1f".format(calculateRSI(min30)) }
""".trimIndent()
}
private fun calculateChange(list: List<CandleData>): Double {
val start = list.first().stck_oprc.toDouble()
val end = list.last().stck_prpr.toDouble()
return if (start != 0.0) ((end - start) / start) * 100 else 0.0
}
private fun calculateRSI(list: List<CandleData>): Double {
if (list.size < 2) return 50.0
var gains = 0.0
var losses = 0.0
for (i in 1 until list.size) {
val diff = list[i].stck_prpr.toDouble() - list[i - 1].stck_prpr.toDouble()
if (diff > 0) gains += diff else losses -= diff
}
return if (gains + losses == 0.0) 50.0 else (gains / (gains + losses)) * 100
}
fun calculateOBV(candles: List<CandleData>): Double {
var obv = 0.0
for (i in 1 until candles.size) {
val prevClose = candles[i - 1].stck_prpr.toDouble()
val currClose = candles[i].stck_prpr.toDouble()
val currVol = candles[i].cntg_vol.toDouble()
when {
currClose > prevClose -> obv += currVol
currClose < prevClose -> obv -= currVol
}
}
return obv
}
/**
* MFI (Money Flow Index) 계산 (기간: 보통 14일)
*/
fun calculateMFI(candles: List<CandleData>, period: Int = 14): Double {
val subList = candles.takeLast(period + 1)
var posFlow = 0.0
var negFlow = 0.0
for (i in 1 until subList.size) {
val prevTypical = (subList[i-1].stck_hgpr.toDouble() + subList[i-1].stck_lwpr.toDouble() + subList[i-1].stck_prpr.toDouble()) / 3
val currTypical = (subList[i].stck_hgpr.toDouble() + subList[i].stck_lwpr.toDouble() + subList[i].stck_prpr.toDouble()) / 3
val moneyFlow = currTypical * subList[i].cntg_vol.toDouble()
if (currTypical > prevTypical) posFlow += moneyFlow
else if (currTypical < prevTypical) negFlow += moneyFlow
}
return if (negFlow == 0.0) 100.0 else 100 - (100 / (1+ (posFlow / negFlow)))
}
private fun calculateADLine(candles: List<CandleData>): Double {
var ad = 0.0
candles.forEach {
val high = it.stck_hgpr.toDouble(); val low = it.stck_lwpr.toDouble(); val close = it.stck_prpr.toDouble()
val mfv = if (high != low) ((close - low) - (high - close)) / (high - low) else 0.0
ad += mfv * it.cntg_vol.toDouble()
}
return ad
}
}
+40 -25
View File
@@ -1,25 +1,40 @@
package service
import network.NewsService
object StockAnalysisManager {
suspend fun analyzeStockWithRealTimeData(stockName: String, currentPrice: String): String {
println("🔍 [1/3] '${stockName}' 실시간 뉴스 수집 및 학습 시작...")
// 1. 실시간 뉴스 검색 및 DB 저장 (Embedding 서버 8081 활용)
// 키워드를 "종목명 주가 전망"으로 최적화하여 검색
NewsService.fetchAndIngestNews("$stockName 주가 전망")
println("🧠 [2/3] 관련 컨텍스트 추출 중...")
// 2. 방금 저장된 뉴스를 포함하여 DB에서 관련성 높은 정보 추출
val question = "${stockName}의 현재가 ${currentPrice}원 기준, 최근 뉴스 수급 상황과 향후 단기 전망을 분석해줘."
val context = RagService.askWithContext(question)
println("🤖 [3/3] AI 분석 생성 중 (Chat 서버 8080)...")
// 3. 최종 분석 결과 반환
return context
}
}
//package service
//
//import kotlinx.coroutines.async
//import kotlinx.coroutines.coroutineScope
//import model.CandleData
//import model.RealTimeTrade
//import network.NewsService
//
//object StockAnalysisManager {
// var days : List<CandleData> = emptyList()
// var weeks : List<CandleData> = emptyList()
// var monthly : List<CandleData> = emptyList()
// var mins : List<CandleData> = emptyList()
//
// suspend fun analyzeStockWithMultiData(stockCode : String, stockName: String, result : (String)-> Unit) {
// coroutineScope {
// println("🔍 [1/3] '${stockName}' 실시간 뉴스 수집 및 학습 시작...")
//
// val corpInfoDeferred = async { NewsService.fetchCorpInfo(stockCode) }
// val financialDataDeferred = async { NewsService.fetchFinancialGrowth(stockCode) }
//
// val corpInfo = corpInfoDeferred.await()
// val financialData = financialDataDeferred.await()
//
// NewsService.fetchAndIngestNews("$stockName 주가 전망")
//
// println("🧠 [2/3] 관련 컨텍스트 추출 중...")
//
// // 2. 방금 저장된 뉴스를 포함하여 DB에서 관련성 높은 정보 추출
// val question = "$stockCode 종목의 현재 주가 흐름과 뉴스, 재무 실적을 바탕으로 종합 투자 전략을 세워줘."
// val context = RagService.askWithContext(question,corpInfo,financialData,days,weeks,monthly)
//
// println("🤖 [3/3] AI 분석 생성 중 (Chat 서버 8080)...")
//
// // 3. 최종 분석 결과 반환
// result.invoke(context)
// }
// }
//
//}