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 = emptyList(), // 종목별 잔고 val output2: List = 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 = emptyList(), val output: List = 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 ) { // [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 = 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 ) { 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 = 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 = 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 = 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 = 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 // 자기자본이익률 )