주식 각종 지표 업데이트
This commit is contained in:
@@ -1371,6 +1371,56 @@
|
|||||||
AI 투자 코치 패널
|
AI 투자 코치 패널
|
||||||
══════════════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.ai-market-ctx {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: rgba(0, 212, 255, 0.04);
|
||||||
|
border: 1px solid rgba(0, 212, 255, 0.15);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-market-ctx__label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--neon-cyan);
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-market-ctx__chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-market-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 100px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-market-chip strong {
|
||||||
|
color: var(--text-bright);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-market-chip em {
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
.ai-coach-settings {
|
.ai-coach-settings {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import {
|
|||||||
deletePortfolio,
|
deletePortfolio,
|
||||||
upsertCash,
|
upsertCash,
|
||||||
deleteCash,
|
deleteCash,
|
||||||
|
getFearAndGreed,
|
||||||
|
getVix,
|
||||||
|
getTreasury10Y,
|
||||||
|
getWTI,
|
||||||
} from '../../api';
|
} from '../../api';
|
||||||
import Loading from '../../components/Loading';
|
import Loading from '../../components/Loading';
|
||||||
import './Stock.css';
|
import './Stock.css';
|
||||||
@@ -91,6 +95,22 @@ const profitColorClass = (numericValue) => {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getVixLabel = (vix) => {
|
||||||
|
if (vix < 12) return '극히 낮음 (안일 주의)';
|
||||||
|
if (vix < 20) return '정상 (안정적)';
|
||||||
|
if (vix < 30) return '주의 (불확실성 증가)';
|
||||||
|
if (vix < 40) return '높음 (극도의 공포)';
|
||||||
|
return '극단 (패닉)';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFgLabel = (score) => {
|
||||||
|
if (score <= 25) return '극단적 공포';
|
||||||
|
if (score <= 45) return '공포';
|
||||||
|
if (score <= 55) return '중립';
|
||||||
|
if (score <= 75) return '탐욕';
|
||||||
|
return '극단적 탐욕';
|
||||||
|
};
|
||||||
|
|
||||||
/* ── empty portfolio form ────────────────────────────────────────── */
|
/* ── empty portfolio form ────────────────────────────────────────── */
|
||||||
|
|
||||||
const emptyPortfolioForm = {
|
const emptyPortfolioForm = {
|
||||||
@@ -153,6 +173,7 @@ const StockTrade = () => {
|
|||||||
const [aiResult, setAiResult] = useState(null);
|
const [aiResult, setAiResult] = useState(null);
|
||||||
const [aiLoading, setAiLoading] = useState(false);
|
const [aiLoading, setAiLoading] = useState(false);
|
||||||
const [aiError, setAiError] = useState('');
|
const [aiError, setAiError] = useState('');
|
||||||
|
const [marketCtx, setMarketCtx] = useState(null);
|
||||||
|
|
||||||
/* ────────────────────────────────────────────────────────────── */
|
/* ────────────────────────────────────────────────────────────── */
|
||||||
/* AI 투자 (Balance) state */
|
/* AI 투자 (Balance) state */
|
||||||
@@ -228,6 +249,22 @@ const StockTrade = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/* 리포트 탭 진입 시 시장 컨텍스트(VIX, F&G, 국채, WTI) 한 번 로드 */
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeTab !== TAB_REPORT || marketCtx !== null) return;
|
||||||
|
Promise.allSettled([getFearAndGreed(), getVix(), getTreasury10Y(), getWTI()])
|
||||||
|
.then(([fg, vix, t, w]) => {
|
||||||
|
const fgRaw = fg.status === 'fulfilled' ? fg.value : null;
|
||||||
|
const fgScore = fgRaw?.fear_and_greed?.score ?? fgRaw?.score;
|
||||||
|
setMarketCtx({
|
||||||
|
fg: fgScore != null ? Math.round(Number(fgScore)) : null,
|
||||||
|
vix: vix.status === 'fulfilled' ? (vix.value?.value ?? null) : null,
|
||||||
|
treasury: t.status === 'fulfilled' ? (t.value?.value ?? null) : null,
|
||||||
|
wti: w.status === 'fulfilled' ? (w.value?.value ?? null) : null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [activeTab, marketCtx]);
|
||||||
|
|
||||||
/* Auto-refresh portfolio every 3 min (포트폴리오 탭 활성 시) */
|
/* Auto-refresh portfolio every 3 min (포트폴리오 탭 활성 시) */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTab !== TAB_PORTFOLIO) return;
|
if (activeTab !== TAB_PORTFOLIO) return;
|
||||||
@@ -381,7 +418,11 @@ const StockTrade = () => {
|
|||||||
)
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
|
||||||
const prompt = `당신은 한국 주식 전문 투자 코치입니다. 아래 포트폴리오를 분석하여 JSON으로만 답하세요.
|
const marketText = marketCtx
|
||||||
|
? `\n[현재 시장 환경]\nVIX: ${marketCtx.vix != null ? `${marketCtx.vix} (${getVixLabel(marketCtx.vix)})` : '데이터 없음'}\nFear & Greed: ${marketCtx.fg != null ? `${marketCtx.fg}점 (${getFgLabel(marketCtx.fg)})` : '데이터 없음'}\n미국 10년물 국채: ${marketCtx.treasury != null ? `${marketCtx.treasury}%` : '데이터 없음'}\nWTI 유가: ${marketCtx.wti != null ? `$${marketCtx.wti}` : '데이터 없음'}`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const prompt = `당신은 한국 주식 전문 투자 코치입니다. 아래 포트폴리오와 시장 환경을 종합 분석하여 JSON으로만 답하세요.
|
||||||
|
|
||||||
분석 일자: ${today}
|
분석 일자: ${today}
|
||||||
총 매입금액: ${formatNumber(portfolioSummary.total_buy)}원
|
총 매입금액: ${formatNumber(portfolioSummary.total_buy)}원
|
||||||
@@ -391,7 +432,7 @@ const StockTrade = () => {
|
|||||||
총 자산: ${totalAssets != null ? formatNumber(totalAssets) + '원' : '미집계'}
|
총 자산: ${totalAssets != null ? formatNumber(totalAssets) + '원' : '미집계'}
|
||||||
보유 종목 수: ${portfolioHoldings.length}개
|
보유 종목 수: ${portfolioHoldings.length}개
|
||||||
보유 종목:
|
보유 종목:
|
||||||
${holdingsText}
|
${holdingsText}${marketText}
|
||||||
|
|
||||||
반드시 아래 JSON 형식으로만 응답하세요 (코드블록 없이, 모든 텍스트는 한국어로):
|
반드시 아래 JSON 형식으로만 응답하세요 (코드블록 없이, 모든 텍스트는 한국어로):
|
||||||
{
|
{
|
||||||
@@ -1695,6 +1736,37 @@ ${holdingsText}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 시장 컨텍스트 미니 패널 */}
|
||||||
|
{marketCtx && (
|
||||||
|
<div className="ai-market-ctx">
|
||||||
|
<span className="ai-market-ctx__label">시장 환경</span>
|
||||||
|
<div className="ai-market-ctx__chips">
|
||||||
|
{marketCtx.vix != null && (
|
||||||
|
<span className="ai-market-chip">
|
||||||
|
VIX <strong>{marketCtx.vix}</strong>
|
||||||
|
<em>{getVixLabel(marketCtx.vix)}</em>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{marketCtx.fg != null && (
|
||||||
|
<span className="ai-market-chip">
|
||||||
|
F&G <strong>{marketCtx.fg}</strong>
|
||||||
|
<em>{getFgLabel(marketCtx.fg)}</em>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{marketCtx.treasury != null && (
|
||||||
|
<span className="ai-market-chip">
|
||||||
|
10년물 <strong>{marketCtx.treasury}%</strong>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{marketCtx.wti != null && (
|
||||||
|
<span className="ai-market-chip">
|
||||||
|
WTI <strong>${marketCtx.wti}</strong>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* API Key 설정 */}
|
{/* API Key 설정 */}
|
||||||
<div className="ai-coach-settings">
|
<div className="ai-coach-settings">
|
||||||
<label>
|
<label>
|
||||||
|
|||||||
Reference in New Issue
Block a user