feat(market): 거래대금 상위 + 52주 위치 게이지

- /api/markets/movers: by_trading_value (close*volume DESC) 카테고리 추가
- MarketsOverview: 4 컬럼 그리드 (등락 상위/하위/거래대금/거래량), 거래대금은 억·조 표기
- SymbolSidebar: 52주 범위 내 현재가 위치 게이지 (그라데이션 바 + 화이트 점)
This commit is contained in:
claude-owner
2026-05-28 15:21:27 +09:00
parent aa58761176
commit 645e93e7bf
4 changed files with 114 additions and 39 deletions

View File

@@ -4,10 +4,11 @@ GET /api/markets/movers
?market=KOSPI|KOSDAQ|ALL (기본 ALL)
?limit=10 (각 카테고리 별 상위 N)
응답 (3 카테고리):
- gainers: 등락률 상위 (전일종가 대비 상승%)
- losers : 등락률 하위 (하락%)
- volume : 거래량 상위 (당일 volume)
응답 (4 카테고리):
- gainers : 등락률 상위 (전일종가 대비 상승%)
- losers : 등락률 하위 (하락%)
- by_volume : 거래량 상위 (당일 volume)
- by_trading_value: 거래대금 상위 (close × volume, 원 단위)
ohlcv_daily 에서 가장 최근 2 거래일을 잡아 비교한다. 데이터가 한 종목당
2 일치 이상 없을 때는 그 종목은 자연 누락된다.
@@ -64,7 +65,8 @@ def _movers_sql(where_market: str) -> str:
CASE WHEN p.close1 > 0
THEN (l.close0 - p.close1) / p.close1 * 100
ELSE NULL
END AS pct_change
END AS pct_change,
(l.close0 * l.volume0) AS trading_value
FROM symb s
JOIN latest l ON l.code = s.code
JOIN prev p ON p.code = s.code
@@ -97,6 +99,10 @@ def movers(
text(base_sql + " ORDER BY volume DESC NULLS LAST LIMIT :lim"),
params,
).all()
by_trading_value = conn.execute(
text(base_sql + " ORDER BY trading_value DESC NULLS LAST LIMIT :lim"),
params,
).all()
def _row(r) -> dict:
return {
@@ -107,6 +113,7 @@ def movers(
"prev_close": float(r[4]) if r[4] is not None else None,
"volume": int(r[5]) if r[5] is not None else None,
"pct_change": float(r[6]) if r[6] is not None else None,
"trading_value": float(r[7]) if r[7] is not None else None,
}
return {
@@ -115,6 +122,7 @@ def movers(
"gainers": [_row(r) for r in gainers],
"losers": [_row(r) for r in losers],
"by_volume": [_row(r) for r in by_volume],
"by_trading_value": [_row(r) for r in by_trading_value],
}