8개 커스텀 훅으로 state/handler 로직 추출: - usePortfolio: 포트폴리오 CRUD, 예수금, 브로커 그룹 - useSellHistory: 매도 내역 CRUD, 드로어/폼 상태 - useAiCoach: AI 코치 분석 + 캐시 - useAssetHistory: 자산 추이 차트 데이터 - useMarketContext: VIX/F&G/국채/WTI 시장 데이터 - useAiBalance: AI 모의투자 잔고, 수동 주문 - useReportData: 리포트 정렬, 차트, 집중도 분석 - useAdvisor: 어드바이저 프롬프트 빌더 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
85 lines
3.1 KiB
JavaScript
85 lines
3.1 KiB
JavaScript
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,
|
|
};
|
|
}
|