전일대비 부호 및 API 데이터 파싱 로직 개선:
Some checks failed
Build Push and Restart Compose / deploy (push) Failing after 1m13s
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` 적용).
This commit is contained in:
@@ -149,7 +149,7 @@ export interface WatchlistItem {
|
|||||||
|
|
||||||
export interface IndexQuote {
|
export interface IndexQuote {
|
||||||
name: string
|
name: string
|
||||||
value: number
|
price: number
|
||||||
change: number
|
change: number
|
||||||
changeRate: number
|
changeRate: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ export function formatDate(iso: string): string {
|
|||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 전일대비 부호 문자 (2=상승, 3=보합, 5=하락)
|
// 전일대비 부호 문자 (1=상한가, 2=상승, 3=보합, 4=하한가, 5=하락)
|
||||||
export function sigToArrow(sig: string): string {
|
export function sigToArrow(sig: string): string {
|
||||||
if (sig === '2') return '▲'
|
if (sig === '1' || sig === '2') return '▲'
|
||||||
if (sig === '5') return '▼'
|
if (sig === '4' || sig === '5') return '▼'
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 전일대비 부호에 따른 클래스
|
// 전일대비 부호에 따른 클래스
|
||||||
export function sigClass(sig: string): string {
|
export function sigClass(sig: string): string {
|
||||||
if (sig === '2') return 'text-red-400'
|
if (sig === '1' || sig === '2') return 'text-red-400'
|
||||||
if (sig === '5') return 'text-blue-400'
|
if (sig === '4' || sig === '5') return 'text-blue-400'
|
||||||
return 'text-gray-400'
|
return 'text-gray-400'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,7 +296,7 @@
|
|||||||
{#each indices as idx}
|
{#each indices as idx}
|
||||||
<div class="bg-gray-800 rounded-lg px-4 py-3">
|
<div class="bg-gray-800 rounded-lg px-4 py-3">
|
||||||
<div class="text-xs text-gray-400 mb-1">{idx.name}</div>
|
<div class="text-xs text-gray-400 mb-1">{idx.name}</div>
|
||||||
<div class="text-lg font-bold text-white">{idx.value.toLocaleString('ko-KR', { maximumFractionDigits: 2 })}</div>
|
<div class="text-lg font-bold text-white">{idx.price.toLocaleString('ko-KR', { maximumFractionDigits: 2 })}</div>
|
||||||
<div class="text-sm {priceClass(idx.changeRate)}">{formatRate(idx.changeRate)}</div>
|
<div class="text-sm {priceClass(idx.changeRate)}">{formatRate(idx.changeRate)}</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
{stock.curPrc.toLocaleString()}
|
{stock.curPrc.toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-3 py-3 text-right text-sm {sigClass(stock.predPreSig)}">
|
<td class="px-3 py-3 text-right text-sm {sigClass(stock.predPreSig)}">
|
||||||
{sigToArrow(stock.predPreSig)} {stock.predPre.toLocaleString()}
|
{sigToArrow(stock.predPreSig)} {Math.abs(stock.predPre).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
||||||
{formatRate(stock.fluRt)}
|
{formatRate(stock.fluRt)}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@
|
|||||||
{stock.curPrc.toLocaleString()}
|
{stock.curPrc.toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
||||||
{sigToArrow(stock.fluSig)}{stock.predPre.toLocaleString()}
|
{sigToArrow(stock.fluSig)}{Math.abs(stock.predPre).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
<td class="px-3 py-3 text-right text-sm {priceClass(stock.fluRt)}">
|
||||||
{formatRate(stock.fluRt)}
|
{formatRate(stock.fluRt)}
|
||||||
|
|||||||
@@ -219,12 +219,11 @@ func (k *KiwoomClient) fetchPrice(stkCd string) (*models.StockPrice, error) {
|
|||||||
if result.ReturnCode != 0 {
|
if result.ReturnCode != 0 {
|
||||||
return nil, fmt.Errorf("현재가 조회 실패: %s", result.ReturnMsg)
|
return nil, fmt.Errorf("현재가 조회 실패: %s", result.ReturnMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
price := &models.StockPrice{
|
price := &models.StockPrice{
|
||||||
Code: displayCode,
|
Code: displayCode,
|
||||||
Name: result.StkNm,
|
Name: result.StkNm,
|
||||||
CurrentPrice: absParseIntSafe(result.CurPrc),
|
CurrentPrice: absParseIntSafe(result.CurPrc),
|
||||||
ChangePrice: parseIntSafe(result.PredPre),
|
ChangePrice: signedChange(result.PredPre, result.FluRt),
|
||||||
ChangeRate: parseFloatSafe(result.FluRt),
|
ChangeRate: parseFloatSafe(result.FluRt),
|
||||||
Volume: absParseIntSafe(result.TrdeQty),
|
Volume: absParseIntSafe(result.TrdeQty),
|
||||||
High: absParseIntSafe(result.HighPric),
|
High: absParseIntSafe(result.HighPric),
|
||||||
@@ -451,7 +450,7 @@ func (k *KiwoomClient) GetTopVolumeStocks(market string, count int) ([]models.St
|
|||||||
Code: row.StkCd,
|
Code: row.StkCd,
|
||||||
Name: row.StkNm,
|
Name: row.StkNm,
|
||||||
CurrentPrice: absParseIntSafe(row.CurPrc),
|
CurrentPrice: absParseIntSafe(row.CurPrc),
|
||||||
ChangePrice: parseIntSafe(row.PredPre),
|
ChangePrice: signedChange(row.PredPre, row.FluRt),
|
||||||
ChangeRate: parseFloatSafe(row.FluRt),
|
ChangeRate: parseFloatSafe(row.FluRt),
|
||||||
Volume: absParseIntSafe(row.TrdeQty),
|
Volume: absParseIntSafe(row.TrdeQty),
|
||||||
Market: mktName,
|
Market: mktName,
|
||||||
@@ -559,7 +558,7 @@ func (k *KiwoomClient) GetTopFluctuation(market string, ascending bool, count in
|
|||||||
Code: row.StkCd,
|
Code: row.StkCd,
|
||||||
Name: row.StkNm,
|
Name: row.StkNm,
|
||||||
CurrentPrice: absParseIntSafe(row.CurPrc),
|
CurrentPrice: absParseIntSafe(row.CurPrc),
|
||||||
ChangePrice: parseIntSafe(row.PredPre),
|
ChangePrice: signedChange(row.PredPre, row.FluRt),
|
||||||
ChangeRate: parseFloatSafe(row.FluRt),
|
ChangeRate: parseFloatSafe(row.FluRt),
|
||||||
Volume: absParseIntSafe(row.NowTrdeQty),
|
Volume: absParseIntSafe(row.NowTrdeQty),
|
||||||
CntrStr: parseFloatSafe(row.CntrStr),
|
CntrStr: parseFloatSafe(row.CntrStr),
|
||||||
@@ -588,6 +587,27 @@ func absParseIntSafe(s string) int64 {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// signedChange 전일대비를 절댓값으로 파싱 후 등락률 부호에 맞춰 부호 결정
|
||||||
|
// 키움 API의 pred_pre 필드는 가격 필드처럼 부호가 방향 표시용이므로 flu_rt 기준으로 부호 결정
|
||||||
|
func signedChange(predPre string, fluRt string) int64 {
|
||||||
|
n := absParseIntSafe(predPre)
|
||||||
|
rate := parseFloatSafe(fluRt)
|
||||||
|
if rate < 0 {
|
||||||
|
return -n
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// signedChangeBySig 전일대비를 절댓값으로 파싱 후 flu_sig(등락기호) 기준으로 부호 결정
|
||||||
|
// flu_sig: 1=상한가, 2=상승, 3=보합, 4=하한가, 5=하락
|
||||||
|
func signedChangeBySig(predPre string, fluSig string) int64 {
|
||||||
|
n := absParseIntSafe(predPre)
|
||||||
|
if fluSig == "4" || fluSig == "5" {
|
||||||
|
return -n
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func parseFloatSafe(s string) float64 {
|
func parseFloatSafe(s string) float64 {
|
||||||
s = strings.ReplaceAll(s, ",", "")
|
s = strings.ReplaceAll(s, ",", "")
|
||||||
s = strings.TrimPrefix(s, "+")
|
s = strings.TrimPrefix(s, "+")
|
||||||
|
|||||||
@@ -434,11 +434,17 @@ func (k *KiwoomWSClient) reconnect() {
|
|||||||
func parseRealPrice(code string, v map[string]string) *models.StockPrice {
|
func parseRealPrice(code string, v map[string]string) *models.StockPrice {
|
||||||
normalized := strings.TrimPrefix(code, "A")
|
normalized := strings.TrimPrefix(code, "A")
|
||||||
normalized = strings.SplitN(normalized, "_", 2)[0] // _NX, _AL 등 접미사 제거
|
normalized = strings.SplitN(normalized, "_", 2)[0] // _NX, _AL 등 접미사 제거
|
||||||
|
// 전일대비(v["11"])는 절댓값으로 파싱 후, 등락률(v["12"]) 부호에 맞춰 부호 결정
|
||||||
|
changeAbs := absInt(parseWSInt(v["11"]))
|
||||||
|
changeRate := parseWSFloat(v["12"])
|
||||||
|
if changeRate < 0 {
|
||||||
|
changeAbs = -changeAbs
|
||||||
|
}
|
||||||
return &models.StockPrice{
|
return &models.StockPrice{
|
||||||
Code: normalized,
|
Code: normalized,
|
||||||
CurrentPrice: absInt(parseWSInt(v["10"])),
|
CurrentPrice: absInt(parseWSInt(v["10"])),
|
||||||
ChangePrice: parseWSInt(v["11"]),
|
ChangePrice: changeAbs,
|
||||||
ChangeRate: parseWSFloat(v["12"]),
|
ChangeRate: changeRate,
|
||||||
Volume: absInt(parseWSInt(v["13"])),
|
Volume: absInt(parseWSInt(v["13"])),
|
||||||
TradeMoney: absInt(parseWSInt(v["14"])),
|
TradeMoney: absInt(parseWSInt(v["14"])),
|
||||||
TradeVolume: absInt(parseWSInt(v["15"])),
|
TradeVolume: absInt(parseWSInt(v["15"])),
|
||||||
@@ -458,11 +464,17 @@ func parseRealPrice(code string, v map[string]string) *models.StockPrice {
|
|||||||
func parseExpectedPrice(code string, v map[string]string) *models.StockPrice {
|
func parseExpectedPrice(code string, v map[string]string) *models.StockPrice {
|
||||||
normalized := strings.TrimPrefix(code, "A")
|
normalized := strings.TrimPrefix(code, "A")
|
||||||
normalized = strings.SplitN(normalized, "_", 2)[0]
|
normalized = strings.SplitN(normalized, "_", 2)[0]
|
||||||
|
// 전일대비(v["11"])는 절댓값으로 파싱 후, 등락률(v["12"]) 부호에 맞춰 부호 결정
|
||||||
|
expChangeAbs := absInt(parseWSInt(v["11"]))
|
||||||
|
expChangeRate := parseWSFloat(v["12"])
|
||||||
|
if expChangeRate < 0 {
|
||||||
|
expChangeAbs = -expChangeAbs
|
||||||
|
}
|
||||||
return &models.StockPrice{
|
return &models.StockPrice{
|
||||||
Code: normalized,
|
Code: normalized,
|
||||||
CurrentPrice: absInt(parseWSInt(v["10"])),
|
CurrentPrice: absInt(parseWSInt(v["10"])),
|
||||||
ChangePrice: parseWSInt(v["11"]),
|
ChangePrice: expChangeAbs,
|
||||||
ChangeRate: parseWSFloat(v["12"]),
|
ChangeRate: expChangeRate,
|
||||||
TradeVolume: absInt(parseWSInt(v["15"])),
|
TradeVolume: absInt(parseWSInt(v["15"])),
|
||||||
Volume: absInt(parseWSInt(v["13"])),
|
Volume: absInt(parseWSInt(v["13"])),
|
||||||
TradeTime: v["20"],
|
TradeTime: v["20"],
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func (s *Kospi200Service) GetStocks() ([]models.Kospi200Stock, error) {
|
|||||||
Name: s.StkNm,
|
Name: s.StkNm,
|
||||||
CurPrc: absParseIntSafe(s.CurPrc),
|
CurPrc: absParseIntSafe(s.CurPrc),
|
||||||
PredPreSig: s.PredPreSig,
|
PredPreSig: s.PredPreSig,
|
||||||
PredPre: parseIntSafe(s.PredPre),
|
PredPre: signedChangeBySig(s.PredPre, s.PredPreSig),
|
||||||
FluRt: parseFloatSafe(s.FluRt),
|
FluRt: parseFloatSafe(s.FluRt),
|
||||||
Volume: absParseIntSafe(s.NowTrdeQty),
|
Volume: absParseIntSafe(s.NowTrdeQty),
|
||||||
Open: absParseIntSafe(s.OpenPric),
|
Open: absParseIntSafe(s.OpenPric),
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ func (s *ThemeService) GetThemeStocks(themeCode, dateTp string) (*models.ThemeDe
|
|||||||
Name: s.StkNm,
|
Name: s.StkNm,
|
||||||
CurPrc: absParseIntSafe(s.CurPrc),
|
CurPrc: absParseIntSafe(s.CurPrc),
|
||||||
FluSig: s.FluSig,
|
FluSig: s.FluSig,
|
||||||
PredPre: parseIntSafe(s.PredPre),
|
PredPre: signedChangeBySig(s.PredPre, s.FluSig),
|
||||||
FluRt: parseFloatSafe(strings.TrimPrefix(s.FluRt, "+")),
|
FluRt: parseFloatSafe(strings.TrimPrefix(s.FluRt, "+")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user