...
This commit is contained in:
parent
c4f58f159a
commit
59c8ff4ebb
@ -8,6 +8,8 @@ import kotlinx.serialization.Serializable
|
|||||||
data class StockBalanceResponse(
|
data class StockBalanceResponse(
|
||||||
val rt_cd: String = "",
|
val rt_cd: String = "",
|
||||||
val msg1: String = "",
|
val msg1: String = "",
|
||||||
|
val ctx_area_fk100: String = "",
|
||||||
|
val ctx_area_nk100: String = "",
|
||||||
val output1: List<StockHolding> = emptyList(),
|
val output1: List<StockHolding> = emptyList(),
|
||||||
val output2: List<BalanceSummary> = emptyList()
|
val output2: List<BalanceSummary> = emptyList()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -505,65 +505,87 @@ object KisTradeService {
|
|||||||
|
|
||||||
// --- 내부 Raw 호출용 (통합 잔고에서 사용) ---
|
// --- 내부 Raw 호출용 (통합 잔고에서 사용) ---
|
||||||
private suspend fun fetchDomesticRawBalance(): Result<StockBalanceResponse> {
|
private suspend fun fetchDomesticRawBalance(): Result<StockBalanceResponse> {
|
||||||
val config = KisSession.config
|
val config = KisSession.config
|
||||||
val baseUrl = prodUrl
|
val baseUrl = prodUrl
|
||||||
val trId = "TTTC8434R"
|
val trId = "TTTC8434R"
|
||||||
|
|
||||||
val allHoldings = mutableListOf<StockHolding>()
|
val allHoldings = mutableListOf<StockHolding>()
|
||||||
var totalBalance: StockBalanceResponse? = null
|
var totalBalance: StockBalanceResponse? = null
|
||||||
|
|
||||||
// 연속 조회를 위한 변수
|
var ctxAreaFk = ""
|
||||||
var ctxAreaFk = ""
|
var ctxAreaNk = ""
|
||||||
var ctxAreaNk = ""
|
var trCont = ""
|
||||||
var trCont = "N" // 'N': 최초 조회, 'F': 다음 조회, 'M': 연속 조회
|
var pageCount = 1
|
||||||
|
var pureAccount = config.accountNo.replace("-", "").trim()
|
||||||
|
if (pureAccount.length == 8) pureAccount += "01"
|
||||||
|
|
||||||
try {
|
val cano = pureAccount.take(8)
|
||||||
do {
|
val acntPrdtCd = pureAccount.takeLast(2)
|
||||||
val response = client.get("$baseUrl/uapi/domestic-stock/v1/trading/inquire-balance") {
|
println("🚀 [잔고조회 시작] 계좌: ${config.realAccountNo}")
|
||||||
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("tr_cont", trCont) // 연속 조회 키 설정
|
|
||||||
|
|
||||||
val pureAccount = config.realAccountNo.replace("-", "").trim()
|
try {
|
||||||
parameter("CANO", pureAccount.take(8))
|
do {
|
||||||
parameter("ACNT_PRDT_CD", pureAccount.takeLast(2))
|
println("📡 [Step $pageCount] 요청 전송 중... (tr_cont: $trCont)")
|
||||||
parameter("AFHR_FLPR_YN", "N")
|
val response = client.get("$baseUrl/uapi/domestic-stock/v1/trading/inquire-balance") {
|
||||||
parameter("OFL_YN", "N")
|
header("authorization", "Bearer ${config.tradeToken}")
|
||||||
parameter("INQR_DVSN", "0")
|
header("appkey", if (config.isSimulation) config.vtsAppKey else config.realAppKey)
|
||||||
parameter("UNPR_DVSN", "01")
|
header("appsecret", if (config.isSimulation) config.vtsSecretKey else config.realSecretKey)
|
||||||
parameter("FUND_STTL_ICLD_YN", "N")
|
header("tr_id", trId)
|
||||||
parameter("FNCG_AMT_AUTO_RDPT_YN", "N")
|
header("tr_cont", trCont)
|
||||||
parameter("PRCS_DVSN", "00")
|
|
||||||
// 연속 조회 파라미터 전달
|
|
||||||
parameter("CTX_AREA_FK100", ctxAreaFk)
|
|
||||||
parameter("CTX_AREA_NK100", ctxAreaNk)
|
|
||||||
}
|
|
||||||
|
|
||||||
val body = response.body<StockBalanceResponse>()
|
parameter("CANO", cano)
|
||||||
|
parameter("ACNT_PRDT_CD", acntPrdtCd)
|
||||||
// 데이터 합치기
|
parameter("AFHR_FLPR_YN", "N")
|
||||||
allHoldings.addAll(body.output1)
|
parameter("OFL_YN", "N")
|
||||||
if (totalBalance == null) totalBalance = body
|
parameter("INQR_DVSN", "0")
|
||||||
|
parameter("UNPR_DVSN", "01")
|
||||||
// 헤더에서 다음 조회를 위한 키값 추출
|
parameter("FUND_STTL_ICLD_YN", "N")
|
||||||
trCont = response.headers["tr_cont"] ?: "D" // 'D' 또는 'E'는 끝을 의미
|
parameter("FNCG_AMT_AUTO_RDPT_YN", "N")
|
||||||
ctxAreaFk = response.headers["ctx_area_fk100"] ?: ""
|
parameter("PRCS_DVSN", "00")
|
||||||
ctxAreaNk = response.headers["ctx_area_nk100"] ?: ""
|
parameter("CTX_AREA_FK100", ctxAreaFk)
|
||||||
delay(250)
|
parameter("CTX_AREA_NK100", ctxAreaNk)
|
||||||
} while (trCont == "F" || trCont == "M") // 연속 데이터가 있는 동안 반복
|
|
||||||
// 모든 데이터를 합친 최종 객체 반환
|
|
||||||
return if (totalBalance != null) {
|
|
||||||
Result.success(totalBalance.copy(output1 = allHoldings))
|
|
||||||
} else {
|
|
||||||
println(totalBalance.toString())
|
|
||||||
Result.failure(Exception("No data found"))
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
if (!response.status.isSuccess()) {
|
||||||
return Result.failure(e)
|
println("❌ [Step $pageCount] HTTP 에러 발생: ${response.status}")
|
||||||
|
return Result.failure(Exception("HTTP Error: ${response.status}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val body = response.body<StockBalanceResponse>()
|
||||||
|
println("✅ [Step $pageCount] 수신 완료 - 종목 수: ${body.output1.size}")
|
||||||
|
|
||||||
|
allHoldings.addAll(body.output1)
|
||||||
|
if (totalBalance == null) totalBalance = body
|
||||||
|
|
||||||
|
// 다음 페이지를 위한 헤더 정보 추출
|
||||||
|
trCont = response.headers["tr_cont"] ?: "D"
|
||||||
|
ctxAreaFk = body.ctx_area_fk100 ?: ""
|
||||||
|
ctxAreaNk = body.ctx_area_nk100 ?: ""
|
||||||
|
|
||||||
|
println("📝 [Header Check] tr_cont: $trCont, ctx_area_nk100: $ctxAreaNk")
|
||||||
|
|
||||||
|
if ( trCont == "M") {
|
||||||
|
pageCount++
|
||||||
|
trCont = "N"
|
||||||
|
println("⏳ [연속 조회] 250ms 대기 후 다음 페이지 요청...")
|
||||||
|
delay(250) // API 과부하 방지
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (trCont == "N")
|
||||||
|
|
||||||
|
println("🎊 [잔고조회 종료] 총 수집 종목: ${allHoldings.size}")
|
||||||
|
|
||||||
|
return if (totalBalance != null) {
|
||||||
|
Result.success(totalBalance.copy(output1 = allHoldings))
|
||||||
|
} else {
|
||||||
|
Result.failure(Exception("응답 바디가 비어있습니다."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("💥 [Fatal Error] 잔고 조회 중 예외 발생: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
return Result.failure(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchOverseasRawBalance(): Result<StockBalanceResponse> {
|
private suspend fun fetchOverseasRawBalance(): Result<StockBalanceResponse> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user