This commit is contained in:
2026-02-09 15:32:31 +09:00
parent aa1f3817bb
commit ce976a895d
7 changed files with 390 additions and 183 deletions
+100 -4
View File
@@ -26,6 +26,51 @@ import model.minimumNetProfit
import network.KisTradeService
import util.MarketUtil
enum class InvestmentGrade(
val displayName: String,
val description: String,
val shortWeight: Double = 0.0,
val midWeight: Double = 0.0,
val longWeight: Double = 0.0
) {
LEVEL_5_STRONG_RECOMMEND(
displayName = "최상급 추천",
description = "단기·중기·장기 모두 우수하고, 신뢰도 매우 높은 범용 매수 추천",
shortWeight = 1.0,
midWeight = 1.0,
longWeight = 1.0
),
LEVEL_4_BALANCED_RECOMMEND(
displayName = "균형 추천",
description = "중기·장기 기본은 양호하고, 단기 성과도 준수한 안정형 추천",
shortWeight = 0.8,
midWeight = 1.0,
longWeight = 1.0
),
LEVEL_3_CAUTIOUS_RECOMMEND(
displayName = "보수적 추천",
description = "중기/장기 기본은 양호하지만, 단기 변동성이 높아 신중히 접근해야 함",
shortWeight = 0.6,
midWeight = 1.0,
longWeight = 1.0
),
LEVEL_2_HIGH_RISK(
displayName = "고위험 추천",
description = "단기/초단기 성과만 강하고, 중기·장기가 애매하여 리스크가 큰 투자",
shortWeight = 1.0,
midWeight = 0.4,
longWeight = 0.4
),
LEVEL_1_SPECULATIVE(
displayName = "순수 공격적 선택",
description = "단기/초단기 성과에만 의존하는 단기 급등형 공격적 투자",
shortWeight = 1.0,
midWeight = 0.2,
longWeight = 0.2
)
}
/**
* 통합 주문 및 자동매매 설정 섹션
* * [수정 사항]
@@ -83,11 +128,62 @@ fun IntegratedOrderSection(
val curPriceNum = currentPrice.replace(",", "").toDoubleOrNull() ?: 0.0
val basePrice = (if (orderPrice.isEmpty()) curPriceNum else orderPrice.toDoubleOrNull() ?: 0.0)
fun getInvestmentGrade(
ts: TradingDecision,
totalScore: Double,
confidence: Double
): InvestmentGrade {
// 1. 기본 조건 충족 여부
if (totalScore < 68.0 || confidence < 70.0) {
return InvestmentGrade.LEVEL_1_SPECULATIVE // 매도/관망 (추천 등급 없음)
}
fun excuteTrade(willEnableAutoSell: Boolean, orderQty: String, profitRate1: Double?,confidence : Boolean = false) {
// 2. 단기/중기/장기 패턴 기준
val ultraShort = ts.ultraShortScore
val short = ts.shortTermScore
val mid = ts.midTermScore
val long = ts.longTermScore
val shortAvg = listOf(ultraShort, short).average() // 초단기+단기
val midLongAvg = listOf(mid, long).average() // 중기+장기
return when {
// LEVEL_5: 단기·중기·장기 모두 매우 높고, 신뢰도까지 높음
shortAvg >= 85.0 && midLongAvg >= 80.0 ->
InvestmentGrade.LEVEL_5_STRONG_RECOMMEND
// LEVEL_4: 중기·장기 기본 준수, 단기까지 양호
midLongAvg >= 75.0 && shortAvg >= 70.0 ->
InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND
// LEVEL_3: 중기·장기 기본 이상, 단기만 단기 변동성 높은 보수형
midLongAvg >= 70.0 && shortAvg in 60.0..70.0 ->
InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
// LEVEL_2: 단기/초단기만 강하고, 중기·장기 애매
shortAvg >= 75.0 && midLongAvg < 65.0 ->
InvestmentGrade.LEVEL_2_HIGH_RISK
// LEVEL_1: 단기/초단기만 의미 있고, 중기·장기 심각히 약함
shortAvg >= 70.0 && midLongAvg < 55.0 ->
InvestmentGrade.LEVEL_1_SPECULATIVE
// 기본 조건은 충족했지만, 패턴에 잘 맞지 않을 때 (예: 중립)
else ->
InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND
}
}
fun excuteTrade(willEnableAutoSell: Boolean, orderQty: String, profitRate1: Double?,investmentGrade: InvestmentGrade = InvestmentGrade.LEVEL_2_HIGH_RISK) {
scope.launch {
val tickSize = MarketUtil.getTickSize(basePrice)
val oneTickLowerPrice = basePrice - (tickSize * if (confidence) { 1} else {2})
val oneTickLowerPrice = basePrice - (tickSize * when(investmentGrade) {
InvestmentGrade.LEVEL_5_STRONG_RECOMMEND -> 0
InvestmentGrade.LEVEL_4_BALANCED_RECOMMEND -> 1
InvestmentGrade.LEVEL_3_CAUTIOUS_RECOMMEND -> 1
InvestmentGrade.LEVEL_2_HIGH_RISK -> 2
InvestmentGrade.LEVEL_1_SPECULATIVE -> 3
})
// 2. 주문 가격 설정 (직접 입력값이 없으면 한 틱 낮은 가격 사용)
val finalPrice = if (orderPrice.isBlank()) {
@@ -183,7 +279,7 @@ fun IntegratedOrderSection(
}
println("🚀 [매수 진행] 토탈 스코어: ${String.format("%.1f", totalScore)} -> 종목: ${completeTradingDecision.stockCode}")
val MAX_BUDGET = 30000.0
val MAX_BUDGET = 35000.0
// basePrice(현재가 혹은 지정가)를 기준으로 매수 가능 수량 산출 (최소 1주 보장)
val calculatedQty = if (basePrice > 0) {
(MAX_BUDGET / basePrice).toInt().coerceAtLeast(1)
@@ -195,7 +291,7 @@ fun IntegratedOrderSection(
willEnableAutoSell = true,
orderQty = calculatedQty.toString(),
profitRate1 = finalMargin,
confidence = totalScore >= HIGH_QUALITY_SCORE
investmentGrade = getInvestmentGrade(completeTradingDecision,totalScore, completeTradingDecision.confidence),
)
} else {