From f7175ad80cc5d1ce52753d1c9e143fbbb5e6d11d Mon Sep 17 00:00:00 2001 From: gahusb Date: Wed, 25 Mar 2026 04:39:13 +0900 Subject: [PATCH] =?UTF-8?q?AI=20=EC=96=B4=EB=93=9C=EB=B0=94=EC=9D=B4?= =?UTF-8?q?=EC=A0=80=20=ED=83=AD=EC=9D=84=20=ED=94=84=EB=A1=AC=ED=94=84?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1/=EB=B3=B5=EC=82=AC=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gemini API 직접 호출 대신 포트폴리오 데이터 기반 전문가 프롬프트를 자동 생성하고 클립보드에 복사하는 방식으로 변경. - 보유 종목, 평균매입가, 현재가, 손익, 예수금, 시장 지표 포함 - Gemini/ChatGPT 바로가기 링크 제공 - 프롬프트 미리보기 영역 추가 Co-Authored-By: Claude Sonnet 4.6 --- src/pages/stock/Stock.css | 33 +++++ src/pages/stock/StockTrade.jsx | 247 +++++++++++++++++++-------------- 2 files changed, 175 insertions(+), 105 deletions(-) diff --git a/src/pages/stock/Stock.css b/src/pages/stock/Stock.css index 8858f0a..03112d2 100644 --- a/src/pages/stock/Stock.css +++ b/src/pages/stock/Stock.css @@ -2740,6 +2740,39 @@ flex-shrink: 0; } +.advisor-prompt__toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.advisor-prompt__info { + font-size: 12px; + color: var(--text-muted); +} + +.button.primary.small.is-copied { + background: #34d399; + color: #0f172a; +} + +.advisor-prompt__preview { + white-space: pre-wrap; + word-break: break-word; + font-family: 'Courier New', Courier, monospace; + font-size: 12px; + line-height: 1.7; + color: var(--text); + background: rgba(255, 255, 255, 0.03); + border: 1px solid var(--line); + border-radius: 12px; + padding: 16px 20px; + max-height: 520px; + overflow-y: auto; +} + .advisor-panel__timestamp { font-size: 11px; color: var(--text-muted); diff --git a/src/pages/stock/StockTrade.jsx b/src/pages/stock/StockTrade.jsx index ca34457..bf98b22 100644 --- a/src/pages/stock/StockTrade.jsx +++ b/src/pages/stock/StockTrade.jsx @@ -19,7 +19,6 @@ import { addSellHistory, updateSellHistory, deleteSellHistory, - getAiAnalysis, } from '../../api'; import Loading from '../../components/Loading'; import './Stock.css'; @@ -92,38 +91,6 @@ const toNumeric = (value) => { return Number.isNaN(numeric) ? null : numeric; }; -/* ── AdvisorMarkdown — 경량 마크다운 렌더러 ──────────────────────── */ - -const AdvisorMarkdown = ({ text }) => { - if (!text) return null; - const lines = text.split('\n'); - const elements = []; - let i = 0; - while (i < lines.length) { - const line = lines[i]; - if (line.startsWith('### ')) { - elements.push(

{line.slice(4)}

); - } else if (line.startsWith('## ')) { - elements.push(

{line.slice(3)}

); - } else if (line.startsWith('---')) { - elements.push(
); - } else if (line.trim() === '') { - elements.push(
); - } else { - // 인라인 마크다운: **bold**, *italic*, 🎯 등 이모지 보존 - const rendered = line - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/\*(.+?)\*/g, '$1') - .replace(/`(.+?)`/g, '$1'); - elements.push( -

- ); - } - i++; - } - return

{elements}
; -}; - /* ── Chart colors ──────────────────────────────────────────────── */ const CHART_COLORS = ['#818cf8', '#fbbf24', '#34d399', '#f472b6', '#fb923c', '#a78bfa', '#38bdf8', '#4ade80']; @@ -237,10 +204,8 @@ const StockTrade = () => { const [sellFormSaving, setSellFormSaving] = useState(false); const [sellFormError, setSellFormError] = useState(''); - /* AI 전문가 분석 (Gemini Pro Advisor) */ - const [advisorData, setAdvisorData] = useState(null); // { analysis, generated_at, cached, holdings_count } - const [advisorLoading, setAdvisorLoading] = useState(false); - const [advisorError, setAdvisorError] = useState(''); + /* AI 어드바이저 — 프롬프트 복사 */ + const [advisorCopied, setAdvisorCopied] = useState(false); /* Cash (예수금) form */ const [cashForm, setCashForm] = useState({ broker: '', cash: '' }); @@ -322,19 +287,85 @@ const StockTrade = () => { } }, []); - const loadAdvisorAnalysis = useCallback(async (force = false) => { - setAdvisorLoading(true); - setAdvisorError(''); - try { - const data = await getAiAnalysis(force); - if (data?.error) throw new Error(data.error); - setAdvisorData(data); - } catch (err) { - setAdvisorError(err?.message ?? '분석 중 오류가 발생했습니다.'); - } finally { - setAdvisorLoading(false); - } - }, []); + /* AI 어드바이저: 포트폴리오 기반 프롬프트 생성 */ + const buildAdvisorPrompt = useCallback(() => { + const today = new Date().toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' }); + + const holdingsLines = portfolioHoldings.map((h) => { + const cp = h.current_price != null ? `${formatNumber(h.current_price)}원` : '시세 미조회'; + const rate = h.profit_rate != null ? formatPercent(h.profit_rate) : '미조회'; + const profit = h.profit_amount != null ? `(${h.profit_amount >= 0 ? '+' : ''}${formatNumber(h.profit_amount)}원)` : ''; + return `- **${h.name ?? h.ticker}** (${h.ticker ?? ''}) | 계좌: ${h.broker ?? '-'} + 수량 ${h.quantity}주 | 평균매입가 ${formatNumber(h.avg_price)}원 | 현재가 ${cp} | 손익 ${rate} ${profit}`; + }).join('\n'); + + const cashLines = cashList.map((c) => `- ${c.broker}: ${formatNumber(c.cash)}원`).join('\n') || '- 없음'; + + const marketLines = marketCtx + ? [ + `VIX: ${marketCtx.vix != null ? `${marketCtx.vix} (${getVixLabel(marketCtx.vix)})` : '데이터 없음'}`, + `공포탐욕지수: ${marketCtx.fg != null ? `${marketCtx.fg}점 (${getFgLabel(marketCtx.fg)})` : '데이터 없음'}`, + `미 10년물 국채: ${marketCtx.treasury != null ? `${marketCtx.treasury}%` : '데이터 없음'}`, + `WTI 유가: ${marketCtx.wti != null ? `$${marketCtx.wti}` : '데이터 없음'}`, + ].join('\n') + : '시장 데이터 미로드'; + + return `당신은 15년 이상 경력의 한국 주식시장 전문 애널리스트입니다. +오늘은 ${today}입니다. 아래 포트폴리오 정보와 시장 환경을 바탕으로 전문가 분석을 제공해주세요. + +--- + +## 📊 현재 시장 환경 + +${marketLines} + +--- + +## 💼 보유 포트폴리오 + +### 보유 종목 (${portfolioHoldings.length}개) + +${holdingsLines || '보유 종목 없음'} + +### 포트폴리오 요약 + +- 총 매입금액: ${formatNumber(portfolioSummary.total_buy)}원 +- 총 평가금액: ${formatNumber(portfolioSummary.total_eval)}원 +- 총 손익: ${formatNumber(portfolioSummary.total_profit)}원 (수익률: ${formatPercent(portfolioSummary.total_profit_rate)}) +- 예수금 합계: ${totalCash != null ? formatNumber(totalCash) + '원' : '미입력'} +- 총 자산: ${totalAssets != null ? formatNumber(totalAssets) + '원' : '미집계'} + +### 예수금 현황 + +${cashLines} + +--- + +## 🔍 분석 요청 + +다음 형식으로 명확하게 작성해주세요: + +### 📈 오늘의 시장 환경 +시장 환경 데이터를 바탕으로 오늘 한국 주식시장의 전반적인 분위기와 주요 이슈를 2-3문장으로 요약하세요. + +### 🔍 종목별 분석 및 행동 지침 +각 보유 종목에 대해 아래 형식으로 작성하세요: + +**[종목명 (티커)]** +- 현황: 현재 손익 상태와 포지션 평가 +- 분석: 업황·섹터 동향, 주요 리스크/기회 +- 🎯 행동 지침: **[매도 / 보유 / 추가매수 / 분할매도]** — 구체적 이유와 목표 참고 가격대 + +### 💼 포트폴리오 종합 의견 +전체 포트폴리오의 섹터 편중, 리밸런싱 필요 여부, 현금 비중 조언을 작성하세요. + +### ⚠️ 오늘 주의해야 할 리스크 +매크로·섹터·개별 종목 측면에서 오늘 특히 주의할 리스크를 2-3가지 나열하세요. + +--- +분석은 반드시 한국어로, 구체적인 수치와 근거를 들어 전문적으로 작성해주세요. +투자 결정은 최종적으로 투자자 본인이 판단함을 명시하세요.`; + }, [portfolioHoldings, portfolioSummary, cashList, totalCash, totalAssets, marketCtx]); const loadBalance = useCallback(async () => { setBalanceLoading(true); @@ -414,10 +445,10 @@ const StockTrade = () => { loadBalance(); } else if (activeTab === TAB_REPORT && !portfolioLoaded) { loadPortfolio(); - } else if (activeTab === TAB_ADVISOR && !advisorData) { - loadAdvisorAnalysis(); + } else if (activeTab === TAB_ADVISOR && !portfolioLoaded) { + loadPortfolio(); } - }, [activeTab, portfolioLoaded, balanceLoaded, advisorData, loadPortfolio, loadBalance, loadSellHistory, loadAdvisorAnalysis]); + }, [activeTab, portfolioLoaded, balanceLoaded, loadPortfolio, loadBalance, loadSellHistory]); /* 자산 추이: 포트폴리오 탭 첫 진입 또는 기간 변경 시 로드 */ useEffect(() => { @@ -2046,77 +2077,83 @@ ${holdingsText}${marketText} )} {/* ════════════════════════════════════════════════════════ - TAB 4: AI 어드바이저 (Gemini Pro) + TAB 4: AI 어드바이저 — 프롬프트 생성/복사 ════════════════════════════════════════════════════════ */} {activeTab === TAB_ADVISOR && (
{/* 헤더 */}
- Gemini Pro -

AI 전문가 분석

+ AI 어드바이저 +

포트폴리오 분석 프롬프트

- 보유 종목 현재가와 오늘의 뉴스를 기반으로 전문가 관점의 매매 조언을 제공합니다. + 보유 종목 정보를 담은 전문가용 프롬프트를 생성합니다. + 복사 후 Gemini, ChatGPT 등에 붙여넣어 분석을 받아보세요.

- {advisorData && ( - - {advisorData.cached ? '🗂 캐시' : '✨ 신규'} ·{' '} - {new Date(advisorData.generated_at).toLocaleTimeString('ko-KR', { - hour: '2-digit', minute: '2-digit', - })} 기준 - - )} - + Gemini 열기 ↗ + + + ChatGPT 열기 ↗ +
- {/* 로딩 */} - {advisorLoading && ( -
-
-
-

Gemini Pro가 포트폴리오를 분석 중입니다...

-

현재가 조회 · 뉴스 분석 · 전략 수립

-
+ {portfolioLoading && ( +
+
)} - {/* 에러 */} - {!advisorLoading && advisorError && ( -
- ⚠️ {advisorError} - -
- )} - - {/* 분석 결과 */} - {!advisorLoading && !advisorError && advisorData && ( -
- -

- ※ 이 분석은 AI가 생성한 참고 자료이며 투자 결정은 본인의 판단과 책임 하에 이루어져야 합니다. -

-
- )} - - {/* 초기 상태 */} - {!advisorLoading && !advisorError && !advisorData && ( + {!portfolioLoading && portfolioHoldings.length === 0 && (
- 🧠 -

아직 분석 결과가 없습니다.

- + 📋 +

포트폴리오 탭에서 보유 종목을 먼저 등록해주세요.

+
+ )} + + {!portfolioLoading && portfolioHoldings.length > 0 && ( +
+ {/* 복사 버튼 */} +
+ + 종목 {portfolioHoldings.length}개 · 총 자산 {totalAssets != null ? formatNumber(totalAssets) + '원' : '미집계'} + + +
+ + {/* 프롬프트 미리보기 */} +
{buildAdvisorPrompt()}
+ +

+ ※ 이 프롬프트를 AI에 붙여넣으면 전문가 관점의 매매 조언을 받을 수 있습니다. + 투자 결정은 최종적으로 본인의 판단과 책임 하에 이루어져야 합니다. +

)}