This commit is contained in:
2026-02-10 15:08:52 +09:00
parent 4dff629861
commit 4bf055fa68
12 changed files with 302 additions and 399 deletions
-116
View File
@@ -1,116 +0,0 @@
//package ui
//
//import AutoTradeItem
//import androidx.compose.foundation.background
//import androidx.compose.foundation.clickable
//import androidx.compose.foundation.layout.*
//import androidx.compose.foundation.lazy.LazyColumn
//import androidx.compose.foundation.lazy.items // 반드시 수동 import 확인
//import androidx.compose.foundation.shape.RoundedCornerShape
//import androidx.compose.material.*
//import androidx.compose.runtime.*
//import io.ktor.client.engine.cio.CIO
//// 아래 두 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.text.style.TextAlign
//import androidx.compose.ui.text.style.TextOverflow
//import androidx.compose.ui.unit.dp
//import androidx.compose.ui.unit.sp
//import kotlinx.coroutines.delay
//import kotlinx.coroutines.launch
//import model.AppConfig
//import model.BalanceSummary
//import model.CandleData
//import model.RankingStock
//import model.StockHolding
//import network.KisTradeService
//import network.KisWebSocketManager
//import kotlin.collections.isNotEmpty
//
//
//@Composable
//fun AutoTradeSettingCard(
// stockCode: String,
// stockName: String, // 종목명 추가
// currentPrice: String,
// isDomestic: Boolean = true
//) {
// var profitRate by remember { mutableStateOf("5.0") }
// var stopLossRate by remember { mutableStateOf("-3.0") }
//
// // DB에서 현재 감시 중인지 확인
// var isEnabled by remember(stockCode) {
// mutableStateOf(DatabaseFactory.findConfigByCode(stockCode) != null)
// }
//
// Card(
// elevation = 4.dp,
// shape = RoundedCornerShape(8.dp),
// backgroundColor = Color(0xFFF8F9FA)
// ) {
// Column(modifier = Modifier.padding(12.dp)) {
// Text("자동 매도 설정 (AI 감시)", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.subtitle2)
// Spacer(modifier = Modifier.height(8.dp))
//
// Row(verticalAlignment = Alignment.CenterVertically) {
// OutlinedTextField(
// value = profitRate,
// onValueChange = { profitRate = it },
// label = { Text("익절 %") },
// modifier = Modifier.weight(1f).padding(end = 4.dp),
// textStyle = androidx.compose.ui.text.TextStyle(fontSize = 12.sp)
// )
// OutlinedTextField(
// value = stopLossRate,
// onValueChange = { stopLossRate = it },
// label = { Text("손절 %") },
// modifier = Modifier.weight(1f),
// textStyle = androidx.compose.ui.text.TextStyle(fontSize = 12.sp)
// )
// }
//
// Spacer(modifier = Modifier.height(8.dp))
//
// Button(
// onClick = {
// if (!isEnabled) {
// // 자동 매매 시작: DB 저장
// val curPriceNum = currentPrice.replace(",", "").toDoubleOrNull() ?: 0.0
// val target = curPriceNum * (1 + profitRate.toDouble() / 100.0)
// val stopLoss = curPriceNum * (1 + stopLossRate.toDouble() / 100.0)
//
// DatabaseFactory.saveAutoTrade(
// AutoTradeItem(
// code = stockCode,
// name = stockName,
// targetPrice = target,
// stopLossPrice = stopLoss,
// status = "MONITORING",
// isDomestic = isDomestic
// )
// )
// // [중요] 웹소켓 실시간 감시 등록 로직이 이곳에 호출되어야 함
// // KisWebSocketManager.subscribe(stockCode)
// isEnabled = true
// } else {
// // 자동 매매 중단: DB 삭제
// DatabaseFactory.deleteAutoTrade(stockCode)
// // KisWebSocketManager.unsubscribe(stockCode)
// isEnabled = false
// }
// },
// modifier = Modifier.fillMaxWidth(),
// colors = ButtonDefaults.buttonColors(
// backgroundColor = if (isEnabled) Color(0xFFE03E2D) else Color(0xFF0E62CF)
// )
// ) {
// Text(if (isEnabled) "자동 매매 중단" else "자동 매매 시작", color = Color.White)
// }
// }
// }
//}
+17 -15
View File
@@ -20,6 +20,7 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import model.MAX_BUDGET
import model.buyWeight
import model.feesAndTaxRate
import model.minimumNetProfit
@@ -31,42 +32,48 @@ enum class InvestmentGrade(
val description: String,
val shortWeight: Double = 0.0,
val midWeight: Double = 0.0,
val longWeight: Double = 0.0
val longWeight: Double = 0.0,
val profitGuide: Double = 0.0,
) {
LEVEL_5_STRONG_RECOMMEND(
displayName = "최상급 추천",
description = "단기·중기·장기 모두 우수하고, 신뢰도 매우 높은 범용 매수 추천",
shortWeight = 1.0,
midWeight = 1.0,
longWeight = 1.0
longWeight = 1.0,
profitGuide = 1.8,
),
LEVEL_4_BALANCED_RECOMMEND(
displayName = "균형 추천",
description = "중기·장기 기본은 양호하고, 단기 성과도 준수한 안정형 추천",
shortWeight = 0.8,
midWeight = 1.0,
longWeight = 1.0
longWeight = 1.0,
profitGuide = 1.4,
),
LEVEL_3_CAUTIOUS_RECOMMEND(
displayName = "보수적 추천",
description = "중기/장기 기본은 양호하지만, 단기 변동성이 높아 신중히 접근해야 함",
shortWeight = 0.6,
midWeight = 1.0,
longWeight = 1.0
longWeight = 1.0,
profitGuide = 1.0,
),
LEVEL_2_HIGH_RISK(
displayName = "고위험 추천",
description = "단기/초단기 성과만 강하고, 중기·장기가 애매하여 리스크가 큰 투자",
shortWeight = 1.0,
midWeight = 0.4,
longWeight = 0.4
longWeight = 0.4,
profitGuide = 0.8,
),
LEVEL_1_SPECULATIVE(
displayName = "순수 공격적 선택",
description = "단기/초단기 성과에만 의존하는 단기 급등형 공격적 투자",
shortWeight = 1.0,
midWeight = 0.2,
longWeight = 0.2
longWeight = 0.2,
profitGuide = 0.6,
)
}
@@ -268,18 +275,13 @@ fun IntegratedOrderSection(
totalScore : ${totalScore}
""".trimIndent())
if (totalScore >= MIN_PURCHASE_SCORE && completeTradingDecision.confidence >= MIN_CONFIDENCE) {
var investmentGrade : InvestmentGrade = getInvestmentGrade(completeTradingDecision,totalScore, completeTradingDecision.confidence)
// 4. 점수에 따른 가변 마진 적용
// 토탈 스코어가 85점 이상이면 마진을 3.0으로 고정하거나 추가 가산(append) 적용
val finalMargin = if (totalScore >= HIGH_QUALITY_SCORE) {
println("💎 [우량주 포착] 토탈 스코어($totalScore)가 매우 높아 목표 마진을 3.0%로 상향합니다.")
minimumNetProfit * 1.5
} else {
minimumNetProfit
}
val finalMargin = minimumNetProfit * investmentGrade.profitGuide
println("🚀 [매수 진행] 토탈 스코어: ${String.format("%.1f", totalScore)} -> 종목: ${completeTradingDecision.stockCode}")
val MAX_BUDGET = 35000.0
// basePrice(현재가 혹은 지정가)를 기준으로 매수 가능 수량 산출 (최소 1주 보장)
val calculatedQty = if (basePrice > 0) {
(MAX_BUDGET / basePrice).toInt().coerceAtLeast(1)
@@ -291,7 +293,7 @@ fun IntegratedOrderSection(
willEnableAutoSell = true,
orderQty = calculatedQty.toString(),
profitRate1 = finalMargin,
investmentGrade = getInvestmentGrade(completeTradingDecision,totalScore, completeTradingDecision.confidence),
investmentGrade = investmentGrade,
)
} else {