This commit is contained in:
2026-02-12 15:31:34 +09:00
parent da4ab01d5f
commit ad5e7e0ccb
8 changed files with 189 additions and 111 deletions
+90 -42
View File
@@ -21,8 +21,6 @@ import model.CandleData
import network.DartCodeManager
import network.KisTradeService
import network.KisWebSocketManager
import network.NewsService
import service.TechnicalAnalyzer
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import kotlin.collections.isNotEmpty
@@ -44,7 +42,7 @@ fun StockDetailSection(
yearSummary : MutableList<CandleData>
) {
var openPrice by remember { mutableStateOf("0") }
// var openPrice by remember { mutableStateOf("0") }
var chartData by remember { mutableStateOf<List<CandleData>>(emptyList()) }
var isLoading by remember { mutableStateOf(false) }
var resultMessage by remember { mutableStateOf("") }
@@ -62,6 +60,8 @@ fun StockDetailSection(
// 이전 종목 코드를 기억하기 위한 상태
var previousCode by remember { mutableStateOf("") }
var latestPrice by remember { mutableStateOf("0") }
// 종목 변경 시 데이터 로드 및 웹소켓 구독 관리
LaunchedEffect(stockCode) {
@@ -81,16 +81,62 @@ fun StockDetailSection(
// 2. 차트 데이터 로드 (KisSession 기반으로 파라미터 간소화)
coroutineScope {
launch {
wsManager.onPriceUpdate = {tradeLog ->
if (tradeLog.code.equals(stockCode)) {
val code = tradeLog.code
val price = tradeLog.price
wsManager.tradeLogs.add(tradeLog)
if (wsManager.tradeLogs.size > 50) wsManager.tradeLogs.removeLast()
println("code $code ,price $price")
val currentPrice = price
if (chartData.isNotEmpty() && currentPrice != "0") {
val priceDouble = currentPrice.replace(",", "").toDoubleOrNull() ?: 0.0
val lastCandle = chartData.last()
// 현재 시간(분 단위) 확인
val currentMinute = LocalTime.now().format(DateTimeFormatter.ofPattern("HHmm00"))
if (lastCandle.stck_bsop_date != currentMinute) {
// [개선] 시간이 바뀌었으면 새로운 캔들 추가 (차트가 밀려나는 효과)
val newCandle = CandleData(
stck_bsop_date = currentMinute,
stck_oprc = currentPrice,
stck_hgpr = currentPrice,
stck_lwpr = currentPrice,
stck_prpr = currentPrice,
stck_cntg_hour = currentMinute,
cntg_vol = "1",
acml_tr_pbmn = "1",
)
// 최대 100개까지만 유지하여 성능 최적화
chartData = (chartData + newCandle).takeLast(100)
} else {
// 같은 분 내에서는 기존 마지막 캔들만 업데이트
val updatedCandle = lastCandle.copy(
stck_prpr = currentPrice,
stck_hgpr = if (priceDouble > (lastCandle.stck_hgpr.toDoubleOrNull() ?: 0.0)) currentPrice else lastCandle.stck_hgpr,
stck_lwpr = if (priceDouble < (lastCandle.stck_lwpr.toDoubleOrNull() ?: Double.MAX_VALUE)) currentPrice else lastCandle.stck_lwpr
)
chartData = chartData.dropLast(1) + updatedCandle
}
}
latestPrice = currentPrice
}
}
}
launch {tradeService.fetchChartData(stockCode, isDomestic)
.onSuccess { data ->
println("✅ 차트 데이터 로드 성공: ${data.size}") // ${} 사용하여 정확히 출력
// println("✅ 차트 데이터 로드 성공: ${data.size}개") // ${} 사용하여 정확히 출력
chartData = data
min30.clear()
min30.addAll(chartData)
}
.onFailure { error ->
println("❌ 차트 데이터 로드 실패: ${error.localizedMessage}")
// println("❌ 차트 데이터 로드 실패: ${error.localizedMessage}")
chartData = emptyList()
}
}
@@ -122,41 +168,43 @@ fun StockDetailSection(
isLoading = false
}
val latestPrice by wsManager.currentPrice // 웹소켓에서 업데이트되는 현재가
LaunchedEffect(latestPrice) {
if (chartData.isNotEmpty() && latestPrice != "0") {
val priceDouble = latestPrice.replace(",", "").toDoubleOrNull() ?: return@LaunchedEffect
val lastCandle = chartData.last()
// 현재 시간(분 단위) 확인
val currentMinute = LocalTime.now().format(DateTimeFormatter.ofPattern("HHmm00"))
if (lastCandle.stck_bsop_date != currentMinute) {
// [개선] 시간이 바뀌었으면 새로운 캔들 추가 (차트가 밀려나는 효과)
val newCandle = CandleData(
stck_bsop_date = currentMinute,
stck_oprc = latestPrice,
stck_hgpr = latestPrice,
stck_lwpr = latestPrice,
stck_prpr = latestPrice,
stck_cntg_hour = currentMinute,
cntg_vol = "1",
acml_tr_pbmn = "1",
)
// 최대 100개까지만 유지하여 성능 최적화
chartData = (chartData + newCandle).takeLast(100)
} else {
// 같은 분 내에서는 기존 마지막 캔들만 업데이트
val updatedCandle = lastCandle.copy(
stck_prpr = latestPrice,
stck_hgpr = if (priceDouble > (lastCandle.stck_hgpr.toDoubleOrNull() ?: 0.0)) latestPrice else lastCandle.stck_hgpr,
stck_lwpr = if (priceDouble < (lastCandle.stck_lwpr.toDoubleOrNull() ?: Double.MAX_VALUE)) latestPrice else lastCandle.stck_lwpr
)
chartData = chartData.dropLast(1) + updatedCandle
}
}
}
// LaunchedEffect(latestPrice) {
// println("latestPrice >>> $latestPrice")
// if (chartData.isNotEmpty() && latestPrice != "0") {
// val latestPrice = latestPrice ?: "0"
// val priceDouble = latestPrice?.replace(",", "")?.toDoubleOrNull() ?: return@LaunchedEffect
// val lastCandle = chartData.last()
//
// // 현재 시간(분 단위) 확인
// val currentMinute = LocalTime.now().format(DateTimeFormatter.ofPattern("HHmm00"))
//
// if (lastCandle.stck_bsop_date != currentMinute) {
// // [개선] 시간이 바뀌었으면 새로운 캔들 추가 (차트가 밀려나는 효과)
// val newCandle = CandleData(
// stck_bsop_date = currentMinute,
// stck_oprc = latestPrice,
// stck_hgpr = latestPrice,
// stck_lwpr = latestPrice,
// stck_prpr = latestPrice,
// stck_cntg_hour = currentMinute,
// cntg_vol = "1",
// acml_tr_pbmn = "1",
// )
// // 최대 100개까지만 유지하여 성능 최적화
// chartData = (chartData + newCandle).takeLast(100)
// } else {
// // 같은 분 내에서는 기존 마지막 캔들만 업데이트
// val updatedCandle = lastCandle.copy(
// stck_prpr = latestPrice,
// stck_hgpr = if (priceDouble > (lastCandle.stck_hgpr.toDoubleOrNull() ?: 0.0)) latestPrice else lastCandle.stck_hgpr,
// stck_lwpr = if (priceDouble < (lastCandle.stck_lwpr.toDoubleOrNull() ?: Double.MAX_VALUE)) latestPrice else lastCandle.stck_lwpr
// )
// chartData = chartData.dropLast(1) + updatedCandle
// }
// }
// }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// [상단] 종목명 및 상태 메시지
@@ -170,7 +218,7 @@ fun StockDetailSection(
code = stockCode,
isDomestic = isDomestic,
previousClose = previousClose,
openPrice = openPrice,
openPrice = latestPrice,
resultMessage = resultMessage,
resultMessageClear = {resultMessage = ""},
isSuccess = isSuccess
@@ -179,10 +227,10 @@ fun StockDetailSection(
// 실시간 가격 표시 (WebSocket 데이터)
Column(horizontalAlignment = Alignment.End) {
Text(
text = "${wsManager.currentPrice.value}",
text = "${latestPrice}",
style = MaterialTheme.typography.h4,
fontWeight = FontWeight.Bold,
color = if (wsManager.currentPrice.value.contains("-")) Color.Blue else Color.Red
color = if (latestPrice?.contains("-") ?: false) Color.Blue else Color.Red
)
Text("실시간 체결가", style = MaterialTheme.typography.caption, color = Color.Gray)
}
@@ -228,7 +276,7 @@ fun StockDetailSection(
stockCode = stockCode,
stockName = stockName,
isDomestic = isDomestic,
currentPrice = wsManager.currentPrice.value,
currentPrice = latestPrice,
holdingQuantity = holdingQuantity,
tradeService = tradeService,
onOrderSaved = onOrderSaved,