atrade/src/main/kotlin/model/StockModels.kt

212 lines
8.6 KiB
Kotlin
Raw Normal View History

2026-01-10 18:16:50 +09:00
package model
2026-01-19 17:09:37 +09:00
import AutoTradeItem
2026-01-13 16:04:25 +09:00
import kotlinx.serialization.SerialName
2026-01-10 18:16:50 +09:00
import kotlinx.serialization.Serializable
@Serializable
data class StockBalanceResponse(
val rt_cd: String = "",
val msg1: String = "",
val output1: List<StockHolding> = emptyList(),
val output2: List<BalanceSummary> = emptyList()
)
@Serializable
data class StockHolding(
val pdno: String = "", // 상품번호
val prdt_name: String = "", // 상품명
val hldg_qty: String = "0", // 보유수량
val pchs_avg_pric: String = "0", // 매입평균가
val prpr: String = "0", // 현재가
val evlu_pfls_rt: String = "0.0", // 평가손익률
val evlu_amt: String = "0" // 평가금액
)
@Serializable
data class BalanceSummary(
val tot_evlu_amt: String = "0", // 총 평가금액
val evlu_pfls_rt: String = "0.0", // 총 수익률 (에러 발생 지점: 기본값 추가로 해결)
val asst_icrt: String = "0.0", // 일부 환경에서 수익률 필드명
2026-02-03 18:07:18 +09:00
val nass_amt: String = "0" , // 순자산 금액
val dnca_tot_amt: String = "0"
2026-01-10 18:16:50 +09:00
)
@Serializable
data class RankingResponse(
2026-01-13 16:04:25 +09:00
var rt_cd : String,
var msg1 : String,
var msg_cd : String,
val output1: List<RankingStock> = emptyList(),
2026-01-10 18:16:50 +09:00
val output: List<RankingStock> = emptyList()
2026-01-13 16:04:25 +09:00
) {
val list = output + output1 + emptyList()
2026-01-10 18:16:50 +09:00
}
2026-02-09 15:32:31 +09:00
//@Serializable
2026-01-13 16:04:25 +09:00
enum class RankingType(
val title: String,
val trId: String,
val scrNo: String,
val path: String,
2026-02-09 15:32:31 +09:00
val extraParams: Map<String, String>
2026-01-13 16:04:25 +09:00
) {
2026-02-09 15:32:31 +09:00
// [1] 등락률 (RISE, FALL)
// FID_INPUT_CNT_1: 0 (전체/당일), FID_TRGT_CLS_CODE: 11111111 (전체)
RISE("상승률순", "FHPST01700000", "20170", "/uapi/domestic-stock/v1/ranking/fluctuation",
mapOf("FID_RANK_SORT_CLS_CODE" to "0", "FID_TRGT_CLS_CODE" to "11111111", "FID_INPUT_CNT_1" to "0")),
FALL("하락률순", "FHPST01700000", "20170", "/uapi/domestic-stock/v1/ranking/fluctuation",
mapOf("FID_RANK_SORT_CLS_CODE" to "1", "FID_TRGT_CLS_CODE" to "11111111", "FID_INPUT_CNT_1" to "0")),
// [2] 당사매매 (COMPANY_TRADE) - 날짜 빈값 = 당일
COMPANY_TRADE("당사매매", "FHPST01860000", "20186", "/uapi/domestic-stock/v1/ranking/traded-by-company",
mapOf("FID_RANK_SORT_CLS_CODE" to "1", "FID_INPUT_DATE_1" to "", "FID_INPUT_DATE_2" to "", "FID_APLY_RANG_VOL" to "0", "FID_TRGT_CLS_CODE" to "11111111")),
// [3] 거래량/거래대금
VOLUME("거래량순", "FHPST01710000", "20171", "/uapi/domestic-stock/v1/quotations/volume-rank",
mapOf("FID_RANK_SORT_CLS_CODE" to "2", "FID_TRGT_CLS_CODE" to "11111111")),
VALUE("거래대금순", "FHPST01710000", "20171", "/uapi/domestic-stock/v1/quotations/volume-rank",
mapOf("FID_RANK_SORT_CLS_CODE" to "3", "FID_TRGT_CLS_CODE" to "11111111")),
2026-02-13 13:49:40 +09:00
VOLUME0("거래량순", "FHPST01710000", "20171", "/uapi/domestic-stock/v1/quotations/volume-rank",
mapOf("FID_RANK_SORT_CLS_CODE" to "0", "FID_TRGT_CLS_CODE" to "11111111")),
VOLUME1("거래량순", "FHPST01710000", "20171", "/uapi/domestic-stock/v1/quotations/volume-rank",
mapOf("FID_RANK_SORT_CLS_CODE" to "1", "FID_TRGT_CLS_CODE" to "11111111")),
VOLUME4("거래량순", "FHPST01710000", "20171", "/uapi/domestic-stock/v1/quotations/volume-rank",
mapOf("FID_RANK_SORT_CLS_CODE" to "4", "FID_TRGT_CLS_CODE" to "11111111")),
2026-02-09 15:32:31 +09:00
// [4] 기타 랭킹 (예상체결만 FID_MK_OP_CLS_CODE 필요)
EXPECTED_RISE("예상상승", "FHPST01820000", "20182", "/uapi/domestic-stock/v1/ranking/exp-trans-updown",
mapOf("FID_MK_OP_CLS_CODE" to "0", "FID_TRGT_CLS_CODE" to "11111111")),
NEW_HIGH("52주신고가", "FHPST01690000", "20169", "/uapi/domestic-stock/v1/ranking/new-high-new-low",
mapOf("FID_RANK_SORT_CLS_CODE" to "0", "FID_TRGT_CLS_CODE" to "11111111")),
FINANCE("재무비율순", "FHPST01750000", "20175", "/uapi/domestic-stock/v1/ranking/finance-ratio",
mapOf("FID_RANK_SORT_CLS_CODE" to "0", "FID_TRGT_CLS_CODE" to "0", "FID_INPUT_OPTION_1" to "2023", "FID_INPUT_OPTION_2" to "3")),
MARKET_VALUE("시장가치순", "FHPST01790000", "20179", "/uapi/domestic-stock/v1/ranking/market-value",
mapOf("FID_RANK_SORT_CLS_CODE" to "23", "FID_TRGT_CLS_CODE" to "0", "FID_INPUT_OPTION_1" to "2023", "FID_INPUT_OPTION_2" to "3")),
SHORT_SALE("공매도상위", "FHPST04820000", "20482", "/uapi/domestic-stock/v1/ranking/short-sale",
mapOf("FID_PERIOD_DIV_CODE" to "D", "FID_SELECT_DIV_CODE" to "1", "FID_INPUT_CNT_1" to "0", "FID_TRGT_CLS_CODE" to "0")),
VOLUME_POWER("체결강도순", "FHPST01680000", "20168", "/uapi/domestic-stock/v1/ranking/volume-power",
mapOf("FID_TRGT_CLS_CODE" to "11111111")),
MARKET_CAP("시가총액순", "FHPST01740000", "20174", "/uapi/domestic-stock/v1/ranking/market-cap",
mapOf("FID_TRGT_CLS_CODE" to "0")),
HTS_TOP20("HTS조회상위", "HHMCM000100C0", "20175", "/uapi/domestic-stock/v1/ranking/hts-top-view", emptyMap())
2026-01-13 16:04:25 +09:00
}
2026-01-10 18:16:50 +09:00
@Serializable
data class RankingStock(
2026-01-13 16:04:25 +09:00
val hts_kor_isnm: String = "", // 종목명
2026-01-10 18:16:50 +09:00
val hts_kor_alph_nm: String = "", // 종목명
val mkrtc_objt_iscd: String = "", // 종목코드
2026-01-13 16:04:25 +09:00
val mksc_shrn_iscd: String = "", // 종목코드
2026-01-14 15:42:26 +09:00
val stck_shrn_iscd: String = "", // 종목코드
2026-01-10 18:16:50 +09:00
val stck_prpr: String = "0", // 현재가
2026-01-13 16:04:25 +09:00
val prdy_ctrt: String = "0.0", // 등락률
val mrkt_div_cls_code : String = "J",
) {
val name : String
2026-01-14 15:42:26 +09:00
get() = listOf(hts_kor_isnm , hts_kor_alph_nm , mkrtc_objt_iscd).firstOrNull { it.isNotBlank() } ?: ""
2026-01-13 16:04:25 +09:00
val code : String
2026-01-14 15:42:26 +09:00
get() = listOf(mksc_shrn_iscd , mkrtc_objt_iscd , stck_shrn_iscd , hts_kor_isnm).firstOrNull { it.isNotBlank() } ?: ""
2026-01-13 16:04:25 +09:00
}
2026-01-10 18:16:50 +09:00
@Serializable
data class OverseasRankingResponse(
2026-01-13 16:04:25 +09:00
val rt_cd: String = "",
val msg1: String = "",
2026-01-10 18:16:50 +09:00
val output: List<OverseasRankingStock> = emptyList()
)
@Serializable
data class OverseasRankingStock(
val hts_kor_alph_nm: String, // 종목명
val mkrtc_objt_iscd: String, // 종목코드 (Ticker)
val last: String, // 현재가
val diff: String, // 전일대비
val rate: String // 등락률
) {
// 국내용 RankingStock과 호환되도록 변환 함수 추가
fun toRankingStock() = RankingStock(
hts_kor_alph_nm = hts_kor_alph_nm,
mkrtc_objt_iscd = mkrtc_objt_iscd,
stck_prpr = last,
prdy_ctrt = rate
)
2026-01-13 16:04:25 +09:00
}
@Serializable
data class UnifiedStockHolding(
val code: String, // 종목코드
val name: String, // 종목명
val quantity: String, // 보유수량
val avgPrice: String, // 매입단가
val currentPrice: String, // 현재가
val profitRate: String, // 수익률
val evalAmount: String, // 평가금액
val isDomestic: Boolean // 국내/해외 구분
)
@Serializable
data class UnifiedBalance(
val totalAsset: String, // 총 평가자산
val totalProfitRate: String, // 총 수익률
2026-02-03 18:07:18 +09:00
val deposit: String,
2026-01-13 16:04:25 +09:00
val holdings: List<UnifiedStockHolding> // 통합 보유 종목 리스트
)
2026-01-14 15:42:26 +09:00
@Serializable
data class UnfilledOrder(
2026-01-19 17:09:37 +09:00
val orgn_odno: String,
2026-01-20 15:13:50 +09:00
@SerialName("odno")
val ord_no: String, // JSON의 odno를 ord_no로 매핑
2026-01-19 17:09:37 +09:00
val pdno: String,
2026-01-20 15:13:50 +09:00
@SerialName("prdt_name")
val prdt_name: String,
2026-01-19 17:09:37 +09:00
val ord_unpr: String, // JSON이 문자열이므로 String 권장
2026-01-20 15:13:50 +09:00
val ord_qty : String,
2026-01-21 11:49:30 +09:00
val sll_buy_dvsn_cd: String,
2026-01-20 15:13:50 +09:00
@SerialName("psbl_qty")
val rmnd_qty: String, // JSON의 psbl_qty를 rmnd_qty로 매핑
2026-01-19 17:09:37 +09:00
val ord_dvsn_name: String,
val rvse_cncl_dvsn_name: String
2026-01-14 15:42:26 +09:00
)
@Serializable
data class UnfilledResponse(
val rt_cd: String,
val msg1: String,
val output: List<UnfilledOrder> = emptyList()
)
2026-01-19 17:09:37 +09:00
fun UnfilledOrder.toAutoTradeItem(isDomestic: Boolean): AutoTradeItem {
return AutoTradeItem(
orderNo = this.ord_no,
code = this.pdno,
name = this.prdt_name,
orderedPrice = this.ord_unpr.toDoubleOrNull() ?: 0.0,
2026-01-20 15:13:50 +09:00
quantity = this.ord_qty.toIntOrNull() ?: 0, // 미체결 내역에서는 원 주문 수량을 알기 어려우므로 0 또는 별도 처리
2026-01-19 17:09:37 +09:00
remainedQuantity = this.rmnd_qty.toIntOrNull() ?: 0,
2026-01-21 11:49:30 +09:00
status = if (this.sll_buy_dvsn_cd.equals("01")) "SELLING" else "PENDING_BUY", // 기본적으로 미체결은 매수/매도 대기 상태
2026-01-19 17:09:37 +09:00
isDomestic = isDomestic
)
}
2026-01-14 15:42:26 +09:00
2026-01-19 17:09:37 +09:00
// 단순 정보 전달용 데이터 클래스
data class StockBasicInfo(
2026-01-14 15:42:26 +09:00
val code: String,
val name: String,
2026-01-19 17:09:37 +09:00
val isDomestic: Boolean,
val quantity: String = "0"
2026-01-21 11:49:30 +09:00
)
data class ExecutionData(
val orderNo: String,
val code: String,
val price: String,
val qty: String,
val isFilled: Boolean
2026-01-22 16:21:18 +09:00
)