Files
stocksearch/services/kospi200_service.go
2026-03-31 19:32:59 +09:00

107 lines
2.8 KiB
Go

package services
import (
"encoding/json"
"fmt"
"stocksearch/models"
"time"
)
// Kospi200Service 코스피200 종목 조회 서비스
type Kospi200Service struct {
kiwoom *KiwoomClient
cache *CacheService
}
var kospi200Svc *Kospi200Service
// GetKospi200Service 코스피200 서비스 싱글턴 반환
func GetKospi200Service() *Kospi200Service {
if kospi200Svc == nil {
kospi200Svc = &Kospi200Service{
kiwoom: GetKiwoomClient(),
cache: GetCacheService(),
}
}
return kospi200Svc
}
// GetStocks ka20002: 코스피200 구성종목 전체 조회 (연속조회, 캐시 1분)
func (s *Kospi200Service) GetStocks() ([]models.Kospi200Stock, error) {
const cacheKey = "kospi200_stocks"
if cached, ok := s.cache.Get(cacheKey); ok {
if stocks, ok := cached.([]models.Kospi200Stock); ok {
return stocks, nil
}
}
var all []models.Kospi200Stock
contYn, nextKey := "N", ""
for {
body, nextContYn, nextNextKey, err := s.kiwoom.postPaged(
"ka20002", "/api/dostk/sect",
map[string]string{
"mrkt_tp": "0", // 코스피
"inds_cd": "201", // KOSPI200
"stex_tp": "1", // KRX
},
contYn, nextKey,
)
if err != nil {
return nil, fmt.Errorf("코스피200 조회 실패: %w", err)
}
var result struct {
IndsStkpc []struct {
StkCd string `json:"stk_cd"`
StkNm string `json:"stk_nm"`
CurPrc string `json:"cur_prc"`
PredPreSig string `json:"pred_pre_sig"`
PredPre string `json:"pred_pre"`
FluRt string `json:"flu_rt"`
NowTrdeQty string `json:"now_trde_qty"`
OpenPric string `json:"open_pric"`
HighPric string `json:"high_pric"`
LowPric string `json:"low_pric"`
} `json:"inds_stkpc"`
ReturnCode int `json:"return_code"`
ReturnMsg string `json:"return_msg"`
}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("코스피200 응답 파싱 실패: %w", err)
}
if result.ReturnCode != 0 {
return nil, fmt.Errorf("코스피200 조회 실패: %s", result.ReturnMsg)
}
for _, s := range result.IndsStkpc {
// 종목코드·종목명이 없는 행은 건너뜀
if s.StkCd == "" || s.StkNm == "" {
continue
}
all = append(all, models.Kospi200Stock{
Code: s.StkCd,
Name: s.StkNm,
CurPrc: absParseIntSafe(s.CurPrc),
PredPreSig: s.PredPreSig,
PredPre: parseIntSafe(s.PredPre),
FluRt: parseFloatSafe(s.FluRt),
Volume: absParseIntSafe(s.NowTrdeQty),
Open: absParseIntSafe(s.OpenPric),
High: absParseIntSafe(s.HighPric),
Low: absParseIntSafe(s.LowPric),
})
}
// 연속조회 종료 조건
if nextContYn != "Y" || nextNextKey == "" {
break
}
contYn, nextKey = nextContYn, nextNextKey
}
s.cache.Set(cacheKey, all, time.Minute)
return all, nil
}