....
This commit is contained in:
@@ -196,7 +196,7 @@ object AutoTradingManager {
|
||||
ts: TradingDecision,
|
||||
totalScore: Double,
|
||||
confidence: Double,
|
||||
finScore100: Double // 💡 [수정1] 컴파일 에러 방지용 파라미터 추가
|
||||
finScore100: Double
|
||||
): InvestmentGrade {
|
||||
val minScore = KisSession.config.getValues(ConfigIndex.MIN_PURCHASE_SCORE_INDEX)
|
||||
val minConfidence = minScore
|
||||
@@ -209,51 +209,52 @@ object AutoTradingManager {
|
||||
val midLongAvg = (ts.midTermScore + ts.longTermScore) / 2.0
|
||||
val isOverheated = ts.analyzer?.isOverheatedStock() ?: true
|
||||
|
||||
// 1. 기본 등급 산정
|
||||
// 🌟 1. 기본 등급 산정 (현실적인 통계 범위로 컷오프 하향 조정)
|
||||
var rawGrade = when {
|
||||
midLongAvg >= 70.0 -> {
|
||||
if (shortAvg >= 75.0) InvestmentGrade.LEVEL_5_STRONG_RECOMMEND
|
||||
else if (shortAvg >= 65.0) InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
else InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
|
||||
midLongAvg >= 65.0 -> { // 우량한 중장기 흐름 (기존 70 -> 65 완화)
|
||||
if (shortAvg >= 65.0) InvestmentGrade.LEVEL_5_STRONG_RECOMMEND // 강한 턴어라운드/수급 (기존 75 -> 65 완화)
|
||||
else if (shortAvg >= 50.0) InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
else InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND // 눌림목 진입 초기
|
||||
}
|
||||
|
||||
midLongAvg >= 60.0 -> {
|
||||
if (shortAvg >= 70.0) InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
else if (shortAvg >= 60.0) InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
|
||||
midLongAvg >= 55.0 -> { // 무난한 흐름
|
||||
if (shortAvg >= 65.0) InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
else if (shortAvg >= 50.0) InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
|
||||
else InvestmentGrade.LEVEL_2_HIGH_RISK
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (shortAvg >= 70.0) InvestmentGrade.LEVEL_2_HIGH_RISK
|
||||
else -> { // 역배열 또는 재무 하위
|
||||
if (shortAvg >= 65.0) InvestmentGrade.LEVEL_2_HIGH_RISK
|
||||
else InvestmentGrade.LEVEL_1_SPECULATIVE
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 [수정2] 누락되었던 우량주 눌림목 프리미엄 & 잡주 투매 회피 로직 추가
|
||||
val isHealthy = finScore100 >= 70.0
|
||||
val isPullback = midLongAvg >= 75.0 && shortAvg <= 45.0
|
||||
// 🌟 2. [핵심] 우량주 황금 눌림목 프리미엄 (Premium)
|
||||
// 재무가 훌륭(75점 이상)하고 중장기 추세가 살아있는데, 단기 지표가 바닥을 찍고 막 고개를 드는(30~55점) 타점
|
||||
val isHealthy = finScore100 >= 75.0
|
||||
val isPullback = midLongAvg >= 70.0 && shortAvg in 30.0..55.0
|
||||
|
||||
if (isHealthy && isPullback) {
|
||||
rawGrade = when (rawGrade) {
|
||||
InvestmentGrade.LEVEL_1_SPECULATIVE,
|
||||
InvestmentGrade.LEVEL_2_HIGH_RISK -> InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
|
||||
|
||||
// 💡 우량주가 완벽한 눌림목에서 반등을 시작하면 최고 등급(5등급)으로 승격!
|
||||
InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND -> InvestmentGrade.LEVEL_5_STRONG_RECOMMEND
|
||||
InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND -> InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
InvestmentGrade.LEVEL_2_HIGH_RISK -> InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
|
||||
else -> rawGrade
|
||||
}
|
||||
} else if (!isHealthy && isPullback) {
|
||||
// 잡주인데 차트가 눌러앉은 경우 (지하실 위험으로 강등)
|
||||
rawGrade = when (rawGrade) {
|
||||
InvestmentGrade.LEVEL_5_STRONG_RECOMMEND,
|
||||
InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND,
|
||||
InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND -> InvestmentGrade.LEVEL_1_SPECULATIVE
|
||||
|
||||
InvestmentGrade.LEVEL_2_HIGH_RISK,
|
||||
InvestmentGrade.LEVEL_1_SPECULATIVE -> InvestmentGrade.LEVEL_0_SPECULATIVE
|
||||
|
||||
else -> InvestmentGrade.LEVEL_0_SPECULATIVE
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 과열 강등 로직 (급등주는 한 단계씩 낮춤)
|
||||
val finalGrade = if (isOverheated) {
|
||||
when (rawGrade) {
|
||||
InvestmentGrade.LEVEL_5_STRONG_RECOMMEND -> InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
|
||||
@@ -265,7 +266,7 @@ object AutoTradingManager {
|
||||
rawGrade
|
||||
}
|
||||
|
||||
// 🌟 [추가] 사용자가 비활성화한 등급인지 체크하여 매수 원천 차단
|
||||
// 4. 사용자가 UI에서 스위치로 꺼둔(비활성화) 등급인지 체크하여 매수 차단
|
||||
val isEnabled = when (finalGrade) {
|
||||
InvestmentGrade.LEVEL_5_STRONG_RECOMMEND -> KisSession.tradeConfig.useGrade5
|
||||
InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND -> KisSession.tradeConfig.useGrade4
|
||||
@@ -275,7 +276,6 @@ object AutoTradingManager {
|
||||
else -> false
|
||||
}
|
||||
|
||||
// 비활성화된 등급이라면 LEVEL_0_SPECULATIVE(관망) 반환
|
||||
return if (isEnabled) finalGrade else InvestmentGrade.LEVEL_0_SPECULATIVE
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ fun TradingDecisionLog() {
|
||||
modifier = Modifier.fillMaxSize().background(Color.White) // fillMaxWidth() 대신 전체 채우기
|
||||
) {
|
||||
// ==========================================
|
||||
// 1️⃣ 첫 번째 섹션: 2열 배치 구간 (span = 3)
|
||||
// 1️⃣ 첫 번째 섹션: 3열 배치 구간 (span = 2로 수정)
|
||||
// ==========================================
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 6칸 모두 차지
|
||||
@@ -298,16 +298,15 @@ fun TradingDecisionLog() {
|
||||
modifier = Modifier.padding(top = 10.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
// ConfigIndex.BUY_WEIGHT_INDEX,
|
||||
|
||||
var defaults = arrayOf(
|
||||
ConfigIndex.TAX_INDEX, ConfigIndex.PROFIT_INDEX,
|
||||
ConfigIndex.TAX_INDEX, ConfigIndex.PROFIT_INDEX,ConfigIndex.MIN_PURCHASE_SCORE_INDEX,
|
||||
ConfigIndex.MAX_BUDGET_INDEX, ConfigIndex.MAX_PRICE_INDEX, ConfigIndex.MIN_PRICE_INDEX,
|
||||
ConfigIndex.MIN_PURCHASE_SCORE_INDEX, ConfigIndex.SELL_PROFIT,
|
||||
ConfigIndex.MAX_COUNT_INDEX, ConfigIndex.MAX_HOLDING_COUNT,
|
||||
ConfigIndex.SELL_PROFIT, ConfigIndex.MAX_COUNT_INDEX, ConfigIndex.MAX_HOLDING_COUNT,
|
||||
)
|
||||
|
||||
// 💡 items에 span을 주어 3칸씩 차지하게 만듦 (결과적으로 2열 배치)
|
||||
items(defaults.size, span = { GridItemSpan(3) }) { index ->
|
||||
// 🌟 [수정] GridItemSpan(3) -> GridItemSpan(2)로 변경하여 3열 배치
|
||||
items(defaults.size, span = { GridItemSpan(2) }) { index ->
|
||||
val configKey = defaults.get(index)
|
||||
var localText by remember(configKey) { mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "") }
|
||||
|
||||
@@ -355,28 +354,28 @@ fun TradingDecisionLog() {
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 2열 배치니까 각각 span = 3
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
// 🌟 [수정] 모든 리스크 관리 아이템들도 GridItemSpan(3) 및 maxLineSpan -> GridItemSpan(2)로 통일
|
||||
item(span = { GridItemSpan(2) }) {
|
||||
SettingSwitchField(
|
||||
label = "보유 주식 자동 매도 활성화",
|
||||
initialChecked = KisSession.config.getValues(ConfigIndex.TAKE_PROFIT) > 0.0,
|
||||
onCheckedChange = {
|
||||
KisSession.config.setValues(ConfigIndex.TAKE_PROFIT, if (it) 1.0 else 0.0)
|
||||
},
|
||||
helperText = "목표 수익률 ${KisSession.config.SELL_PROFIT} 도달 시 기계적 매도"
|
||||
helperText = "수익 ${KisSession.config.SELL_PROFIT} 도달 매도" // 텍스트 간소화
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
item(span = { GridItemSpan(2) }) {
|
||||
SettingSwitchField(
|
||||
label = "자동 손절 활성화",
|
||||
initialChecked = KisSession.config.getValues(ConfigIndex.STOP_LOSS) > 0.0,
|
||||
onCheckedChange = { KisSession.config.setValues(ConfigIndex.STOP_LOSS, if (it) 1.0 else 0.0) },
|
||||
helperText = "손실 방어선 도달 시 기계적 손절"
|
||||
helperText = "방어선 도달 시 기계적 손절"
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
item(span = { GridItemSpan(2) }) {
|
||||
SettingInputField(
|
||||
label = "최소 손절 라인 (%)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MINRATE).toString(),
|
||||
@@ -386,7 +385,7 @@ fun TradingDecisionLog() {
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
item(span = { GridItemSpan(2) }) {
|
||||
SettingInputField(
|
||||
label = "최대 허용 손절률 (%)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MAXRATE).toString(),
|
||||
@@ -396,27 +395,20 @@ fun TradingDecisionLog() {
|
||||
)
|
||||
}
|
||||
|
||||
// 💡 금액은 길게 뻗어야 하니 maxLineSpan
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
// 🌟 금액 입력칸도 한 줄에 3개 나란히 배치되도록 변경
|
||||
item(span = { GridItemSpan(2) }) {
|
||||
SettingInputField(
|
||||
label = "최대 허용 손실 금액 (원)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY).toLong().toString(),
|
||||
placeholder = "50000",
|
||||
onSave = { KisSession.config.setValues(ConfigIndex.LOSS_MAX_MONEY, it.toDoubleOrNull() ?: 0.0) },
|
||||
helperText = "1종목당 허용할 수 있는 최대 손실액 (원 단위)"
|
||||
helperText = "1종목당 최대 손실액" // 텍스트 간소화
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Divider(color = Color.LightGray, thickness = 1.dp)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2️⃣ 두 번째 섹션: 3열 배치 구간 (span = 2)
|
||||
// 2️⃣ 두 번째 섹션: 3열 배치 구간 (기존 코드 그대로 유지)
|
||||
// ==========================================
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 6칸 모두 차지
|
||||
Text(
|
||||
|
||||
Reference in New Issue
Block a user