This commit is contained in:
lunaticbum 2026-04-02 15:22:38 +09:00
parent ef1847f115
commit 0b7684a176
9 changed files with 112 additions and 32 deletions

View File

@ -3,5 +3,5 @@ object Defines {
var LLM_PORT = 8080
var EMBEDDING_PORT = 8081
val AUTOSELL = true
val BLACKLISTEDSTOCKCODES = listOf<String>()
val BLACKLISTEDSTOCKCODES = listOf<String>("034220")
}

View File

@ -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],
)
}

View File

@ -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
}
}

View File

@ -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()}
}
}

View File

@ -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)
// }
/**
* 실행 호출하여 매핑 테이블 업데이트
*/

View File

@ -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

View File

@ -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}")
}
}
}

View File

@ -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}")
}
}
}

View File

@ -63,7 +63,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) {
KisSession.config = config
DatabaseFactory.saveConfig(config)
DartCodeManager.updateCorpCodes(HttpClient(CIO) {
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
})