diff --git a/src/main/kotlin/Defines.kt b/src/main/kotlin/Defines.kt index e2d9d80..5c14af6 100644 --- a/src/main/kotlin/Defines.kt +++ b/src/main/kotlin/Defines.kt @@ -3,5 +3,5 @@ object Defines { var LLM_PORT = 8080 var EMBEDDING_PORT = 8081 val AUTOSELL = true - val BLACKLISTEDSTOCKCODES = listOf() + val BLACKLISTEDSTOCKCODES = listOf("034220") } \ No newline at end of file diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index faa3086..185f72a 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -13,7 +13,12 @@ import ConfigTable.grade_4_profit import ConfigTable.grade_5_allocationrate import ConfigTable.grade_5_buy import ConfigTable.grade_5_profit +import ConfigTable.loss_max_money +import ConfigTable.loss_maxrate +import ConfigTable.loss_minrate import ConfigTable.max_count +import ConfigTable.stop_Loss +import ConfigTable.take_profit import Defines.DETAILLOG import Defines.EMBEDDING_PORT import Defines.LLM_PORT @@ -205,6 +210,11 @@ fun main() = application { GRADE_3_ALLOCATIONRATE = it[grade_3_allocationrate], GRADE_4_ALLOCATIONRATE = it[grade_4_allocationrate], GRADE_5_ALLOCATIONRATE = it[grade_5_allocationrate], + stop_Loss = it[stop_Loss], + take_profit = it[take_profit], + loss_max = it[loss_maxrate], + loss_min = it[loss_minrate], + loss_money = it[loss_max_money], MAX_COUNT = it[max_count], ) } diff --git a/src/main/kotlin/database/DatabaseFactory.kt b/src/main/kotlin/database/DatabaseFactory.kt index 000424b..3a3e3cc 100644 --- a/src/main/kotlin/database/DatabaseFactory.kt +++ b/src/main/kotlin/database/DatabaseFactory.kt @@ -63,13 +63,12 @@ object ConfigTable : Table("app_config") { val grade_2_allocationrate = double("grade_2_allocationrate").default(0.4) val grade_1_allocationrate = double("grade_1_allocationrate").default(0.3) + val take_profit = bool("take_profit").default(false) + val stop_Loss = bool("stop_Loss").default(false) - - - - - - + val loss_minrate = double("loss_minrate").default(3.5) + val loss_maxrate = double("loss_maxrate").default(10.0) + val loss_max_money = double("loss_max_money").default(10000.0) @@ -284,6 +283,11 @@ object DatabaseFactory { GRADE_3_ALLOCATIONRATE = it[ConfigTable.grade_3_allocationrate], GRADE_4_ALLOCATIONRATE = it[ConfigTable.grade_4_allocationrate], GRADE_5_ALLOCATIONRATE = it[ConfigTable.grade_5_allocationrate], + stop_Loss = it[ConfigTable.stop_Loss], + take_profit = it[ConfigTable.take_profit], + loss_min = it[ConfigTable.loss_minrate], + loss_max = it[ConfigTable.loss_maxrate], + loss_money = it[ConfigTable.loss_max_money], MAX_COUNT = it[ConfigTable.max_count], ) } @@ -329,7 +333,11 @@ object DatabaseFactory { it[grade_3_allocationrate] = config.GRADE_3_ALLOCATIONRATE it[grade_2_allocationrate] = config.GRADE_2_ALLOCATIONRATE it[grade_1_allocationrate] = config.GRADE_1_ALLOCATIONRATE - + it[stop_Loss] = config.stop_Loss + it[take_profit] = config.take_profit + it[loss_maxrate] = config.loss_max + it[loss_minrate] = config.loss_min + it[loss_max_money] = config.loss_money it[max_count] = config.MAX_COUNT } } diff --git a/src/main/kotlin/model/AppConfig.kt b/src/main/kotlin/model/AppConfig.kt index b93ae88..1202b6f 100644 --- a/src/main/kotlin/model/AppConfig.kt +++ b/src/main/kotlin/model/AppConfig.kt @@ -32,7 +32,15 @@ enum class ConfigIndex(val index : Int,val label : String) { GRADE_4_ALLOCATIONRATE(GRADE_5_ALLOCATIONRATE.index + 1, "안정적 투자 목표 투자금 비율"), GRADE_3_ALLOCATIONRATE(GRADE_4_ALLOCATIONRATE.index + 1, "보수적 투자 목표 투자금 비율"), GRADE_2_ALLOCATIONRATE(GRADE_3_ALLOCATIONRATE.index + 1, "하이리스크,리턴 목표 투자금 비율"), - GRADE_1_ALLOCATIONRATE(GRADE_2_ALLOCATIONRATE.index + 1, "공격적초단기 목표 투자금 비율"); + GRADE_1_ALLOCATIONRATE(GRADE_2_ALLOCATIONRATE.index + 1, "공격적초단기 목표 투자금 비율"), + + TAKE_PROFIT(GRADE_1_ALLOCATIONRATE.index + 1, "보유 주식 자동 매매 활성") , + STOP_LOSS(TAKE_PROFIT.index + 1, "손절 활성 여부 (최소율~최대율 사이의 최대 금액 이하로 자동 손절)") , + LOSS_MINRATE(STOP_LOSS.index + 1, "손절 최소 기준") , + LOSS_MAXRATE(LOSS_MINRATE.index + 1, "손절 최소 기준") , + LOSS_MAX_MONEY(LOSS_MAXRATE.index + 1, "손절 최대 금액") , + + ; companion object { fun get(index : Int) = ConfigIndex.entries[index] @@ -91,13 +99,17 @@ data class AppConfig( var GRADE_2_PROFIT : Double = 0.7, var GRADE_1_PROFIT : Double = 0.5, - var GRADE_5_ALLOCATIONRATE : Double = 1.0, - var GRADE_4_ALLOCATIONRATE : Double = 0.8, - var GRADE_3_ALLOCATIONRATE : Double = 0.6, - var GRADE_2_ALLOCATIONRATE : Double = 0.4, - var GRADE_1_ALLOCATIONRATE : Double = 0.3, - + var GRADE_5_ALLOCATIONRATE : Double = 1.0, + var GRADE_4_ALLOCATIONRATE : Double = 0.8, + var GRADE_3_ALLOCATIONRATE : Double = 0.6, + var GRADE_2_ALLOCATIONRATE : Double = 0.4, + var GRADE_1_ALLOCATIONRATE : Double = 0.3, var MAX_COUNT : Int = 20, + var take_profit : Boolean = false, + var stop_Loss : Boolean = false, + var loss_min : Double = 3.5, + var loss_max : Double = 10.0, + var loss_money : Double = 10000.0, ) { val accountNo : String @@ -130,11 +142,16 @@ data class AppConfig( ConfigIndex.MAX_COUNT_INDEX -> {MAX_COUNT = value.toInt()} - ConfigIndex.GRADE_5_ALLOCATIONRATE -> {GRADE_5_ALLOCATIONRATE = value} - ConfigIndex.GRADE_4_ALLOCATIONRATE -> {GRADE_4_ALLOCATIONRATE = value} - ConfigIndex.GRADE_3_ALLOCATIONRATE -> {GRADE_3_ALLOCATIONRATE = value} - ConfigIndex.GRADE_2_ALLOCATIONRATE -> {GRADE_2_ALLOCATIONRATE = value} - ConfigIndex.GRADE_1_ALLOCATIONRATE -> {GRADE_1_ALLOCATIONRATE = value} + ConfigIndex.GRADE_5_ALLOCATIONRATE -> {GRADE_5_ALLOCATIONRATE = value} + ConfigIndex.GRADE_4_ALLOCATIONRATE -> {GRADE_4_ALLOCATIONRATE = value} + ConfigIndex.GRADE_3_ALLOCATIONRATE -> {GRADE_3_ALLOCATIONRATE = value} + ConfigIndex.GRADE_2_ALLOCATIONRATE -> {GRADE_2_ALLOCATIONRATE = value} + ConfigIndex.GRADE_1_ALLOCATIONRATE -> {GRADE_1_ALLOCATIONRATE = value} + ConfigIndex.LOSS_MAXRATE -> { loss_max = value} + ConfigIndex.LOSS_MINRATE -> { loss_min = value} + ConfigIndex.LOSS_MAX_MONEY -> { loss_money = value} + ConfigIndex.STOP_LOSS -> { stop_Loss = value > 0.1} + ConfigIndex.TAKE_PROFIT -> { take_profit = value > 0.1 } } } fun getValues(index :ConfigIndex) : Double { @@ -177,8 +194,11 @@ data class AppConfig( ConfigIndex.GRADE_3_ALLOCATIONRATE -> {GRADE_3_ALLOCATIONRATE} ConfigIndex.GRADE_2_ALLOCATIONRATE -> {GRADE_2_ALLOCATIONRATE} ConfigIndex.GRADE_1_ALLOCATIONRATE -> {GRADE_1_ALLOCATIONRATE} - - + ConfigIndex.LOSS_MAXRATE -> { loss_max} + ConfigIndex.LOSS_MINRATE -> { loss_min} + ConfigIndex.LOSS_MAX_MONEY -> { loss_money } + ConfigIndex.STOP_LOSS -> {if(!stop_Loss) 0.0 else 1.0} + ConfigIndex.TAKE_PROFIT -> {if(!take_profit) 0.0 else 1.0} ConfigIndex.MAX_COUNT_INDEX -> {MAX_COUNT.toDouble()} } } diff --git a/src/main/kotlin/network/DartCodeManager.kt b/src/main/kotlin/network/DartCodeManager.kt index 9a4bff3..a191c8c 100644 --- a/src/main/kotlin/network/DartCodeManager.kt +++ b/src/main/kotlin/network/DartCodeManager.kt @@ -2,7 +2,10 @@ package network import io.ktor.client.* import io.ktor.client.request.* +import io.ktor.client.request.forms.FormDataContent import io.ktor.client.statement.* +import io.ktor.http.HttpHeaders +import io.ktor.http.Parameters import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import model.KisSession @@ -65,6 +68,37 @@ object DartCodeManager { } } +// suspend fun fetchKrxData(client: HttpClient) { +// val url = "https://data.krx.co.kr/comm/bldAttendant/getJsonData.cmd" +// +// val response: HttpResponse = client.post(url) { +// // Headers 설정 +// header(HttpHeaders.Accept, "application/json, text/javascript, */*; q=0.01") +// header(HttpHeaders.AcceptLanguage, "en-US,en;q=0.9,ko-KR;q=0.8,ko;q=0.7") +// header("X-Requested-With", "XMLHttpRequest") +// header(HttpHeaders.Referrer, "https://data.krx.co.kr/contents/MDC/MDI/mdiLoader/index.cmd?menuId=MDC0302") +// header(HttpHeaders.UserAgent, "Mozilla/5.0 (Linux; Android) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Mobile Safari/537.36") +// +// // Body 설정 (application/x-www-form-urlencoded) +// setBody(FormDataContent(Parameters.build { +// append("bld", "dbms/MDC/EASY/ranking/MDCEASY01601") +// append("locale", "ko_KR") +// append("mktId", "ALL") +// append("itmTpCd3", "2") +// append("itmTpCd2", "1") +// append("strtDd", "20250402") +// append("endDd", "20260402") +// append("stkprcTpCd", "Y") +// append("share", "1") +// append("money", "1") +// append("csvxls_isNo", "false") +// })) +// } +// +// val responseBody = response.bodyAsText() +// println(responseBody) +// } + /** * 앱 실행 시 호출하여 매핑 테이블 업데이트 */ diff --git a/src/main/kotlin/network/NewsService.kt b/src/main/kotlin/network/NewsService.kt index ee6ca89..408ccb6 100644 --- a/src/main/kotlin/network/NewsService.kt +++ b/src/main/kotlin/network/NewsService.kt @@ -9,10 +9,17 @@ import io.ktor.client.plugins.logging.DEFAULT import io.ktor.client.plugins.logging.LogLevel import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.forms.FormDataContent import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.client.request.parameter +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType.Application.Json +import io.ktor.http.HttpHeaders +import io.ktor.http.Parameters import io.ktor.http.Url import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.Serializable @@ -71,6 +78,7 @@ object NewsService { } + suspend fun fetchFinancialGrowth(corpCode: String?): String { if (corpCode != null) { val apiKey = KisSession.config.dAppKey diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index 10c6ff2..8e7d5a3 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -131,7 +131,7 @@ object AutoTradingManager { println("🚀 [매수 진행] 토탈 스코어: ${String.format("%.1f", totalScore)} -> 종목: ${completeTradingDecision.stockCode}") // basePrice(현재가 혹은 지정가)를 기준으로 매수 가능 수량 산출 (최소 1주 보장) - val gradeRate = (1.0 - (investmentGrade.ordinal * 0.12)) + val gradeRate = KisSession.config.getValues(investmentGrade.allocationRate) val maxQty = (KisSession.config.getValues(ConfigIndex.MAX_COUNT_INDEX) * gradeRate).roundToInt() maxBudget = maxBudget * gradeRate val calculatedQty = if (basePrice > 0) { @@ -147,7 +147,7 @@ object AutoTradingManager { investmentGrade = investmentGrade, ) - } else if(totalScore >= (minScore * 0.85) && completeTradingDecision.confidence + append >= (MIN_CONFIDENCE * 0.85)) { + } else if(totalScore >= (minScore * 0.9) && completeTradingDecision.confidence + append >= (MIN_CONFIDENCE * 0.9)) { addToReanalysis(RankingStock(mksc_shrn_iscd = completeTradingDecision.stockCode,hts_kor_isnm = completeTradingDecision.stockName)) TradingLogStore.addLog(completeTradingDecision,"RETRY","✋ [관망] 토탈 스코어[$totalScore] 또는 신뢰도[${completeTradingDecision.confidence}] 미달 이나 약간의 오차로 재분석 대기열에 추가") } else { @@ -160,21 +160,21 @@ object AutoTradingManager { when (completeTradingDecision?.decision) { "BUY","매수" -> { append = buyWeight - TradingLogStore.addLog(completeTradingDecision,"BUY","[$stockCode] 매수 추천 resultCheck: ${completeTradingDecision?.reason}") + TradingLogStore.addLog(completeTradingDecision,"BUY","[$stockCode] 매수 추천 : ${completeTradingDecision?.reason}") resultCheck(completeTradingDecision) } "SELL" -> { - TradingLogStore.addLog(completeTradingDecision,"SELL","[$stockCode] 매도 추천 resultCheck: ${completeTradingDecision?.reason}") + TradingLogStore.addLog(completeTradingDecision,"SELL","[$stockCode] 매도 추천 : ${completeTradingDecision?.reason}") println("[$stockCode] 매도: ${completeTradingDecision?.reason}") } "HOLD" -> { append = 0.0 - TradingLogStore.addLog(completeTradingDecision,"HOLD","[$stockCode] 관망 유지 resultCheck: ${completeTradingDecision?.reason}") + TradingLogStore.addLog(completeTradingDecision,"HOLD","[$stockCode] 관망 유지 : ${completeTradingDecision?.reason}") resultCheck(completeTradingDecision) } else -> { append = 0.0 - println("[$stockCode] ${completeTradingDecision?.decision} resultCheck: ${completeTradingDecision?.reason}") + println("[$stockCode] ${completeTradingDecision?.decision} : ${completeTradingDecision?.reason}") } } } diff --git a/src/main/kotlin/ui/IntegratedOrderSection.kt b/src/main/kotlin/ui/IntegratedOrderSection.kt index 9e3e29a..0f1ae8a 100644 --- a/src/main/kotlin/ui/IntegratedOrderSection.kt +++ b/src/main/kotlin/ui/IntegratedOrderSection.kt @@ -268,7 +268,7 @@ fun IntegratedOrderSection( when (completeTradingDecision?.decision) { "BUY" -> { append = buyWeight - println("[$stockCode] 매수 추천 resultCheck: ${completeTradingDecision?.reason}") + println("[$stockCode] 매수 추천 : ${completeTradingDecision?.reason}") resultCheck(completeTradingDecision) } "SELL" -> { @@ -277,11 +277,11 @@ fun IntegratedOrderSection( "HOLD" -> { append = 0.0 resultCheck(completeTradingDecision) - println("[$stockCode] 관망 유지 resultCheck: ${completeTradingDecision?.reason}") + println("[$stockCode] 관망 유지 : ${completeTradingDecision?.reason}") } else -> { append = 0.0 - println("[$stockCode] ${completeTradingDecision?.decision} resultCheck: ${completeTradingDecision?.reason}") + println("[$stockCode] ${completeTradingDecision?.decision} : ${completeTradingDecision?.reason}") } } } diff --git a/src/main/kotlin/ui/SettingsScreen.kt b/src/main/kotlin/ui/SettingsScreen.kt index 6338d67..84d53bb 100644 --- a/src/main/kotlin/ui/SettingsScreen.kt +++ b/src/main/kotlin/ui/SettingsScreen.kt @@ -63,7 +63,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) { KisSession.config = config DatabaseFactory.saveConfig(config) - + DartCodeManager.updateCorpCodes(HttpClient(CIO) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } })