From 22897c3eb6b79f57972698e80cffe42e879ef7cb Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 26 Jan 2026 22:47:18 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A3=BC=EC=8B=9D=20=EC=9E=94=EA=B3=A0,=20?= =?UTF-8?q?=EC=A3=BC=EB=AC=B8=20=EC=B0=BD=20=EC=97=85=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=20-=20=EC=9E=94=EA=B3=A0,=20=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=20=EC=B0=BD=20=EB=B6=84=EB=A6=AC=20=20-=20full-width?= =?UTF-8?q?=20=EC=84=B9=EC=85=98=EC=9C=BC=EB=A1=9C=20=EC=8C=93=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 8 + src/pages/stock/Stock.css | 81 +++++++++ src/pages/stock/Stock.jsx | 4 + src/pages/stock/StockTrade.jsx | 309 +++++++++++++++++++++++++++++++++ src/routes.jsx | 5 + 5 files changed, 407 insertions(+) create mode 100644 src/pages/stock/StockTrade.jsx 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: ,