From 84f2202f17b43aba2fbe0ef7ae6827bf1702b5a7 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Tue, 15 Sep 2026 16:17:45 +0900 Subject: [PATCH] ... --- src/main/kotlin/analyzer/TechnicalAnalyzer.kt | 4 ++-- src/main/kotlin/model/AppConfig.kt | 4 ++++ src/main/kotlin/network/KisTradeService.kt | 15 +++++++++------ src/main/kotlin/network/RagService.kt | 2 +- src/main/kotlin/service/AutoTradingManager.kt | 8 ++++---- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/analyzer/TechnicalAnalyzer.kt b/src/main/kotlin/analyzer/TechnicalAnalyzer.kt index 934cccc..71c34b3 100644 --- a/src/main/kotlin/analyzer/TechnicalAnalyzer.kt +++ b/src/main/kotlin/analyzer/TechnicalAnalyzer.kt @@ -352,7 +352,7 @@ class TechnicalAnalyzer { candles: List, avgReboundTerm: Double, dropThreshold: Double = 5.0, - timeTolerance: Double = 1.5 + timeTolerance: Double = 2.0 ): Boolean { if (candles.size < 20 || avgReboundTerm <= 0.0) return false @@ -771,7 +771,7 @@ data class VolatilityForecast( ) data class ReboundStats( val avgReboundPeriod: Double = 0.0, // 평균 반등 소요 캔들 (일/주/월) - val timeTolerance: Double = 1.5, // 오차 허용 범위 (표준편차) + val timeTolerance: Double = 2.0, // 오차 허용 범위 (표준편차) val avgDropRate: Double = 5.0, // 평균 하락폭 val avgReboundAmplitude: Double = 0.0, // 🌟 [신규] 바닥 찍고 평균적으로 몇 % 올랐는가? val isValid: Boolean = false diff --git a/src/main/kotlin/model/AppConfig.kt b/src/main/kotlin/model/AppConfig.kt index 4edd5b8..24db78f 100644 --- a/src/main/kotlin/model/AppConfig.kt +++ b/src/main/kotlin/model/AppConfig.kt @@ -365,4 +365,8 @@ object KisSession { return now.isBefore(LocalTime.of(15,30)) && now.isAfter(LocalTime.of(9,0)) } + fun isMarketAnalyzerTime(now: LocalTime) : Boolean { + return now.isBefore(LocalTime.of(18,0)) && now.isAfter(LocalTime.of(8,0)) + } + } \ No newline at end of file diff --git a/src/main/kotlin/network/KisTradeService.kt b/src/main/kotlin/network/KisTradeService.kt index 248fbcb..896441b 100644 --- a/src/main/kotlin/network/KisTradeService.kt +++ b/src/main/kotlin/network/KisTradeService.kt @@ -31,6 +31,7 @@ import kotlinx.serialization.json.jsonPrimitive import model.* import java.time.LocalDate import java.time.LocalTime +import java.time.ZoneId import java.time.format.DateTimeFormatter import kotlin.coroutines.coroutineContext @@ -426,12 +427,14 @@ object KisTradeService { isDomestic && !config.isSimulation -> if (isBuy) "TTTC0802U" else "TTTC0801U" else -> if (isBuy) "TTTS3002U" else "TTTS3001U" } - val finalOrderDivision = when { + var finalOrderDivision = when { orderDivision.isNotEmpty() -> orderDivision marketCode.equals("SOR") || price == "0" || price.isEmpty() -> "01" // 시장가 else -> "00" // 지정가 } - + if (marketCode.equals("KRX") && LocalTime.now(ZoneId.of("Asia/Seoul")).isAfter(LocalTime.of(16,0))) { + finalOrderDivision = "41" + } return try { val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-cash") { @@ -684,7 +687,7 @@ object KisTradeService { try { do { if (!coroutineContext.isActive) throw _root_ide_package_.io.ktor.utils.io.CancellationException("UI에서 작업을 취소함") // [추가] - println("📡 [Step $pageCount] 요청 전송 중... (tr_cont: $trCont)") +// println("📡 [Step $pageCount] 요청 전송 중... (tr_cont: $trCont)") val response = client.get("$baseUrl/uapi/domestic-stock/v1/trading/inquire-balance") { header("authorization", "Bearer ${config.tradeToken}") header("appkey", config.realAppKey) @@ -714,8 +717,8 @@ object KisTradeService { } val body = response.body() - println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}\n${body.output2}\n\n") -// println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}") +// println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}\n${body.output2}\n\n") + println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}") allHoldings.addAll(body.output1) if (totalBalance == null) totalBalance = body @@ -725,7 +728,7 @@ object KisTradeService { ctxAreaFk = body.ctx_area_fk100 ?: "" ctxAreaNk = body.ctx_area_nk100 ?: "" - println("📝 [Header Check] tr_cont: $trCont, ctx_area_nk100: $ctxAreaNk") +// println("📝 [Header Check] tr_cont: $trCont, ctx_area_nk100: $ctxAreaNk") if ( trCont == "M") { pageCount++ diff --git a/src/main/kotlin/network/RagService.kt b/src/main/kotlin/network/RagService.kt index 614fe7c..fced423 100644 --- a/src/main/kotlin/network/RagService.kt +++ b/src/main/kotlin/network/RagService.kt @@ -435,7 +435,7 @@ object RagService { put("type", "json_object") } }.toString() - println("requestBodyJson =>> $requestBodyJson") +// println("requestBodyJson =>> $requestBodyJson") val request = Request.Builder() .url(LLM_API_URL()) .post(requestBodyJson.toRequestBody(jsonMediaType)) diff --git a/src/main/kotlin/service/AutoTradingManager.kt b/src/main/kotlin/service/AutoTradingManager.kt index 9bfe295..66a3ecf 100644 --- a/src/main/kotlin/service/AutoTradingManager.kt +++ b/src/main/kotlin/service/AutoTradingManager.kt @@ -116,7 +116,7 @@ object AutoTradingManager { val globalCallback = { completeTradingDecision: TradingDecision?, isSuccess: Boolean -> val seoulZone = ZoneId.of("Asia/Seoul") val now = LocalTime.now(ZoneId.of("Asia/Seoul")) - if (KisSession.isMarketOpenTime(now) && isSuccess && completeTradingDecision != null) { + if (KisSession.isMarketAnalyzerTime(now) && isSuccess && completeTradingDecision != null) { val decision = completeTradingDecision println("${decision.stockName} ${decision.decision}") @@ -1262,7 +1262,7 @@ object AutoTradingManager { while (iterator.hasNext()) { totalCount-- val stock = iterator.next() - if (KisSession.isMarketOpenTime(now)) { + if (KisSession.isMarketAnalyzerTime(now)) { if (BLACKLISTEDSTOCKCODES.contains(stock.code)) { println("❌ 차단 처리된 주식 : ${stock.name}") } else { @@ -1385,7 +1385,7 @@ object AutoTradingManager { // 🌟 [핵심] 물타기 대상 여부 플래그 식별 val targetHolding = currentBalance?.getHoldings()?.firstOrNull { - it.code == stock.code && it.quantity.toInt() > 0 + it.code == stock.code && it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount } val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice @@ -1450,7 +1450,7 @@ object AutoTradingManager { val tempAnalyzer = TechnicalAnalyzer().apply { this.daily = dailyData } val volatility = tempAnalyzer.calculateVolatilityForecast(dailyData, 20) val expectedProfitRate = ((volatility.realisticHigh - currentPrice) / currentPrice) * 100.0 - val dailyStats = tempAnalyzer.calculateDynamicReboundStats(dailyData, 5.0) + val dailyStats = tempAnalyzer.calculateDynamicReboundStats(dailyData, 3.0) // 🌟 [완화 3] 기대수익률 및 진입 타이밍 이원화 val isProfitable: Boolean