Files
stocksearch/services/kospi200_service.go
hayato5246 2f8a6ea349
Some checks failed
Build Push and Restart Compose / deploy (push) Failing after 1m13s
전일대비 부호 및 API 데이터 파싱 로직 개선:
- `utils.ts`: 전일대비 부호 매핑 로직(1=상한가, 4=하한가) 수정 및 관련 함수(`sigToArrow`, `sigClass`) 업데이트.
- `types.ts`: `IndexQuote` 인터페이스의 `value` 필드명을 `price`로 수정.
- `kiwoom_ws_service.go`, `theme_service.go`, `kiwoom_service.go`: 전일대비 데이터 파싱 시 부호 결정 로직 추가(`signedChange`, `signedChangeBySig`) 및 관련 함수 호출로 대체.
- 기존 `predPre` 처리 부분을 양수/음수를 정확히 계산하도록 변경.
- `kospi200_service.go`: `PredPre` 필드 부호 처리 로직(`signedChangeBySig`) 추가.
- Svelte 페이지 업데이트:
  - `+page.svelte` 파일에서 `value` 필드를 `price`로 대체.
  - `theme` 및 `kospi200` 페이지에서 전일대비 표시값 절댓값으로 렌더링되도록 수정 (`Math.abs` 적용).
2026-04-08 19:53:51 +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: signedChangeBySig(s.PredPre, s.PredPreSig),
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
}