빨라져라
This commit is contained in:
@@ -1,302 +1,302 @@
|
||||
package ui
|
||||
|
||||
|
||||
|
||||
import network.TradingDecision
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
// 아래 두 import가 'delegate' 에러를 해결합니다.
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import model.CandleData
|
||||
import network.DartCodeManager
|
||||
import network.KisTradeService
|
||||
import network.KisWebSocketManager
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.collections.isNotEmpty
|
||||
|
||||
@Composable
|
||||
fun StockDetailSection(
|
||||
stockCode: String,
|
||||
stockName: String,
|
||||
holdingQuantity: String,
|
||||
isDomestic: Boolean,
|
||||
tradeService: KisTradeService,
|
||||
wsManager: KisWebSocketManager,
|
||||
onOrderSaved: (String) -> Unit,
|
||||
completeTradingDecision: TradingDecision?,
|
||||
min30 : MutableList<CandleData>,
|
||||
daySummary : MutableList<CandleData>,
|
||||
weekSummary : MutableList<CandleData>,
|
||||
monthSummary : MutableList<CandleData>,
|
||||
yearSummary : MutableList<CandleData>
|
||||
) {
|
||||
|
||||
// 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("") }
|
||||
var isSuccess by remember { mutableStateOf(true) }
|
||||
|
||||
|
||||
val todayOpen = remember(daySummary) {
|
||||
daySummary.lastOrNull()?.stck_oprc ?: "0"
|
||||
}
|
||||
val previousClose = remember(daySummary) {
|
||||
if (daySummary.size >= 2) daySummary[daySummary.size - 2].stck_prpr else "0"
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 이전 종목 코드를 기억하기 위한 상태
|
||||
var previousCode by remember { mutableStateOf("") }
|
||||
var lastPrice by remember { mutableStateOf("0") }
|
||||
|
||||
|
||||
// 종목 변경 시 데이터 로드 및 웹소켓 구독 관리
|
||||
LaunchedEffect(stockCode) {
|
||||
if (stockCode.isEmpty()) return@LaunchedEffect
|
||||
|
||||
isLoading = true
|
||||
|
||||
// 1. 웹소켓 구독 관리: 이전 종목 해제 -> 새 종목 구독
|
||||
if (previousCode.isNotEmpty()) {
|
||||
wsManager.unsubscribeStock(previousCode)
|
||||
}
|
||||
wsManager.clearData()
|
||||
wsManager.subscribeStock(stockCode)
|
||||
previousCode = stockCode
|
||||
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
lastPrice = currentPrice
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
launch {tradeService.fetchChartData(stockCode, isDomestic)
|
||||
.onSuccess { data ->
|
||||
// println("✅ 차트 데이터 로드 성공: ${data.size}개") // ${} 사용하여 정확히 출력
|
||||
chartData = data
|
||||
min30.clear()
|
||||
min30.addAll(chartData)
|
||||
}
|
||||
.onFailure { error ->
|
||||
// println("❌ 차트 데이터 로드 실패: ${error.localizedMessage}")
|
||||
chartData = emptyList()
|
||||
}
|
||||
}
|
||||
launch { tradeService.fetchPeriodChartData(stockCode, "D").onSuccess {
|
||||
daySummary.clear()
|
||||
daySummary.addAll(it)
|
||||
}
|
||||
} // 최근 7일
|
||||
launch { tradeService.fetchPeriodChartData(stockCode, "W").onSuccess {
|
||||
weekSummary.clear()
|
||||
weekSummary.addAll(it.takeLast(4))
|
||||
// println("weekSummary ${weekSummary.size} total: ${it.size} ${it.firstOrNull()?.toString()}")
|
||||
}
|
||||
} // 최근 4주
|
||||
launch { tradeService.fetchPeriodChartData(stockCode, "M").onSuccess {
|
||||
monthSummary.clear()
|
||||
monthSummary.addAll(it.takeLast(6))
|
||||
yearSummary.clear()
|
||||
yearSummary.addAll(it.takeLast(36))
|
||||
}
|
||||
}
|
||||
launch {
|
||||
DartCodeManager.getCorpCode(stockCode)?.let {
|
||||
it.stockName = stockName
|
||||
// NewsService.fetchAndIngestNews(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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()
|
||||
//package ui
|
||||
//
|
||||
// // 현재 시간(분 단위) 확인
|
||||
// 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",
|
||||
//
|
||||
//import network.TradingDecision
|
||||
//import androidx.compose.foundation.layout.*
|
||||
//import androidx.compose.material.*
|
||||
//import androidx.compose.runtime.*
|
||||
//// 아래 두 import가 'delegate' 에러를 해결합니다.
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import kotlinx.coroutines.coroutineScope
|
||||
//import kotlinx.coroutines.launch
|
||||
//import model.CandleData
|
||||
//import network.DartCodeManager
|
||||
//import network.KisTradeService
|
||||
//import network.KisWebSocketManager
|
||||
//import java.time.LocalTime
|
||||
//import java.time.format.DateTimeFormatter
|
||||
//import kotlin.collections.isNotEmpty
|
||||
//
|
||||
//@Composable
|
||||
//fun StockDetailSection(
|
||||
// stockCode: String,
|
||||
// stockName: String,
|
||||
// holdingQuantity: String,
|
||||
// isDomestic: Boolean,
|
||||
// tradeService: KisTradeService,
|
||||
// wsManager: KisWebSocketManager,
|
||||
// onOrderSaved: (String) -> Unit,
|
||||
// completeTradingDecision: TradingDecision?,
|
||||
// min30 : MutableList<CandleData>,
|
||||
// daySummary : MutableList<CandleData>,
|
||||
// weekSummary : MutableList<CandleData>,
|
||||
// monthSummary : MutableList<CandleData>,
|
||||
// yearSummary : MutableList<CandleData>
|
||||
//) {
|
||||
//
|
||||
//// 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("") }
|
||||
// var isSuccess by remember { mutableStateOf(true) }
|
||||
//
|
||||
//
|
||||
// val todayOpen = remember(daySummary) {
|
||||
// daySummary.lastOrNull()?.stck_oprc ?: "0"
|
||||
// }
|
||||
// val previousClose = remember(daySummary) {
|
||||
// if (daySummary.size >= 2) daySummary[daySummary.size - 2].stck_prpr else "0"
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// // 이전 종목 코드를 기억하기 위한 상태
|
||||
// var previousCode by remember { mutableStateOf("") }
|
||||
// var lastPrice by remember { mutableStateOf("0") }
|
||||
//
|
||||
//
|
||||
// // 종목 변경 시 데이터 로드 및 웹소켓 구독 관리
|
||||
// LaunchedEffect(stockCode) {
|
||||
// if (stockCode.isEmpty()) return@LaunchedEffect
|
||||
//
|
||||
// isLoading = true
|
||||
//
|
||||
// // 1. 웹소켓 구독 관리: 이전 종목 해제 -> 새 종목 구독
|
||||
// if (previousCode.isNotEmpty()) {
|
||||
// wsManager.unsubscribeStock(previousCode)
|
||||
// }
|
||||
// wsManager.clearData()
|
||||
// wsManager.subscribeStock(stockCode)
|
||||
// previousCode = stockCode
|
||||
//
|
||||
//
|
||||
// // 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
|
||||
// }
|
||||
// }
|
||||
// lastPrice = currentPrice
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// launch {tradeService.fetchChartData(stockCode, isDomestic)
|
||||
// .onSuccess { data ->
|
||||
//// println("✅ 차트 데이터 로드 성공: ${data.size}개") // ${} 사용하여 정확히 출력
|
||||
// chartData = data
|
||||
// min30.clear()
|
||||
// min30.addAll(chartData)
|
||||
// }
|
||||
// .onFailure { error ->
|
||||
//// println("❌ 차트 데이터 로드 실패: ${error.localizedMessage}")
|
||||
// chartData = emptyList()
|
||||
// }
|
||||
// }
|
||||
// launch { tradeService.fetchPeriodChartData(stockCode, "D").onSuccess {
|
||||
// daySummary.clear()
|
||||
// daySummary.addAll(it)
|
||||
// }
|
||||
// } // 최근 7일
|
||||
// launch { tradeService.fetchPeriodChartData(stockCode, "W").onSuccess {
|
||||
// weekSummary.clear()
|
||||
// weekSummary.addAll(it.takeLast(4))
|
||||
//// println("weekSummary ${weekSummary.size} total: ${it.size} ${it.firstOrNull()?.toString()}")
|
||||
// }
|
||||
// } // 최근 4주
|
||||
// launch { tradeService.fetchPeriodChartData(stockCode, "M").onSuccess {
|
||||
// monthSummary.clear()
|
||||
// monthSummary.addAll(it.takeLast(6))
|
||||
// yearSummary.clear()
|
||||
// yearSummary.addAll(it.takeLast(36))
|
||||
// }
|
||||
// }
|
||||
// launch {
|
||||
// DartCodeManager.getCorpCode(stockCode)?.let {
|
||||
// it.stockName = stockName
|
||||
//// NewsService.fetchAndIngestNews(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// isLoading = false
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//// 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)) {
|
||||
// // [상단] 종목명 및 상태 메시지
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// horizontalArrangement = Arrangement.SpaceBetween,
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// StockHeader(
|
||||
// name = stockName,
|
||||
// code = stockCode,
|
||||
// isDomestic = isDomestic,
|
||||
// previousClose = previousClose,
|
||||
// openPrice = lastPrice,
|
||||
// resultMessage = resultMessage,
|
||||
// resultMessageClear = {resultMessage = ""},
|
||||
// isSuccess = isSuccess
|
||||
// )
|
||||
//
|
||||
// // 실시간 가격 표시 (WebSocket 데이터)
|
||||
// Column(horizontalAlignment = Alignment.End) {
|
||||
// Text(
|
||||
// text = "${lastPrice} 원",
|
||||
// style = MaterialTheme.typography.h4,
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// color = if (lastPrice?.contains("-") ?: false) Color.Blue else Color.Red
|
||||
// )
|
||||
// // 최대 100개까지만 유지하여 성능 최적화
|
||||
// chartData = (chartData + newCandle).takeLast(100)
|
||||
// Text("실시간 체결가", style = MaterialTheme.typography.caption, color = Color.Gray)
|
||||
// }
|
||||
// }
|
||||
// // 통합된 트렌드 카드 배치
|
||||
// Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
// PeriodTrendCard("7일", daySummary, Modifier.weight(1f))
|
||||
// PeriodTrendCard("4주", weekSummary, Modifier.weight(1f))
|
||||
// PeriodTrendCard("6개월", monthSummary, Modifier.weight(1f))
|
||||
// PeriodTrendCard("3년", yearSummary, Modifier.weight(1f))
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
// // [중앙] 캔들 차트 (Card 내부)
|
||||
// Card(
|
||||
// modifier = Modifier.fillMaxWidth().height(320.dp),
|
||||
// backgroundColor = Color(0xFF121212)
|
||||
// ) {
|
||||
// if (isLoading) {
|
||||
// Box(contentAlignment = Alignment.Center) { CircularProgressIndicator(color = Color.White) }
|
||||
// } 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
|
||||
// CandleChart(data = chartData, modifier = Modifier.padding(16.dp))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
//
|
||||
//
|
||||
//
|
||||
// // [하단] 실시간 체결 내역 및 주문 섹션
|
||||
// Row(modifier = Modifier.weight(1f)) {
|
||||
// // 실시간 체결 리스트
|
||||
// Column(modifier = Modifier.weight(1f)) {
|
||||
// Text("실시간 체결", style = MaterialTheme.typography.subtitle2, fontWeight = FontWeight.Bold)
|
||||
// RealTimeTradeList(wsManager.tradeLogs)
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
//
|
||||
// // 주문 섹션 (인자 간소화)
|
||||
// Column(modifier = Modifier.weight(0.6f)) {
|
||||
// IntegratedOrderSection(
|
||||
// stockCode = stockCode,
|
||||
// stockName = stockName,
|
||||
// isDomestic = isDomestic,
|
||||
// currentPrice = lastPrice,
|
||||
// holdingQuantity = holdingQuantity,
|
||||
// tradeService = tradeService,
|
||||
// onOrderSaved = onOrderSaved,
|
||||
// onOrderResult = { msg, success ->
|
||||
// resultMessage = msg
|
||||
// isSuccess = success
|
||||
// },
|
||||
// completeTradingDecision
|
||||
// )
|
||||
// chartData = chartData.dropLast(1) + updatedCandle
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||
// [상단] 종목명 및 상태 메시지
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
StockHeader(
|
||||
name = stockName,
|
||||
code = stockCode,
|
||||
isDomestic = isDomestic,
|
||||
previousClose = previousClose,
|
||||
openPrice = lastPrice,
|
||||
resultMessage = resultMessage,
|
||||
resultMessageClear = {resultMessage = ""},
|
||||
isSuccess = isSuccess
|
||||
)
|
||||
|
||||
// 실시간 가격 표시 (WebSocket 데이터)
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = "${lastPrice} 원",
|
||||
style = MaterialTheme.typography.h4,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = if (lastPrice?.contains("-") ?: false) Color.Blue else Color.Red
|
||||
)
|
||||
Text("실시간 체결가", style = MaterialTheme.typography.caption, color = Color.Gray)
|
||||
}
|
||||
}
|
||||
// 통합된 트렌드 카드 배치
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
PeriodTrendCard("7일", daySummary, Modifier.weight(1f))
|
||||
PeriodTrendCard("4주", weekSummary, Modifier.weight(1f))
|
||||
PeriodTrendCard("6개월", monthSummary, Modifier.weight(1f))
|
||||
PeriodTrendCard("3년", yearSummary, Modifier.weight(1f))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
// [중앙] 캔들 차트 (Card 내부)
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth().height(320.dp),
|
||||
backgroundColor = Color(0xFF121212)
|
||||
) {
|
||||
if (isLoading) {
|
||||
Box(contentAlignment = Alignment.Center) { CircularProgressIndicator(color = Color.White) }
|
||||
} else {
|
||||
CandleChart(data = chartData, modifier = Modifier.padding(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
|
||||
|
||||
// [하단] 실시간 체결 내역 및 주문 섹션
|
||||
Row(modifier = Modifier.weight(1f)) {
|
||||
// 실시간 체결 리스트
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text("실시간 체결", style = MaterialTheme.typography.subtitle2, fontWeight = FontWeight.Bold)
|
||||
RealTimeTradeList(wsManager.tradeLogs)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
// 주문 섹션 (인자 간소화)
|
||||
Column(modifier = Modifier.weight(0.6f)) {
|
||||
IntegratedOrderSection(
|
||||
stockCode = stockCode,
|
||||
stockName = stockName,
|
||||
isDomestic = isDomestic,
|
||||
currentPrice = lastPrice,
|
||||
holdingQuantity = holdingQuantity,
|
||||
tradeService = tradeService,
|
||||
onOrderSaved = onOrderSaved,
|
||||
onOrderResult = { msg, success ->
|
||||
resultMessage = msg
|
||||
isSuccess = success
|
||||
},
|
||||
completeTradingDecision
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PeriodSummaryCard(label: String, avgPrice: String, modifier: Modifier = Modifier) {
|
||||
Card(modifier = modifier, elevation = 2.dp, backgroundColor = Color.White) {
|
||||
Column(modifier = Modifier.padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(label, fontSize = 10.sp, color = Color.Gray)
|
||||
Text(text = "${avgPrice}원", fontSize = 13.sp, fontWeight = FontWeight.Bold, color = Color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
//
|
||||
//@Composable
|
||||
//fun PeriodSummaryCard(label: String, avgPrice: String, modifier: Modifier = Modifier) {
|
||||
// Card(modifier = modifier, elevation = 2.dp, backgroundColor = Color.White) {
|
||||
// Column(modifier = Modifier.padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
// Text(label, fontSize = 10.sp, color = Color.Gray)
|
||||
// Text(text = "${avgPrice}원", fontSize = 13.sp, fontWeight = FontWeight.Bold, color = Color.Black)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
Reference in New Issue
Block a user