시장 주요 지표 참고 추가
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getStockIndices, getStockNews, getFearAndGreed, getVix } from '../../api';
|
||||
import { getStockIndices, getStockNews, getFearAndGreed, getVix, getTreasury10Y, getWTI, getBrent } from '../../api';
|
||||
import Loading from '../../components/Loading';
|
||||
import FearGreedGauge from '../../components/FearGreedGauge';
|
||||
import './Stock.css';
|
||||
@@ -77,12 +77,35 @@ const getDirection = (change, percent, direction) => {
|
||||
return '';
|
||||
};
|
||||
|
||||
const VIX_LEVELS = [
|
||||
{
|
||||
range: '0 – 12', label: '극히 낮음', color: '#22c55e',
|
||||
desc: '시장이 극도로 안정적. 오히려 투자자 안일함의 신호일 수 있어, 갑작스러운 조정에 대비가 필요합니다.',
|
||||
},
|
||||
{
|
||||
range: '12 – 20', label: '정상', color: '#84cc16',
|
||||
desc: '시장이 안정적인 상태. 보통 상승장에서 나타나며, 건강한 변동성 수준입니다.',
|
||||
},
|
||||
{
|
||||
range: '20 – 30', label: '주의', color: '#eab308',
|
||||
desc: '불확실성이 높아지는 구간. 주가와 반대로 움직이며, 단기 바닥 신호로 해석되기도 합니다.',
|
||||
},
|
||||
{
|
||||
range: '30 – 40', label: '높음', color: '#f97316',
|
||||
desc: '극도의 공포가 퍼진 상태. 급격한 매도세가 나타나지만, 역사적으로 역발상 매수 기회가 되기도 합니다.',
|
||||
},
|
||||
{
|
||||
range: '40+', label: '극단', color: '#ef4444',
|
||||
desc: '패닉 수준의 공포. 2008 금융위기·2020 코로나 때 발생. VIX가 꺾이기 시작하면 심리적 진정의 시작입니다.',
|
||||
},
|
||||
];
|
||||
|
||||
const getVixLevel = (score) => {
|
||||
if (score < 12) return { label: '극히 낮음', color: '#22c55e' };
|
||||
if (score < 20) return { label: '정상', color: '#84cc16' };
|
||||
if (score < 30) return { label: '보통', color: '#eab308' };
|
||||
if (score < 40) return { label: '높음', color: '#f97316' };
|
||||
return { label: '극단', color: '#ef4444' };
|
||||
if (score < 12) return VIX_LEVELS[0];
|
||||
if (score < 20) return VIX_LEVELS[1];
|
||||
if (score < 30) return VIX_LEVELS[2];
|
||||
if (score < 40) return VIX_LEVELS[3];
|
||||
return VIX_LEVELS[4];
|
||||
};
|
||||
|
||||
const Stock = () => {
|
||||
@@ -99,6 +122,7 @@ const Stock = () => {
|
||||
|
||||
const [fgData, setFgData] = useState(null);
|
||||
const [vixData, setVixData] = useState(null);
|
||||
const [macroData, setMacroData] = useState({ treasury: null, wti: null, brent: null });
|
||||
|
||||
const combinedNews = useMemo(
|
||||
() => [...newsDomestic, ...newsOverseas],
|
||||
@@ -156,6 +180,14 @@ const Stock = () => {
|
||||
})
|
||||
.catch(() => { });
|
||||
getVix().then(setVixData).catch(() => { });
|
||||
Promise.allSettled([getTreasury10Y(), getWTI(), getBrent()])
|
||||
.then(([t, w, b]) => {
|
||||
setMacroData({
|
||||
treasury: t.status === 'fulfilled' ? t.value : null,
|
||||
wti: w.status === 'fulfilled' ? w.value : null,
|
||||
brent: b.status === 'fulfilled' ? b.value : null,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
const indexOrder = [
|
||||
@@ -307,18 +339,36 @@ const Stock = () => {
|
||||
</div>
|
||||
{vixData ? (
|
||||
<div className="stock-vix">
|
||||
<div className="stock-vix__score" style={{ color: getVixLevel(vixData.value ?? vixData.vix ?? 0).color }}>
|
||||
{vixData.value ?? vixData.vix ?? '--'}
|
||||
<div className="stock-vix__top">
|
||||
<div className="stock-vix__score" style={{ color: getVixLevel(vixData.value ?? 0).color }}>
|
||||
{vixData.value ?? '--'}
|
||||
</div>
|
||||
<div>
|
||||
<p className="stock-vix__label" style={{ color: getVixLevel(vixData.value ?? 0).color }}>
|
||||
{getVixLevel(vixData.value ?? 0).label}
|
||||
</p>
|
||||
{vixData.change != null && (
|
||||
<p className={`stock-vix__change ${vixData.change >= 0 ? 'is-up' : 'is-down'}`}>
|
||||
{vixData.change >= 0 ? '+' : ''}{vixData.change}
|
||||
{vixData.changePercent != null && ` (${vixData.changePercent >= 0 ? '+' : ''}${vixData.changePercent}%)`}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="stock-vix__label" style={{ color: getVixLevel(vixData.value ?? vixData.vix ?? 0).color }}>
|
||||
{getVixLevel(vixData.value ?? vixData.vix ?? 0).label}
|
||||
</p>
|
||||
<div className="stock-vix__legend">
|
||||
<span style={{ color: '#22c55e' }}>{'<12'} 극히낮음</span>
|
||||
<span style={{ color: '#84cc16' }}>12-20 정상</span>
|
||||
<span style={{ color: '#eab308' }}>20-30 보통</span>
|
||||
<span style={{ color: '#f97316' }}>30-40 높음</span>
|
||||
<span style={{ color: '#ef4444' }}>{'40+'} 극단</span>
|
||||
<div className="stock-vix__levels">
|
||||
{VIX_LEVELS.map((level) => (
|
||||
<div
|
||||
key={level.range}
|
||||
className={`stock-vix__level ${level.label === getVixLevel(vixData.value ?? 0).label ? 'is-current' : ''}`}
|
||||
>
|
||||
<div className="stock-vix__level-head">
|
||||
<span className="stock-vix__level-dot" style={{ background: level.color }} />
|
||||
<span className="stock-vix__level-label" style={{ color: level.color }}>{level.label}</span>
|
||||
<span className="stock-vix__level-range">{level.range}</span>
|
||||
</div>
|
||||
<p className="stock-vix__level-desc">{level.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -327,6 +377,83 @@ const Stock = () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 매크로 지표 섹션 */}
|
||||
<section className="stock-panel stock-panel--wide">
|
||||
<div className="stock-panel__head">
|
||||
<div>
|
||||
<p className="stock-panel__eyebrow">글로벌 매크로</p>
|
||||
<h3>매크로 지표</h3>
|
||||
<p className="stock-panel__sub">금리·원자재 등 주요 거시경제 지표를 확인합니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="stock-macro-grid">
|
||||
<div className="stock-macro-card">
|
||||
<p className="stock-macro-card__title">미국 10년물 국채 금리</p>
|
||||
<div className="stock-macro-card__value">
|
||||
{macroData.treasury ? `${macroData.treasury.value}%` : '--'}
|
||||
</div>
|
||||
{macroData.treasury?.change != null && (
|
||||
<p className={`stock-macro-card__change ${macroData.treasury.change >= 0 ? 'is-up' : 'is-down'}`}>
|
||||
{macroData.treasury.change >= 0 ? '+' : ''}{macroData.treasury.change}
|
||||
{macroData.treasury.changePercent != null && ` (${macroData.treasury.changePercent >= 0 ? '+' : ''}${macroData.treasury.changePercent}%)`}
|
||||
</p>
|
||||
)}
|
||||
<p className="stock-macro-card__desc">금리 상승 시 주식 밸류에이션 압박. 4% 이상 지속은 주식 하락 압력 신호. 단기 급등은 인플레이션 우려를 반영합니다.</p>
|
||||
</div>
|
||||
<div className="stock-macro-card">
|
||||
<p className="stock-macro-card__title">WTI 유가</p>
|
||||
<div className="stock-macro-card__value">
|
||||
{macroData.wti ? `$${macroData.wti.value}` : '--'}
|
||||
</div>
|
||||
{macroData.wti?.change != null && (
|
||||
<p className={`stock-macro-card__change ${macroData.wti.change >= 0 ? 'is-up' : 'is-down'}`}>
|
||||
{macroData.wti.change >= 0 ? '+' : ''}{macroData.wti.change}
|
||||
{macroData.wti.changePercent != null && ` (${macroData.wti.changePercent >= 0 ? '+' : ''}${macroData.wti.changePercent}%)`}
|
||||
</p>
|
||||
)}
|
||||
<p className="stock-macro-card__desc">에너지 인플레이션 지표. $80 이상 지속 시 물가 상승 우려 확대. 급락은 경기침체 가능성을 반영하기도 합니다.</p>
|
||||
</div>
|
||||
<div className="stock-macro-card">
|
||||
<p className="stock-macro-card__title">Brent 유가</p>
|
||||
<div className="stock-macro-card__value">
|
||||
{macroData.brent ? `$${macroData.brent.value}` : '--'}
|
||||
</div>
|
||||
{macroData.brent?.change != null && (
|
||||
<p className={`stock-macro-card__change ${macroData.brent.change >= 0 ? 'is-up' : 'is-down'}`}>
|
||||
{macroData.brent.change >= 0 ? '+' : ''}{macroData.brent.change}
|
||||
{macroData.brent.changePercent != null && ` (${macroData.brent.changePercent >= 0 ? '+' : ''}${macroData.brent.changePercent}%)`}
|
||||
</p>
|
||||
)}
|
||||
<p className="stock-macro-card__desc">국제 기준 유가. WTI와 함께 에너지 시장 방향을 파악하는 데 활용. 지정학 리스크 시 WTI 대비 프리미엄 형성.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 시장 건강 지표 (Placeholder) */}
|
||||
<section className="stock-panel stock-panel--wide">
|
||||
<div className="stock-panel__head">
|
||||
<div>
|
||||
<p className="stock-panel__eyebrow">시장 건강</p>
|
||||
<h3>시장 건강 지표</h3>
|
||||
<p className="stock-panel__sub">백엔드 API 연동 후 실시간 데이터를 표시합니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="stock-health-grid">
|
||||
<div className="stock-placeholder-card">
|
||||
<p className="stock-placeholder-card__title">ADR (등락주선 비율)</p>
|
||||
<div className="stock-placeholder-card__status">🔧 데이터 준비 중</div>
|
||||
<p className="stock-placeholder-card__desc">일정 기간 상승종목 ÷ (상승+하락) 종목 비율. 0.5 이상 = 폭넓은 상승장. 0.3 이하 = 일부 대형주만 오르는 약세 신호.</p>
|
||||
<code className="stock-placeholder-card__api">GET /api/stock/adr</code>
|
||||
</div>
|
||||
<div className="stock-placeholder-card">
|
||||
<p className="stock-placeholder-card__title">고객예탁금 / 신용융자</p>
|
||||
<div className="stock-placeholder-card__status">🔧 데이터 준비 중</div>
|
||||
<p className="stock-placeholder-card__desc">고객예탁금 증가 = 투자 대기자금 유입 = 강세. 신용융자 급증 = 과열 경고. 예탁금 감소 + 신용 급증 = 위험 구간.</p>
|
||||
<code className="stock-placeholder-card__api">GET /api/stock/deposit</code>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="stock-panel stock-panel--wide">
|
||||
<div className="stock-panel__head">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user