From 5c088414c5d2e19e122c8a78aef53edb2940f3ec Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Tue, 20 Jan 2026 15:13:50 +0900 Subject: [PATCH] ... --- src/main/kotlin/model/StockModels.kt | 13 +++++++--- .../kotlin/network/KisWebSocketManager.kt | 8 +++++- src/main/kotlin/ui/AutoTradeSection.kt | 25 +++++++++++++++---- src/main/kotlin/ui/DashboardScreen.kt | 22 +++++++++++++--- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/model/StockModels.kt b/src/main/kotlin/model/StockModels.kt index 5258ebf..e089e90 100644 --- a/src/main/kotlin/model/StockModels.kt +++ b/src/main/kotlin/model/StockModels.kt @@ -123,11 +123,16 @@ data class UnifiedBalance( @Serializable data class UnfilledOrder( val orgn_odno: String, - @SerialName("odno") val ord_no: String, // JSON의 odno를 ord_no로 매핑 + @SerialName("odno") + val ord_no: String, // JSON의 odno를 ord_no로 매핑 val pdno: String, - @SerialName("prdt_name") val prdt_name: String, + @SerialName("prdt_name") + val prdt_name: String, val ord_unpr: String, // JSON이 문자열이므로 String 권장 - @SerialName("psbl_qty") val rmnd_qty: String, // JSON의 psbl_qty를 rmnd_qty로 매핑 + val ord_qty : String, + + @SerialName("psbl_qty") + val rmnd_qty: String, // JSON의 psbl_qty를 rmnd_qty로 매핑 val ord_dvsn_name: String, val rvse_cncl_dvsn_name: String ) @@ -145,7 +150,7 @@ fun UnfilledOrder.toAutoTradeItem(isDomestic: Boolean): AutoTradeItem { code = this.pdno, name = this.prdt_name, orderedPrice = this.ord_unpr.toDoubleOrNull() ?: 0.0, - quantity = 0, // 미체결 내역에서는 원 주문 수량을 알기 어려우므로 0 또는 별도 처리 + quantity = this.ord_qty.toIntOrNull() ?: 0, // 미체결 내역에서는 원 주문 수량을 알기 어려우므로 0 또는 별도 처리 remainedQuantity = this.rmnd_qty.toIntOrNull() ?: 0, status = "PENDING_BUY", // 기본적으로 미체결은 매수/매도 대기 상태 isDomestic = isDomestic diff --git a/src/main/kotlin/network/KisWebSocketManager.kt b/src/main/kotlin/network/KisWebSocketManager.kt index 614a0ee..0fd7eed 100644 --- a/src/main/kotlin/network/KisWebSocketManager.kt +++ b/src/main/kotlin/network/KisWebSocketManager.kt @@ -79,7 +79,13 @@ class KisWebSocketManager { )) if (tradeLogs.size > 50) tradeLogs.removeLast() } - "H0STT084R" -> onExecutionReceived?.invoke(dataRows[5], dataRows[9], dataRows[12], dataRows[13], dataRows[15] == "02") + "H0STT084R" -> { + println("채결 데이터") + onExecutionReceived?.invoke(dataRows[5], dataRows[9], dataRows[12], dataRows[13], dataRows[15] == "02") + } + else -> { + println("쓰레기? ${trId}") + } } } fun clearData() { diff --git a/src/main/kotlin/ui/AutoTradeSection.kt b/src/main/kotlin/ui/AutoTradeSection.kt index d36e161..a20971b 100644 --- a/src/main/kotlin/ui/AutoTradeSection.kt +++ b/src/main/kotlin/ui/AutoTradeSection.kt @@ -33,17 +33,32 @@ fun AutoTradeSection( var tradeList by remember { mutableStateOf(emptyList()) } // refreshTrigger가 바뀔 때마다 실행됨 LaunchedEffect(refreshTrigger) { + // 1. 서버에서 실제 미체결 내역 가져오기 val serverUnfilled = tradeService.fetchUnfilledOrders().getOrNull()?.map { it.toAutoTradeItem(isDomestic) } ?: emptyList() // 2. DB에서 로컬 감시 데이터 가져오기 val localTrades = DatabaseFactory.getActiveAutoTrades() - // 3. 동기화 로직: 서버에 없는 주문번호를 가진 로컬 데이터는 EXPIRED로 표시 - tradeList = localTrades.map { local -> - if (local.status != "COMPLETED" && serverUnfilled.none { it.orderNo == local.orderNo }) { - local.copy(status = "EXPIRED") - } else local + // 3. 리스트 병합 및 동기화 + val mergedList = mutableListOf() + + // (A) DB에 있는 항목 처리 + localTrades.forEach { local -> + val serverMatch = serverUnfilled.find { it.orderNo == local.orderNo } + if (local.status != "COMPLETED" && serverMatch == null) { + // 서버에 없으면 만료 처리 + mergedList.add(local.copy(status = "EXPIRED")) + } else { + // 서버에 있으면 그대로 표시 (필요시 잔량 등 업데이트) + mergedList.add(local.copy(remainedQuantity = serverMatch?.remainedQuantity ?: 0)) + } } + + // (B) 서버에는 있지만 DB에는 없는 항목(수동 주문 등) 추가 + val manualOrders = serverUnfilled.filter { server -> localTrades.none { it.orderNo == server.orderNo } } + mergedList.addAll(manualOrders.map { it.copy(status = "MANUAL_ORDER") }) // 수동 주문 상태 등으로 표시 + + tradeList = mergedList } Column(modifier = Modifier.fillMaxSize().padding(8.dp)) { diff --git a/src/main/kotlin/ui/DashboardScreen.kt b/src/main/kotlin/ui/DashboardScreen.kt index c1d4240..dd5b21e 100644 --- a/src/main/kotlin/ui/DashboardScreen.kt +++ b/src/main/kotlin/ui/DashboardScreen.kt @@ -61,9 +61,25 @@ fun DashboardScreen() { if (dbItem != null) { when (dbItem.status) { TradeStatus.PENDING_BUY -> { - // 매수 주문 체결 -> 감시(MONITORING)로 전환 및 익절 주문 발주 - // 여기서 익절 주문 후 받은 신규 주문번호를 DB에 갱신 - // updateStatusAndOrderNo(dbItem.id!!, TradeStatus.MONITORING, newSellOrderNo) + // 1. 매수 주문 체결 확인됨 -> 즉시 익절 매도 주문 발주 + println("✅ 매수 체결 확인 [${dbItem.name}]: 익절가 ${dbItem.targetPrice}로 매도 주문을 생성합니다.") + tradeService.postOrder( + stockCode = dbItem.code, + qty = dbItem.quantity.toString(), + price = dbItem.targetPrice.toLong().toString(), // 가격은 정수형 문자열로 전달 + isBuy = false + ).onSuccess { newSellOrderNo -> + // 2. 매도 주문 성공 시 DB 상태를 SELLING으로 변경하고 새로운 주문번호로 갱신 + DatabaseFactory.updateStatusAndOrderNo( + id = dbItem.id!!, + newStatus = TradeStatus.SELLING, + newOrderNo = newSellOrderNo + ) + println("🚀 익절 매도 주문 완료: 주문번호 $newSellOrderNo") + refreshTrigger++ // UI 갱신 + }.onFailure { + println("❌ 매수 체결 후 익절 주문 발주 실패: ${it.message}") + } } TradeStatus.SELLING -> { // 매도(손절/익절) 주문 체결 -> COMPLETED