This commit is contained in:
lunaticbum 2026-05-14 13:27:33 +09:00
parent ad6d00ac39
commit b9ba8efc1a
3 changed files with 89 additions and 5 deletions

View File

@ -547,8 +547,11 @@ object TradingLogStore {
reason = log
).apply {
CoroutineScope(Dispatchers.Default).launch {
if (((tradingDecision.investmentGrade?.name?.length ?: 0) > 0 && KisSession.tradeConfig.useGradeShare.contains(tradingDecision.investmentGrade?.name))
) {
if (((tradingDecision.investmentGrade?.name?.length ?: 0) > 0 && KisSession.tradeConfig.useGradeShare.any {
tradingDecision.investmentGrade?.name?.contains(
it
) ?: false
})) {
NewsService.sendTelegramMessage("${this@apply.decision} ${tradingDecision.stockName}[${tradingDecision.currentPrice}] ${log}")
}
}

View File

@ -766,4 +766,64 @@ object KisTradeService {
}
suspend fun reserveSell(stockCode: String,
qty: String,
price: String,
targetDate : String) :Result<String> {
val config = KisSession.config
val isDomestic = stockCode.length == 6 && stockCode.all { it.isDigit() }
val baseUrl = if (config.isSimulation) vtsUrl else prodUrl
// 계좌번호 처리: 8자리면 01 자동 추가
var pureAccount = config.accountNo.replace("-", "").trim()
if (pureAccount.length == 8) pureAccount += "01"
val cano = pureAccount.take(8)
val acntPrdtCd = pureAccount.takeLast(2)
val trId = "CTSC0008U"
return try {
val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-resv") {
header("authorization", "Bearer ${config.tradeToken}")
header("appkey", if (config.isSimulation) config.vtsAppKey else config.realAppKey)
header("appsecret", if (config.isSimulation) config.vtsSecretKey else config.realSecretKey)
header("tr_id", trId)
header("custtype", "P") // [해결] 필수 헤더 추가
header("Content-Type", "application/json")
setBody(mapOf(
"CANO" to cano,
"ACNT_PRDT_CD" to acntPrdtCd,
"PDNO" to stockCode,
"ORD_DVSN_CD" to "00",
"SLL_BUY_DVSN_CD" to "01",
"ORD_QTY" to qty,
"ORD_UNPR" to price,
"ORD_OBJT_CBLC_DVSN_CD" to "10",
"RSVN_ORD_END_DT" to targetDate
))
}
val body = response.body<JsonObject>() // [해결] Polymorphic 직렬화 에러 방지
val rtCd = body["rt_cd"]?.jsonPrimitive?.content
val msg = body["msg1"]?.jsonPrimitive?.content ?: "메시지 없음"
if (rtCd == "0") {
// 응답의 output 객체에서 주문 번호(ODNO) 추출
val orderNo = body["output"]?.jsonObject?.get("ODNO")?.jsonPrimitive?.content
?: body["output"]?.jsonObject?.get("odno")?.jsonPrimitive?.content // API마다 대소문자가 다를 수 있음
?: ""
Result.success(orderNo) // 성공 시 주문 번호 반환
} else {
val msg = body["msg1"]?.jsonPrimitive?.content ?: "메시지 없음"
Result.failure(Exception("❌ 오류 ($rtCd): $msg"))
}
} catch (e: Exception) { Result.failure(e) }
}
}

View File

@ -529,7 +529,7 @@ object AutoTradingManager {
println("${holding.name} ${holding.profitRate.toDouble()} ${holding.valuationProfitAmount.toDouble()} ${KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY)} , ${KisSession.config.getValues(ConfigIndex.LOSS_MINRATE)} , ${KisSession.config.getValues(ConfigIndex.STOP_LOSS)}")
val profit = holding.profitRate.toDouble()
var targetPrice = holding.currentPrice.toDouble()
targetPrice = MarketUtil.roundToTickSize(targetPrice + (MarketUtil.getTickSize(targetPrice) * 3.0))
targetPrice = MarketUtil.roundToTickSize(targetPrice + (MarketUtil.getTickSize(targetPrice) * 3.0))
tradeService.postOrder(
stockCode = holding.code,
@ -814,6 +814,21 @@ object AutoTradingManager {
}
}
suspend fun cancelAllPendingSellOrders(
) {
// 1. 미체결 내역 조회
val unfilledResult = KisTradeService.fetchUnfilledOrders()
unfilledResult.onSuccess { response ->
response.filter { it.sll_buy_dvsn_cd == "01" }.forEach { order ->
TradingLogStore.addNotice(order.prdt_name,order.pdno,"[주문 취소] 정규장 시작전 모든 매도 주문 취소")
KisTradeService.cancelOrder(
order.ord_no, // 원주문번호
order.pdno
)
}
}
}
// 주문 시간 문자열을 Millis로 변환하는 유틸리티 (당일 주문 기준)
fun parseOrderTime(ordTmd: String): Long {
return try {
@ -908,8 +923,14 @@ object AutoTradingManager {
} else {
val now = LocalTime.now()
val currentMinute = now.minute
if (now.hour == 9 && currentMinute % 2 == 1
) {
if (now.hour == 8 && currentMinute < 50 && currentMinute > 45) {
if (lastForceCheckMinute != currentMinute) {
cancelAllPendingSellOrders()
}
} else if (now.hour == 8 && currentMinute > 55 && currentMinute > 50) {
} else if (now.hour == 9 && currentMinute % 2 == 1
) {
if (lastForceCheckMinute != currentMinute) {
TradingLogStore.addAnalyzer(
" - ",