diff --git a/src/api.js b/src/api.js index b3d361f..2b32633 100644 --- a/src/api.js +++ b/src/api.js @@ -77,3 +77,11 @@ export function getStockHealth() { export function getStockIndices() { return apiGet("/api/stock/indices"); } + +export function getTradeBalance() { + return apiGet("/api/trade/balance"); +} + +export function createTradeOrder(payload) { + return apiPost("/api/trade/order", payload); +} diff --git a/src/pages/stock/Stock.css b/src/pages/stock/Stock.css index 43b5451..96c4bd1 100644 --- a/src/pages/stock/Stock.css +++ b/src/pages/stock/Stock.css @@ -325,6 +325,87 @@ color: var(--muted); } +.stock-trade { + display: grid; + gap: 16px; +} + +.stock-balance { + display: grid; + gap: 12px; +} + +.stock-balance__summary { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 10px; +} + +.stock-balance__card { + border: 1px solid var(--line); + border-radius: 14px; + padding: 10px; + display: grid; + gap: 6px; + background: rgba(0, 0, 0, 0.2); + font-size: 12px; + color: var(--muted); +} + +.stock-balance__card strong { + font-size: 16px; + color: var(--text); +} + +.stock-holdings { + display: grid; + gap: 8px; +} + +.stock-holdings__item { + border: 1px solid var(--line); + border-radius: 12px; + padding: 10px; + display: grid; + grid-template-columns: minmax(0, 1.1fr) repeat(2, minmax(0, 0.7fr)); + gap: 10px; + font-size: 12px; + color: var(--muted); + background: rgba(255, 255, 255, 0.02); +} + +.stock-holdings__name { + margin: 0; + font-weight: 600; + color: var(--text); +} + +.stock-holdings__code { + font-size: 11px; +} + +.stock-order { + display: grid; + gap: 10px; +} + +.stock-order label { + display: grid; + gap: 6px; + font-size: 12px; + color: var(--muted); +} + +.stock-order input, +.stock-order select { + border: 1px solid var(--line); + border-radius: 12px; + padding: 10px 12px; + background: rgba(0, 0, 0, 0.25); + color: var(--text); + outline: none; +} + @media (max-width: 900px) { .stock-header { grid-template-columns: 1fr; diff --git a/src/pages/stock/Stock.jsx b/src/pages/stock/Stock.jsx index 9c65ce3..d79f329 100644 --- a/src/pages/stock/Stock.jsx +++ b/src/pages/stock/Stock.jsx @@ -1,4 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; +import { Link } from 'react-router-dom'; import { getStockHealth, getStockIndices, @@ -157,6 +158,9 @@ const Stock = () => { > 뉴스 새로고침 + + 잔고/주문 화면 + + + + {balanceError ? ( +

{balanceError}

+ ) : ( +
+
+ {[ + { + label: '예수금', + value: + balance?.cash ?? + balance?.available_cash ?? + balance?.deposit, + }, + { + label: '총평가', + value: + balance?.total_eval ?? + balance?.total_value ?? + balance?.evaluation, + }, + { + label: '손익', + value: + balance?.pnl ?? + balance?.profit_loss ?? + balance?.total_pnl, + }, + ] + .filter((item) => item.value !== undefined) + .map((item) => ( +
+ {item.label} + {formatNumber(item.value)} +
+ ))} +
+ {Array.isArray( + balance?.holdings ?? + balance?.positions ?? + balance?.items + ) && + (balance?.holdings ?? + balance?.positions ?? + balance?.items).length ? ( +
+ {(balance?.holdings ?? + balance?.positions ?? + balance?.items + ).map((item, idx) => ( +
+
+

+ {item.name ?? item.code ?? '종목'} +

+ + {item.code ?? item.symbol ?? ''} + +
+
+ 수량 + + {formatNumber( + item.qty ?? + item.quantity ?? + item.holding + )} + +
+
+ 평균단가 + + {formatNumber( + item.avg_price ?? + item.avg ?? + item.price + )} + +
+
+ ))} +
+ ) : ( +

보유 종목 없음

+ )} +
+ )} + + +
+
+
+

Order

+

주문 (매수/매도)

+

+ 종목 코드, 수량, 가격을 입력해 주문을 전송합니다. +

+
+
+
+ + + + + + {orderMessage ? ( +

{orderMessage}

+ ) : null} +
+
+ + ); +}; + +export default StockTrade; diff --git a/src/routes.jsx b/src/routes.jsx index d6a3859..c16387c 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -4,6 +4,7 @@ import Blog from './pages/blog/Blog'; import Lotto from './pages/lotto/Lotto'; import Travel from './pages/travel/Travel'; import Stock from './pages/stock/Stock'; +import StockTrade from './pages/stock/StockTrade'; export const navLinks = [ { @@ -55,6 +56,10 @@ export const appRoutes = [ path: 'stock', element: , }, + { + path: 'stock/trade', + element: , + }, { path: 'travel', element: ,