107 lines
2.8 KiB
Go
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
|
|
}
|