package model import java.time.LocalDateTime enum class ConfigIndex(val index : Int,val label : String) { TAX_INDEX(0,"세금 및 수수료"), PROFIT_INDEX(TAX_INDEX.index + 1 , "기준 수익률"), BUY_WEIGHT_INDEX(PROFIT_INDEX.index + 1, "매수 가중치"), MAX_BUDGET_INDEX(BUY_WEIGHT_INDEX.index + 1, "종목 당 최대 투자금"), MAX_PRICE_INDEX(MAX_BUDGET_INDEX.index + 1 , "단일 종목 주당 최대 금액"), MIN_PRICE_INDEX(MAX_PRICE_INDEX.index + 1, "단일 종목 주당 최소 금액"), MIN_PURCHASE_SCORE_INDEX(MIN_PRICE_INDEX.index + 1, "주문 최소 기준 점수"), MAX_COUNT_INDEX(MIN_PURCHASE_SCORE_INDEX.index + 1, "단일 종목 최대 주문 개수"), SELL_PROFIT(MAX_COUNT_INDEX.index + 1, "보유 주식 매도 기준 수익율"), GRADE_5_BUY(SELL_PROFIT.index + 1, "강력 추천 투자 매수 기준"), GRADE_4_BUY(GRADE_5_BUY.index + 1, "안정적 투자 매수 기준"), GRADE_3_BUY(GRADE_4_BUY.index + 1, "보수적 투자 매수 기준"), GRADE_2_BUY(GRADE_3_BUY.index + 10, "하이리스크,리턴 매수 기준"), GRADE_1_BUY(GRADE_2_BUY.index + 1, "공격적초단기 매수 기준"), GRADE_5_PROFIT(GRADE_1_BUY.index + 1, "강력 추천 투자 목표 수익 비율"), GRADE_4_PROFIT(GRADE_5_PROFIT.index + 1, "안정적 투자 목표 수익 비율"), GRADE_3_PROFIT(GRADE_4_PROFIT.index + 1, "보수적 투자 목표 수익 비율"), GRADE_2_PROFIT(GRADE_3_PROFIT.index + 1, "하이리스크,리턴 목표 수익 비율"), GRADE_1_PROFIT(GRADE_2_PROFIT.index + 1, "공격적초단기 목표 수익 비율"); companion object { fun get(index : Int) = ConfigIndex.entries[index] } } data class AppConfig( // [DB 저장 데이터] // 실전 3종 val realAppKey: String = "", val realSecretKey: String = "", val realAccountNo: String = "", // 모의 3종 val vtsAppKey: String = "", val vtsSecretKey: String = "", val vtsAccountNo: String = "", // 모의 3종 val nAppKey: String = "", val nSecretKey: String = "", val dAppKey: String = "", // [세션 데이터 - 메모리에서만 관리] var marketToken: String = "", var marketTokenExpiredAt: LocalDateTime? = null, // 만료 시간 추가 var tradeToken: String = "", var tradeTokenExpiredAt: LocalDateTime? = null, val htsId: String = "", var websocketToken: String = "", val isSimulation: Boolean = true, val modelPath: String = "", val embedModelPath: String = "", var FEES_AND_TAXRATE: Double = 0.33, var MINIMUM_NET_PROFIT: Double = 0.8, var BUY_WEIGHT: Double = 2.0, var MAX_BUDGET: Double = 80000.0, var MAX_PRICE: Double = 40000.0, var MIN_PRICE: Double = 800.0, var MIN_PURCHASE_SCORE: Double = 65.0, var SELL_PROFIT: Double = 1.3, var GRADE_5_BUY : Int = 0, var GRADE_4_BUY : Int = 1, var GRADE_3_BUY : Int = 2, var GRADE_2_BUY : Int = 2, var GRADE_1_BUY : Int = 3, var GRADE_5_PROFIT : Double = 1.8, var GRADE_4_PROFIT : Double = 1.3, var GRADE_3_PROFIT : Double = 0.9, var GRADE_2_PROFIT : Double = 0.7, var GRADE_1_PROFIT : Double = 0.5, var MAX_COUNT : Int = 20, ) { val accountNo : String get() { return if (isSimulation) vtsAccountNo else realAccountNo } fun setValues(index :ConfigIndex , value : Double) { when (index) { ConfigIndex.TAX_INDEX -> {FEES_AND_TAXRATE = value} ConfigIndex.PROFIT_INDEX -> {MINIMUM_NET_PROFIT = value} ConfigIndex.MAX_PRICE_INDEX -> {MAX_PRICE = value} ConfigIndex.MIN_PRICE_INDEX -> {MIN_PRICE = value} ConfigIndex.BUY_WEIGHT_INDEX -> {BUY_WEIGHT = value} ConfigIndex.MAX_BUDGET_INDEX -> {MAX_BUDGET = value} ConfigIndex.MIN_PURCHASE_SCORE_INDEX -> {MIN_PURCHASE_SCORE = value} ConfigIndex.SELL_PROFIT -> {SELL_PROFIT = value} ConfigIndex.GRADE_5_PROFIT -> {GRADE_5_PROFIT = value} ConfigIndex.GRADE_4_PROFIT -> {GRADE_4_PROFIT = value} ConfigIndex.GRADE_3_PROFIT -> {GRADE_3_PROFIT = value} ConfigIndex.GRADE_2_PROFIT -> {GRADE_2_PROFIT = value} ConfigIndex.GRADE_1_PROFIT -> {GRADE_1_PROFIT = value} ConfigIndex.GRADE_5_BUY -> {GRADE_5_BUY = value.toInt()} ConfigIndex.GRADE_4_BUY -> {GRADE_4_BUY = value.toInt()} ConfigIndex.GRADE_3_BUY -> {GRADE_3_BUY = value.toInt()} ConfigIndex.GRADE_2_BUY -> {GRADE_2_BUY = value.toInt()} ConfigIndex.GRADE_1_BUY -> {GRADE_1_BUY = value.toInt()} ConfigIndex.MAX_COUNT_INDEX -> {MAX_COUNT = value.toInt()} } } fun getValues(index :ConfigIndex) : Double { return when (index) { ConfigIndex.TAX_INDEX -> { FEES_AND_TAXRATE } ConfigIndex.PROFIT_INDEX -> { MINIMUM_NET_PROFIT } ConfigIndex.MAX_PRICE_INDEX -> { MAX_PRICE } ConfigIndex.MIN_PRICE_INDEX -> { MIN_PRICE } ConfigIndex.BUY_WEIGHT_INDEX -> { BUY_WEIGHT } ConfigIndex.MAX_BUDGET_INDEX -> { MAX_BUDGET } ConfigIndex.MIN_PURCHASE_SCORE_INDEX -> { MIN_PURCHASE_SCORE } ConfigIndex.SELL_PROFIT -> {SELL_PROFIT } ConfigIndex.GRADE_5_BUY -> {GRADE_5_BUY.toDouble()} ConfigIndex.GRADE_4_BUY -> {GRADE_4_BUY.toDouble()} ConfigIndex.GRADE_3_BUY -> {GRADE_3_BUY.toDouble()} ConfigIndex.GRADE_2_BUY -> {GRADE_2_BUY.toDouble()} ConfigIndex.GRADE_1_BUY -> {GRADE_1_BUY.toDouble()} ConfigIndex.GRADE_5_PROFIT -> {GRADE_5_PROFIT} ConfigIndex.GRADE_4_PROFIT -> {GRADE_4_PROFIT} ConfigIndex.GRADE_3_PROFIT -> {GRADE_3_PROFIT} ConfigIndex.GRADE_2_PROFIT -> {GRADE_2_PROFIT} ConfigIndex.GRADE_1_PROFIT -> {GRADE_1_PROFIT} ConfigIndex.MAX_COUNT_INDEX -> {MAX_COUNT.toDouble()} } } } // [신규] 전역에서 참조할 단일 세션 객체 object KisSession { var config: AppConfig = AppConfig() fun getWebSocketKey() = config.websocketToken // 시장 데이터 토큰 유효성 검사 (만료 5분 전부터는 유효하지 않은 것으로 간주) fun isMarketTokenValid(): Boolean { return config.marketToken.isNotEmpty() && config.marketTokenExpiredAt?.isAfter(LocalDateTime.now().plusMinutes(5)) ?: false } // 매매용 토큰 유효성 검사 fun isTradeTokenValid(): Boolean { return config.tradeToken.isNotEmpty() && config.tradeTokenExpiredAt?.isAfter(LocalDateTime.now().plusMinutes(5)) ?: false } }