diff --git a/src/pages/stock/StockTrade.jsx b/src/pages/stock/StockTrade.jsx index c9f117a..d801e9c 100644 --- a/src/pages/stock/StockTrade.jsx +++ b/src/pages/stock/StockTrade.jsx @@ -6,7 +6,7 @@ import SwipeableView from '../../components/SwipeableView'; import { formatNumber, formatPercent, toNumeric, profitColorClass, - TAB_PORTFOLIO, TAB_AI, TAB_REPORT, TAB_ADVISOR, + TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, } from './stockUtils'; /* ── hooks ──────────────────────────────────────────────────────── */ @@ -15,13 +15,11 @@ import useSellHistory from './hooks/useSellHistory'; import useAiCoach from './hooks/useAiCoach'; import useAssetHistory from './hooks/useAssetHistory'; import useMarketContext from './hooks/useMarketContext'; -import useAiBalance from './hooks/useAiBalance'; import useReportData from './hooks/useReportData'; import useAdvisor from './hooks/useAdvisor'; /* ── tab components ─────────────────────────────────────────────── */ import PortfolioTab from './components/PortfolioTab'; -import AiTradeTab from './components/AiTradeTab'; import ReportTab from './components/ReportTab'; import AdvisorTab from './components/AdvisorTab'; import SellHistoryDrawer from './components/SellHistoryDrawer'; @@ -32,8 +30,8 @@ const StockTrade = () => { const [activeTab, setActiveTab] = React.useState(TAB_REPORT); const isMobile = useIsMobile(); - const TAB_ORDER = [TAB_PORTFOLIO, TAB_AI, TAB_REPORT, TAB_ADVISOR]; - const tabLabels = ['포트폴리오', 'AI 트레이드', '리포트', '어드바이저']; + const TAB_ORDER = [TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR]; + const tabLabels = ['포트폴리오', '리포트', '어드바이저']; const tabIndex = TAB_ORDER.indexOf(activeTab); const handleTabChange = useCallback((idx) => setActiveTab(TAB_ORDER[idx]), []); // eslint-disable-line react-hooks/exhaustive-deps @@ -49,7 +47,6 @@ const StockTrade = () => { totalAssets: pf.totalAssets, marketCtx, }); - const aib = useAiBalance(); const report = useReportData({ portfolioHoldings: pf.portfolioHoldings, portfolioSummary: pf.portfolioSummary, @@ -97,12 +94,10 @@ const StockTrade = () => { if (activeTab === TAB_PORTFOLIO && !pf.portfolioLoaded) { pf.loadPortfolio(); sell.loadSellHistory(); - } else if (activeTab === TAB_AI && !aib.balanceLoaded) { - aib.loadBalance(); } else if ((activeTab === TAB_REPORT || activeTab === TAB_ADVISOR) && !pf.portfolioLoaded) { pf.loadPortfolio(); } - }, [activeTab, pf.portfolioLoaded, aib.balanceLoaded]); // eslint-disable-line react-hooks/exhaustive-deps + }, [activeTab, pf.portfolioLoaded]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { if (activeTab === TAB_PORTFOLIO) asset.loadAssetHistory(asset.assetHistoryDays); @@ -135,42 +130,29 @@ const StockTrade = () => {
-

- {activeTab === TAB_AI ? 'AI 투자 요약' : '쟁승토리 계좌 요약'} -

- {activeTab === TAB_PORTFOLIO || activeTab === TAB_REPORT ? ( -
-
총 매입{formatNumber(pf.portfolioSummary.total_buy)}
-
총 평가{formatNumber(pf.portfolioSummary.total_eval)}
-
- 총 손익 - - {formatNumber(pf.portfolioSummary.total_profit)} - {pf.portfolioSummary.total_profit_rate != null && ( - - ({formatPercent(pf.portfolioSummary.total_profit_rate)}) - - )} - -
-
보유 종목{pf.portfolioHoldings.length}
- {pf.totalCash != null && ( -
예수금 합계{formatNumber(pf.totalCash)}원
- )} - {pf.totalAssets != null && ( -
총 자산{formatNumber(pf.totalAssets)}원
- )} +

쟁승토리 계좌 요약

+
+
총 매입{formatNumber(pf.portfolioSummary.total_buy)}
+
총 평가{formatNumber(pf.portfolioSummary.total_eval)}
+
+ 총 손익 + + {formatNumber(pf.portfolioSummary.total_profit)} + {pf.portfolioSummary.total_profit_rate != null && ( + + ({formatPercent(pf.portfolioSummary.total_profit_rate)}) + + )} +
- ) : ( -
-
총 평가금액{formatNumber(aib.totalEval)}
-
예수금{formatNumber(aib.deposit)}
-
보유 종목{aib.holdings.length}
-
- )} - {activeTab === TAB_AI && aib.summary.note ? ( -

{aib.summary.note}

- ) : null} +
보유 종목{pf.portfolioHoldings.length}
+ {pf.totalCash != null && ( +
예수금 합계{formatNumber(pf.totalCash)}원
+ )} + {pf.totalAssets != null && ( +
총 자산{formatNumber(pf.totalAssets)}원
+ )} +
@@ -182,11 +164,9 @@ const StockTrade = () => { label: tabLabels[i], content: tabId === TAB_PORTFOLIO ? - : tabId === TAB_AI - ? - : tabId === TAB_REPORT - ? - : , + : tabId === TAB_REPORT + ? + : , }))} activeIndex={tabIndex} onTabChange={handleTabChange} @@ -196,7 +176,6 @@ const StockTrade = () => {
{[ { id: TAB_PORTFOLIO, icon: '💼', label: '쟁승토리 계좌', badge: pf.portfolioHoldings.length || null }, - { id: TAB_AI, icon: '🤖', label: 'AI 투자', sub: '모의투자' }, { id: TAB_REPORT, icon: '📊', label: '리포트', sub: '분석·AI코치' }, { id: TAB_ADVISOR, icon: '🧠', label: 'AI 어드바이저', sub: 'Gemini Pro', className: 'stock-main-tab--advisor' }, ].map(({ id, icon, label, sub, badge, className: cls }) => ( @@ -217,7 +196,6 @@ const StockTrade = () => { {activeTab === TAB_PORTFOLIO && ( )} - {activeTab === TAB_AI && } {activeTab === TAB_REPORT && } {activeTab === TAB_ADVISOR && } diff --git a/src/pages/stock/components/AiTradeTab.jsx b/src/pages/stock/components/AiTradeTab.jsx deleted file mode 100644 index 4697468..0000000 --- a/src/pages/stock/components/AiTradeTab.jsx +++ /dev/null @@ -1,220 +0,0 @@ -import React from 'react'; -import { - formatNumber, formatPercent, - getQty, getBuyPrice, getCurrentPrice, getProfitRate, getProfitLoss, - toNumeric, profitColorClass, -} from '../stockUtils'; - -const AiTradeTab = ({ aib }) => ( - <> - {aib.balanceError ?

{aib.balanceError}

: null} - - {/* AI Balance section */} -
-
-
-

AI 모의투자

-

보유 현황

-

- AI가 운용 중인 모의투자 계좌의 잔고와 보유 종목을 확인합니다. -

-
-
- {aib.balanceLoading ? ( - 조회 중 - ) : null} - -
-
-
-
- {[ - { label: '총 평가', value: aib.totalEval }, - { label: '예수금', value: aib.deposit }, - ].map((item) => ( -
- {item.label} - {formatNumber(item.value)} -
- ))} -
- {aib.holdings.length ? ( -
- {aib.holdings.map((item, idx) => { - const profitLoss = getProfitLoss(item); - const profitLossNumeric = toNumeric(profitLoss); - const profitClass = profitColorClass(profitLossNumeric); - const profitRate = getProfitRate(item); - const profitRateNumeric = toNumeric(profitRate); - const profitRateClass = profitColorClass(profitRateNumeric); - return ( -
-
-

- {item.name ?? item.code ?? 'N/A'} -

- - {item.code ?? ''} - -
-
- 수량 - {formatNumber(getQty(item))} -
-
- 매입가 - {formatNumber(getBuyPrice(item))} -
-
- 현재가 - {formatNumber(getCurrentPrice(item))} -
-
- 평가금액 - - {getCurrentPrice(item) != null && getQty(item) != null - ? formatNumber(toNumeric(getCurrentPrice(item)) * toNumeric(getQty(item))) - : '-'} - -
-
- 수익률 - - {formatPercent(profitRate)} - -
-
- 평가손익 - - {formatNumber(profitLoss)} - -
-
- ); - })} -
- ) : ( -

보유 종목이 없습니다.

- )} -
-
- - {/* Manual order section */} -
-
-
-

수동 주문

-

직접 매수/매도

-

- 종목명 또는 종목코드를 입력하고 매수/매도 주문을 요청합니다. -

-
-
-
- - - - - - {aib.manualError ? ( -

{aib.manualError}

- ) : null} - {aib.manualResult ? ( -
-

요청 결과

-
-                            {typeof aib.manualResult === 'string'
-                                ? aib.manualResult
-                                : JSON.stringify(aib.manualResult, null, 2)}
-                        
-
- ) : null} -
-
- - {/* KIS modal */} - {aib.kisModal ? ( -
-
aib.setKisModal('')} - /> -
-
-

주문 결과

- -
-
{aib.kisModal}
-
-
- ) : null} - -); - -export default AiTradeTab; diff --git a/src/pages/stock/hooks/useAiBalance.js b/src/pages/stock/hooks/useAiBalance.js deleted file mode 100644 index d700e1d..0000000 --- a/src/pages/stock/hooks/useAiBalance.js +++ /dev/null @@ -1,84 +0,0 @@ -import { useState, useCallback, useMemo } from 'react'; -import { getTradeBalance, createTradeOrder } from '../../../api'; -import { getQty, getBuyPrice, getCurrentPrice, getProfitRate, getProfitLoss } from '../stockUtils'; - -export default function useAiBalance() { - const [balance, setBalance] = useState(null); - const [balanceLoading, setBalanceLoading] = useState(false); - const [balanceError, setBalanceError] = useState(''); - const [balanceLoaded, setBalanceLoaded] = useState(false); - - const [manualForm, setManualForm] = useState({ - code: '', - qty: 1, - price: 0, - type: 'buy', - }); - const [manualLoading, setManualLoading] = useState(false); - const [manualError, setManualError] = useState(''); - const [manualResult, setManualResult] = useState(null); - const [kisModal, setKisModal] = useState(''); - - const loadBalance = useCallback(async () => { - setBalanceLoading(true); - setBalanceError(''); - try { - const data = await getTradeBalance(); - setBalance(data); - setBalanceLoaded(true); - } catch (err) { - setBalanceError(err?.message ?? String(err)); - } finally { - setBalanceLoading(false); - } - }, []); - - const submitManualOrder = async (event) => { - event.preventDefault(); - setManualLoading(true); - setManualError(''); - setManualResult(null); - try { - const payload = { - ticker: manualForm.code.trim(), - action: manualForm.type === 'sell' ? 'SELL' : 'BUY', - quantity: Number(manualForm.qty), - price: Number(manualForm.price), - }; - const result = await createTradeOrder(payload); - setManualResult(result ?? { ok: true }); - if (result?.kis_result !== undefined) { - const message = - typeof result.kis_result === 'string' - ? result.kis_result - : JSON.stringify(result.kis_result, null, 2); - setKisModal(message); - } - await loadBalance(); - } catch (err) { - setManualError(err?.message ?? String(err)); - } finally { - setManualLoading(false); - } - }; - - /* derived */ - const holdings = useMemo(() => { - if (!balance) return []; - if (Array.isArray(balance.holdings)) return balance.holdings; - if (Array.isArray(balance.positions)) return balance.positions; - if (Array.isArray(balance.items)) return balance.items; - return []; - }, [balance]); - - const summary = balance?.summary ?? {}; - const totalEval = summary.total_eval ?? balance?.total_eval ?? balance?.total_value; - const deposit = summary.deposit ?? balance?.deposit ?? balance?.available_cash; - - return { - balance, balanceLoading, balanceError, balanceLoaded, loadBalance, - holdings, summary, totalEval, deposit, - manualForm, setManualForm, manualLoading, manualError, manualResult, - kisModal, setKisModal, submitManualOrder, - }; -} diff --git a/src/pages/stock/stockUtils.js b/src/pages/stock/stockUtils.js index 15ebba7..8b0533f 100644 --- a/src/pages/stock/stockUtils.js +++ b/src/pages/stock/stockUtils.js @@ -128,6 +128,5 @@ export const emptySellForm = () => ({ /* ── TAB IDs ─────────────────────────────────────────────────────── */ export const TAB_PORTFOLIO = 'portfolio'; -export const TAB_AI = 'ai'; export const TAB_REPORT = 'report'; export const TAB_ADVISOR = 'advisor';