...
This commit is contained in:
parent
ad6d00ac39
commit
b9ba8efc1a
@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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(
|
||||
" - ",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user