Some checks failed
Build Push and Restart Compose / deploy (push) Failing after 1m13s
- `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` 적용).
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: 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
|
|
}
|