/** * 자산 현황 페이지 로직 * - 요약 카드, 예수금, 보유 종목, 미체결/체결내역 */ // ----------------------------------- // 초기화 // ----------------------------------- document.addEventListener('DOMContentLoaded', () => { loadSummary(); loadCash(); loadPending(); }); // ----------------------------------- // 요약 카드 + 보유 종목 (GET /api/account/balance) // ----------------------------------- async function loadSummary() { try { const res = await fetch('/api/account/balance'); const data = await res.json(); if (!res.ok) { renderSummaryError(data.error || '잔고 조회 실패'); return; } renderSummaryCards(data); renderHoldings(data.stocks); } catch (e) { renderSummaryError('네트워크 오류: ' + e.message); } } function renderSummaryCards(data) { const plRate = parseFloat(data.totPrftRt || '0'); const plAmt = parseInt(data.totEvltPl || '0'); const plClass = plRate >= 0 ? 'text-red-500' : 'text-blue-500'; const cards = [ { label: '추정예탁자산', value: parseInt(data.prsmDpstAsetAmt || '0').toLocaleString('ko-KR') + '원', cls: 'text-gray-800', }, { label: '총평가금액', value: parseInt(data.totEvltAmt || '0').toLocaleString('ko-KR') + '원', cls: 'text-gray-800', }, { label: '총평가손익', value: (plAmt >= 0 ? '+' : '') + plAmt.toLocaleString('ko-KR') + '원', cls: plClass, }, { label: '수익률', value: (plRate >= 0 ? '+' : '') + plRate.toFixed(2) + '%', cls: plClass, }, ]; document.getElementById('summaryCards').innerHTML = cards.map(c => `

${c.label}

${c.value}

`).join(''); } function renderSummaryError(msg) { document.getElementById('summaryCards').innerHTML = `
${msg}
`; document.getElementById('holdingsTable').innerHTML = ''; } function renderHoldings(stocks) { const tbody = document.getElementById('holdingsTable'); if (!stocks || stocks.length === 0) { tbody.innerHTML = '
보유 종목이 없습니다.
'; return; } tbody.innerHTML = stocks.map(s => { const prft = parseFloat(s.prftRt || '0'); const evlt = parseInt(s.evltvPrft || '0'); const cls = prft >= 0 ? 'text-red-500' : 'text-blue-500'; const sign = prft >= 0 ? '+' : ''; return `
${s.stkNm} ${parseInt(s.rmndQty || '0').toLocaleString('ko-KR')}주 ${parseInt(s.purPric || '0').toLocaleString('ko-KR')} ${parseInt(s.curPrc || '0').toLocaleString('ko-KR')} ${(evlt >= 0 ? '+' : '') + evlt.toLocaleString('ko-KR')}원 ${sign}${prft.toFixed(2)}%
`; }).join(''); } // ----------------------------------- // 예수금 카드 (GET /api/account/deposit — kt00001) // ----------------------------------- async function loadCash() { try { const res = await fetch('/api/account/deposit'); const data = await res.json(); if (!res.ok) { document.getElementById('cashEntr').textContent = '조회 실패'; document.getElementById('cashOrdAlowa').textContent = '조회 실패'; return; } // entr: 예수금, d2Entra: D+2 추정예수금, ordAlowAmt: 주문가능금액 document.getElementById('cashEntr').textContent = data.d2Entra ? parseInt(data.d2Entra).toLocaleString('ko-KR') + '원' : '-'; document.getElementById('cashOrdAlowa').textContent = data.ordAlowAmt ? parseInt(data.ordAlowAmt).toLocaleString('ko-KR') + '원' : '-'; } catch (e) { document.getElementById('cashEntr').textContent = '오류'; document.getElementById('cashOrdAlowa').textContent = '오류'; } } // ----------------------------------- // 탭 전환 // ----------------------------------- function showAssetTab(tab) { ['pending', 'history'].forEach(t => { const btn = document.getElementById('asset' + capitalize(t) + 'Tab'); const panel = document.getElementById('asset' + capitalize(t) + 'Panel'); const active = t === tab; if (btn) { btn.classList.toggle('border-b-2', active); btn.classList.toggle('border-blue-500', active); btn.classList.toggle('text-blue-600', active); btn.classList.toggle('text-gray-500', !active); } if (panel) panel.classList.toggle('hidden', !active); }); if (tab === 'pending') loadPending(); if (tab === 'history') loadHistory(); } function capitalize(s) { return s.charAt(0).toUpperCase() + s.slice(1); } // ----------------------------------- // 미체결 목록 (GET /api/account/pending) // ----------------------------------- async function loadPending() { const panel = document.getElementById('assetPendingPanel'); if (!panel) return; panel.innerHTML = '
조회 중...
'; try { const res = await fetch('/api/account/pending'); const list = await res.json(); if (!res.ok) { panel.innerHTML = `
${list.error || '조회 실패'}
`; return; } if (!list || list.length === 0) { panel.innerHTML = '
미체결 주문이 없습니다.
'; return; } panel.innerHTML = `
${list.map(o => { const isBuy = o.trdeTp === '2'; const cls = isBuy ? 'text-red-500' : 'text-blue-500'; const label = isBuy ? '매수' : '매도'; return ` `; }).join('')}
종목명 구분 주문가 미체결/주문 취소
${o.stkNm} ${label} ${parseInt(o.ordPric || '0').toLocaleString('ko-KR')}원 ${parseInt(o.osoQty || '0').toLocaleString('ko-KR')} / ${parseInt(o.ordQty || '0').toLocaleString('ko-KR')}주
`; } catch (e) { panel.innerHTML = `
오류: ${e.message}
`; } } // ----------------------------------- // 미체결 취소 // ----------------------------------- async function assetCancelOrder(ordNo, stkCd) { if (!confirm('전량 취소하시겠습니까?')) return; try { const res = await fetch('/api/order', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origOrdNo: ordNo, code: stkCd, qty: '0', exchange: 'KRX' }), }); const data = await res.json(); if (!res.ok || data.error) { showAssetToast(data.error || '취소 실패', 'error'); return; } showAssetToast('취소 주문 접수 완료', 'success'); loadPending(); } catch (e) { showAssetToast('네트워크 오류', 'error'); } } // ----------------------------------- // 체결내역 (GET /api/account/history) // ----------------------------------- async function loadHistory() { const panel = document.getElementById('assetHistoryPanel'); if (!panel) return; panel.innerHTML = '
조회 중...
'; try { const res = await fetch('/api/account/history'); const list = await res.json(); if (!res.ok) { panel.innerHTML = `
${list.error || '조회 실패'}
`; return; } if (!list || list.length === 0) { panel.innerHTML = '
체결 내역이 없습니다.
'; return; } panel.innerHTML = `
${list.map(o => { const isBuy = o.trdeTp === '2'; const cls = isBuy ? 'text-red-500' : 'text-blue-500'; const label = isBuy ? '매수' : '매도'; const fee = (parseInt(o.trdeCmsn || '0') + parseInt(o.trdeTax || '0')).toLocaleString('ko-KR'); return ` `; }).join('')}
종목명 구분 체결가 체결수량 수수료+세금
${o.stkNm} ${label} ${parseInt(o.cntrPric || '0').toLocaleString('ko-KR')}원 ${parseInt(o.cntrQty || '0').toLocaleString('ko-KR')}주 ${fee}원
`; } catch (e) { panel.innerHTML = `
오류: ${e.message}
`; } } // ----------------------------------- // 토스트 알림 // ----------------------------------- function showAssetToast(msg, type) { const toast = document.createElement('div'); toast.className = `fixed bottom-6 left-1/2 -translate-x-1/2 z-50 px-5 py-3 rounded-lg text-sm font-medium shadow-lg transition-opacity ${type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white'}`; toast.textContent = msg; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 400); }, 3000); }