ㅎㅎㅎ
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user