353 lines
14 KiB
Kotlin
353 lines
14 KiB
Kotlin
package model
|
|
|
|
import AutoTradeItem
|
|
import kotlinx.serialization.SerialName
|
|
import kotlinx.serialization.Serializable
|
|
@Serializable
|
|
data class StockBalanceResponse(
|
|
val rt_cd: String = "",
|
|
val msg1: String = "",
|
|
val ctx_area_fk100: String = "",
|
|
val ctx_area_nk100: 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 pchs_amt: String = "0", // 매입금액합계 (투자 원금)
|
|
val prpr: String = "0", // 현재가
|
|
val evlu_amt: String = "0", // 평가금액
|
|
val evlu_pfls_amt: String = "0", // 평가손익금액 (평가금액 - 매입금액)
|
|
val evlu_pfls_rt: String = "0.0", // 평가손익률
|
|
val fltt_rt: String = "0.0", // 등락율 (당일 시장 강도 분석용)
|
|
val bfdy_cprs_icdc: String = "0", // 전일대비증감 (수급 확인용)
|
|
val ord_psbl_qty: String = "0", // 주문가능수량
|
|
val thdt_buyqty: String = "0" // 금일매수수량
|
|
)
|
|
|
|
@Serializable
|
|
data class BalanceSummary(
|
|
val dnca_tot_amt: String = "0", // 예수금총금액
|
|
val tot_evlu_amt: String = "0", // 총평가금액 (자산 총계)
|
|
val pchs_amt_smtl_amt: String = "0", // 매입금액합계금액
|
|
val evlu_pfls_smtl_amt: String = "0", // 평가손익합계금액
|
|
val asst_icdc_amt: String = "0", // 자산증감액 (어제 대비 성적 - 리포트 핵심)
|
|
val thdt_tlex_amt: String = "0" // 금일제비용금액 (세금/수수료 - 순수익 계산용)
|
|
)
|
|
|
|
@Serializable
|
|
data class RankingResponse(
|
|
var rt_cd : String,
|
|
var msg1 : String,
|
|
var msg_cd : String,
|
|
val output1: List<RankingStock> = emptyList(),
|
|
val output: List<RankingStock> = emptyList()
|
|
) {
|
|
val list = output + output1 + emptyList()
|
|
}
|
|
|
|
//@Serializable
|
|
enum class RankingType(
|
|
val title: String,
|
|
val trId: String,
|
|
val scrNo: String,
|
|
val path: String,
|
|
val extraParams: Map<String, String>
|
|
) {
|
|
// [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")),
|
|
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")),
|
|
// [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())
|
|
}
|
|
|
|
@Serializable
|
|
data class RankingStock(
|
|
val hts_kor_isnm: String = "", // 종목명
|
|
val hts_kor_alph_nm: String = "", // 종목명
|
|
val mkrtc_objt_iscd: String = "", // 종목코드
|
|
val mksc_shrn_iscd: String = "", // 종목코드
|
|
val stck_shrn_iscd: String = "", // 종목코드
|
|
val stck_prpr: String = "0", // 현재가
|
|
val prdy_ctrt: String = "0.0", // 등락률
|
|
val mrkt_div_cls_code : String = "J",
|
|
) {
|
|
val name : String
|
|
get() = listOf(hts_kor_isnm , hts_kor_alph_nm , mkrtc_objt_iscd).firstOrNull { it.isNotBlank() } ?: ""
|
|
val code : String
|
|
get() = listOf(mksc_shrn_iscd , mkrtc_objt_iscd , stck_shrn_iscd , hts_kor_isnm).firstOrNull { it.isNotBlank() } ?: ""
|
|
}
|
|
@Serializable
|
|
data class OverseasRankingResponse(
|
|
val rt_cd: String = "",
|
|
val msg1: String = "",
|
|
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
|
|
)
|
|
}
|
|
@Serializable
|
|
data class UnifiedStockHolding(
|
|
val code: String, // 종목코드
|
|
val name: String, // 종목명
|
|
val quantity: String, // 보유수량
|
|
val avgPrice: String, // 매입단가 (pchs_avg_pric)
|
|
val currentPrice: String, // 현재가 (prpr)
|
|
val profitRate: String, // 수익률 (evlu_pfls_rt)
|
|
val evalAmount: String, // 평가금액 (evlu_amt)
|
|
val valuationProfitAmount: String, // 평가손익금액 (evlu_pfls_amt)
|
|
val isDomestic: Boolean, // 국내/해외 구분
|
|
val availOrderCount: String, // 주문가능수량
|
|
val thdtBuyQty: String, // 금일매수수량
|
|
|
|
// 추가 추천 필드
|
|
val dailyChangeRate: String = "0.0", // 당일 등락율 (fltt_rt)
|
|
val pchsAmount: String = "0" // 총 매입금액 (pchs_amt)
|
|
) {
|
|
val isTodayEntry: Boolean get() = thdtBuyQty.toIntOrNull() ?: 0 > 0
|
|
}
|
|
|
|
@Serializable
|
|
data class UnifiedBalance(
|
|
val totalAsset: String, // 총 평가자산
|
|
val deposit: String, // 예수금
|
|
val dailyAssetChange: String, // 당일 자산 증감
|
|
val todayFees: String, // 당일 제비용
|
|
val totalProfitRate: String, // 직접 계산된 총 수익률
|
|
|
|
private val holdings: List<UnifiedStockHolding>
|
|
) {
|
|
fun getHoldings() = holdings.filter { (it.quantity.toIntOrNull() ?: 0) > 0 }
|
|
}
|
|
@Serializable
|
|
data class UnfilledOrder(
|
|
val orgn_odno: String,
|
|
@SerialName("odno")
|
|
val ord_no: String, // JSON의 odno를 ord_no로 매핑
|
|
val pdno: String,
|
|
@SerialName("prdt_name")
|
|
val prdt_name: String,
|
|
val ord_unpr: String, // JSON이 문자열이므로 String 권장
|
|
val ord_qty : String,
|
|
val sll_buy_dvsn_cd: String,
|
|
@SerialName("psbl_qty")
|
|
val rmnd_qty: String, // JSON의 psbl_qty를 rmnd_qty로 매핑
|
|
val ord_dvsn_name: String,
|
|
val rvse_cncl_dvsn_name: String,
|
|
val ord_tmd : String,
|
|
) {
|
|
fun isBuyOrder() : Boolean {
|
|
return true
|
|
}
|
|
}
|
|
|
|
@Serializable
|
|
data class UnfilledResponse(
|
|
val rt_cd: String,
|
|
val msg1: String,
|
|
val output: List<UnfilledOrder> = emptyList()
|
|
)
|
|
|
|
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,
|
|
quantity = this.ord_qty.toIntOrNull() ?: 0, // 미체결 내역에서는 원 주문 수량을 알기 어려우므로 0 또는 별도 처리
|
|
remainedQuantity = this.rmnd_qty.toIntOrNull() ?: 0,
|
|
status = if (this.sll_buy_dvsn_cd.equals("01")) "SELLING" else "PENDING_BUY", // 기본적으로 미체결은 매수/매도 대기 상태
|
|
isDomestic = isDomestic
|
|
)
|
|
}
|
|
|
|
// 단순 정보 전달용 데이터 클래스
|
|
data class StockBasicInfo(
|
|
val code: String,
|
|
val name: String,
|
|
val isDomestic: Boolean,
|
|
val quantity: String = "0"
|
|
)
|
|
|
|
data class ExecutionData(
|
|
val orderNo: String,
|
|
val code: String,
|
|
val price: String,
|
|
val qty: String,
|
|
val isFilled: Boolean
|
|
)
|
|
|
|
|
|
data class CurrentPriceResponse(
|
|
val rt_cd: String, // 0: 성공, 0 이외: 실패
|
|
val msg_cd: String,
|
|
val msg1: String,
|
|
val output: CurrentPriceOutput
|
|
)
|
|
|
|
data class CurrentPriceOutput(
|
|
val stck_prpr: String, // 주식 현재가
|
|
val prdy_vrss: String, // 전일 대비
|
|
val prdy_ctrt: String, // 전일 대비율
|
|
val acml_vol: String, // 누적 거래량
|
|
val stck_oprc: String, // 시가
|
|
val stck_hgpr: String, // 고가
|
|
val stck_lwpr: String, // 저가
|
|
val hts_avls: String, // 시가총액
|
|
val per: String,
|
|
val pbr: String,
|
|
val stck_shrn_iscd: String // 종목 코드
|
|
// ... 필요한 필드가 있다면 Python 모델을 참고하여 추가하세요.
|
|
)
|
|
|
|
|
|
@Serializable
|
|
data class OverseasBalanceResponse(
|
|
val rt_cd: String,
|
|
val msg_cd: String,
|
|
val msg1: String,
|
|
val ctx_area_fk200: String? = null,
|
|
val ctx_area_nk200: String? = null,
|
|
val output1: List<OverseasStockHolding> = emptyList(),
|
|
val output2: OverseasBalanceSummary? = null
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasStockHolding(
|
|
val ovrs_pdno: String, // 종목코드
|
|
val ovrs_item_name: String, // 종목명
|
|
val frcr_evlu_pfls_amt: String, // 외화평가손익금액
|
|
val evlu_pfls_rt: String, // 평가손익율
|
|
val pchs_avg_pric: String, // 매입평균가격
|
|
val ovrs_cblc_qty: String, // 잔고수량
|
|
val ord_psbl_qty: String, // 주문가능수량
|
|
val ovrs_stck_evlu_amt: String, // 평가금액
|
|
val now_pric2: String, // 현재가
|
|
val tr_crcy_cd: String, // 통화코드
|
|
val ovrs_excg_cd: String // 거래소코드
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasBalanceSummary(
|
|
val frcr_pchs_amt1: String, // 외화매입금액합계
|
|
val ovrs_tot_pfls: String, // 해외총손익
|
|
val tot_pftrt: String, // 총수익률
|
|
val tot_evlu_pfls_amt: String // 총평가손익금액
|
|
)
|
|
|
|
|
|
@Serializable
|
|
data class OverseasOrderResponse(
|
|
val rt_cd: String, // 성공 실패 여부 (0: 성공, 이외 실패)
|
|
val msg_cd: String, // 응답코드
|
|
val msg1: String, // 응답메세지
|
|
val output: OverseasOrderOutput? = null
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasOrderOutput(
|
|
val KRX_FWDG_ORD_ORGNO: String = "", // 한국거래소전송주문조직번호
|
|
val ODNO: String = "", // 주문번호
|
|
val ORD_TMD: String = "" // 주문시각
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasAnalysisResponse(
|
|
val rt_cd: String, val msg1: String,
|
|
val output: List<OverseasAnalysisItem> = emptyList()
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasAnalysisItem(
|
|
val excd: String, // 거래소 (NAS, NYS)
|
|
val syml: String, // 종목코드 (AAPL)
|
|
val name: String, // 종목명
|
|
val last: String, // 현재가
|
|
val rate: String, // 등락률
|
|
val tvol: String // 거래량
|
|
)
|
|
|
|
// [시세] 기간별 시세 응답
|
|
@Serializable
|
|
data class OverseasDailyPriceResponse(
|
|
val rt_cd: String, val msg1: String,
|
|
val output1: OverseasPriceOutput1,
|
|
val output2: List<OverseasPriceOutput2> = emptyList()
|
|
)
|
|
|
|
@Serializable
|
|
data class OverseasPriceOutput1(val rsym: String, val nrec: String)
|
|
|
|
@Serializable
|
|
data class OverseasPriceOutput2(
|
|
val xymd: String, val clos: String, val open: String,
|
|
val high: String, val low: String, val tvol: String
|
|
)
|
|
|
|
// [재무] FMP API용 핵심 지표
|
|
@Serializable
|
|
data class KeyMetrics(
|
|
val symbol: String,
|
|
val currentRatio: Double, // 유동비율 (> 1.0 권장)
|
|
val debtToEquity: Double, // 부채비율 (< 1.5 권장)
|
|
val roe: Double // 자기자본이익률
|
|
) |