This commit is contained in:
2026-02-06 17:53:17 +09:00
parent 0b855188a9
commit aa1f3817bb
9 changed files with 274 additions and 316 deletions
+28 -5
View File
@@ -11,6 +11,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import model.CandleData
import model.ExecutionData
@@ -22,6 +25,7 @@ import network.KisTradeService
import network.KisWebSocketManager
import service.AutoTradingManager
import service.TechnicalAnalyzer
import service.TradingDecisionCallback
import util.MarketUtil
import kotlin.collections.mutableListOf
@@ -46,11 +50,18 @@ fun DashboardScreen() {
var monthSummary by remember { mutableStateOf<MutableList<CandleData>>(mutableListOf()) }
var yearSummary by remember { mutableStateOf<MutableList<CandleData>>(mutableListOf()) }
DisposableEffect(Unit) {
// 1. 화면 진입 시: 자동 발굴 루프 시작
// AI 분석 결과(decision)가 나오면 completeTradingDecision 상태를 업데이트하여
// IntegratedOrderSection에서 자동으로 매수 로직이 실행되도록 연결합니다.
AutoTradingManager.startAutoDiscoveryLoop(tradeService) { decision, isSuccess ->
fun setupAutoTradingWatchdog(tradeService: KisTradeService, callback: TradingDecisionCallback) {
CoroutineScope(Dispatchers.Default).launch {
// while (true) {
// delay(60000) // 1분마다 체크
AutoTradingManager.checkAndRestart(tradeService, callback)
// }
}
}
var callback = object : TradingDecisionCallback {
override fun invoke(decision: TradingDecision?, isSuccess: Boolean) {
if (!isSuccess && decision?.confidence ?: 0.0 < 0.0) {
decision?.stockCode?.let { stockCode ->
decision?.stockName?.let { stockName ->
@@ -70,6 +81,13 @@ fun DashboardScreen() {
completeTradingDecision = decision
}
}
}
DisposableEffect(Unit) {
// 1. 화면 진입 시: 자동 발굴 루프 시작
// AI 분석 결과(decision)가 나오면 completeTradingDecision 상태를 업데이트하여
// IntegratedOrderSection에서 자동으로 매수 로직이 실행되도록 연결합니다.
AutoTradingManager.startAutoDiscoveryLoop(tradeService,callback)
// 2. 화면 이탈 시(앱 종료 등): 루프 중단 (리소스 정리)
onDispose {
@@ -83,6 +101,11 @@ fun DashboardScreen() {
val executionCache = remember { mutableMapOf<String, ExecutionData>() }
val processingIds = remember { mutableSetOf<String>() } // 주문번호 기준 잠금
// [중앙 관리 함수] 체결 정보와 DB 정보를 매칭하여 실행
LaunchedEffect(refreshTrigger) {
setupAutoTradingWatchdog(tradeService,callback)
}
suspend fun syncAndExecute(orderNo: String) {
if (processingIds.contains(orderNo)) return
processingIds.add(orderNo)
+3 -3
View File
@@ -177,13 +177,13 @@ fun IntegratedOrderSection(
// 토탈 스코어가 85점 이상이면 마진을 3.0으로 고정하거나 추가 가산(append) 적용
val finalMargin = if (totalScore >= HIGH_QUALITY_SCORE) {
println("💎 [우량주 포착] 토탈 스코어($totalScore)가 매우 높아 목표 마진을 3.0%로 상향합니다.")
minimumNetProfit + (append * 1.5)
minimumNetProfit * 1.5
} else {
minimumNetProfit + append
minimumNetProfit
}
println("🚀 [매수 진행] 토탈 스코어: ${String.format("%.1f", totalScore)} -> 종목: ${completeTradingDecision.stockCode}")
val MAX_BUDGET = 25000.0
val MAX_BUDGET = 30000.0
// basePrice(현재가 혹은 지정가)를 기준으로 매수 가능 수량 산출 (최소 1주 보장)
val calculatedQty = if (basePrice > 0) {
(MAX_BUDGET / basePrice).toInt().coerceAtLeast(1)