매매탭 주문패널을 스플릿 카드 레이아웃으로 복원 (상단 매수, 하단 매도)
- TradeTabOrderPanel(탭 방식) → TradeSplitOrderPanel(카드 분리 방식)으로 전환 - VirtualTradingPage·PaperTradingPage에 '거래' 외부 탭 복원 - 거래 내역(미체결/체결/원장)을 '거래' 탭 하위로 원복 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,7 +23,7 @@ import PaperPendingOrdersList from './paper/PaperPendingOrdersList';
|
|||||||
import PaperLedgerTab from './paper/PaperLedgerTab';
|
import PaperLedgerTab from './paper/PaperLedgerTab';
|
||||||
import PaperAssetTrendChart from './paper/PaperAssetTrendChart';
|
import PaperAssetTrendChart from './paper/PaperAssetTrendChart';
|
||||||
import { computePaperMetrics } from '../utils/paperMetrics';
|
import { computePaperMetrics } from '../utils/paperMetrics';
|
||||||
import { TradeTabOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
|
import { TradeSplitOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
|
||||||
import type { Theme, TradeOrderFillRequest } from '../types';
|
import type { Theme, TradeOrderFillRequest } from '../types';
|
||||||
import PaperAnalysisChart from './paper/PaperAnalysisChart';
|
import PaperAnalysisChart from './paper/PaperAnalysisChart';
|
||||||
import PaperLeftStrategyTab from './paper/PaperLeftStrategyTab';
|
import PaperLeftStrategyTab from './paper/PaperLeftStrategyTab';
|
||||||
@@ -38,7 +38,7 @@ import { formatVirtualTargetOptionLabel } from '../utils/virtualTargetNames';
|
|||||||
|
|
||||||
type HistoryTab = 'open' | 'recent' | 'ledger';
|
type HistoryTab = 'open' | 'recent' | 'ledger';
|
||||||
type CenterTab = 'invest' | 'chart';
|
type CenterTab = 'invest' | 'chart';
|
||||||
type RightTab = 'trade' | 'orderbook';
|
type RightTab = 'trade' | 'orderbook' | 'history';
|
||||||
type LeftTab = 'strategy' | 'settings';
|
type LeftTab = 'strategy' | 'settings';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -140,7 +140,7 @@ const PaperTradingPage: React.FC<Props> = ({
|
|||||||
} finally {
|
} finally {
|
||||||
setInitialLoading(false);
|
setInitialLoading(false);
|
||||||
}
|
}
|
||||||
if (opts?.focusHistory) setRightTab('trade');
|
if (opts?.focusHistory) setRightTab('history');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -293,33 +293,7 @@ const PaperTradingPage: React.FC<Props> = ({
|
|||||||
<div className="bps-right-tabs">
|
<div className="bps-right-tabs">
|
||||||
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}>매매</button>
|
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}>매매</button>
|
||||||
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}>호가</button>
|
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}>호가</button>
|
||||||
</div>
|
<button type="button" className={`bps-right-tab${rightTab === 'history' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('history')}>거래</button>
|
||||||
);
|
|
||||||
|
|
||||||
const tradeHistorySlot = (
|
|
||||||
<div className="ptd-right-history">
|
|
||||||
<div className="ptd-tabs ptd-tabs--history">
|
|
||||||
<button type="button" className={`ptd-tab${historyTab === 'open' ? ' active' : ''}`} onClick={() => setHistoryTab('open')}>미체결</button>
|
|
||||||
<button type="button" className={`ptd-tab${historyTab === 'recent' ? ' active' : ''}`} onClick={() => setHistoryTab('recent')}>체결{trades.length > 0 ? ` (${trades.length})` : ''}</button>
|
|
||||||
<button type="button" className={`ptd-tab${historyTab === 'ledger' ? ' active' : ''}`} onClick={() => setHistoryTab('ledger')}>원장</button>
|
|
||||||
</div>
|
|
||||||
<div className="ptd-right-history-body">
|
|
||||||
{historyTab === 'open' ? (
|
|
||||||
<PaperPendingOrdersList orders={pendingOrders} onChanged={() => void loadData()} />
|
|
||||||
) : historyTab === 'ledger' ? (
|
|
||||||
<PaperLedgerTab refreshKey={dataRefreshKey} />
|
|
||||||
) : (
|
|
||||||
<PaperTradeHistoryList
|
|
||||||
trades={trades}
|
|
||||||
strategyNames={strategyNames}
|
|
||||||
tickers={tickers}
|
|
||||||
selectedTradeId={selectedTradeId}
|
|
||||||
onSelectTrade={handleTradeHistorySelect}
|
|
||||||
emptyText="체결 내역이 없습니다."
|
|
||||||
className="ptd-trade-history--fill"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -445,7 +419,7 @@ const PaperTradingPage: React.FC<Props> = ({
|
|||||||
right={(
|
right={(
|
||||||
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
|
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
|
||||||
{rightTab === 'trade' ? (
|
{rightTab === 'trade' ? (
|
||||||
<TradeTabOrderPanel
|
<TradeSplitOrderPanel
|
||||||
market={selectedMarket}
|
market={selectedMarket}
|
||||||
tradePrice={tradePrice}
|
tradePrice={tradePrice}
|
||||||
availableKrw={orderableCash}
|
availableKrw={orderableCash}
|
||||||
@@ -457,9 +431,8 @@ const PaperTradingPage: React.FC<Props> = ({
|
|||||||
paperTradingEnabled={paperTradingEnabled}
|
paperTradingEnabled={paperTradingEnabled}
|
||||||
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
||||||
onPaperOrderFilled={() => void loadData({ focusHistory: true })}
|
onPaperOrderFilled={() => void loadData({ focusHistory: true })}
|
||||||
historySlot={tradeHistorySlot}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : rightTab === 'orderbook' ? (
|
||||||
<TradeOrderbookPanelContent
|
<TradeOrderbookPanelContent
|
||||||
market={selectedMarket}
|
market={selectedMarket}
|
||||||
asks={orderbook.asks}
|
asks={orderbook.asks}
|
||||||
@@ -477,6 +450,31 @@ const PaperTradingPage: React.FC<Props> = ({
|
|||||||
tradeStrength={tradeStrength ?? undefined}
|
tradeStrength={tradeStrength ?? undefined}
|
||||||
onRowClick={handleOrderbookRowClick}
|
onRowClick={handleOrderbookRowClick}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="ptd-right-history">
|
||||||
|
<div className="ptd-tabs ptd-tabs--history">
|
||||||
|
<button type="button" className={`ptd-tab${historyTab === 'open' ? ' active' : ''}`} onClick={() => setHistoryTab('open')}>미체결</button>
|
||||||
|
<button type="button" className={`ptd-tab${historyTab === 'recent' ? ' active' : ''}`} onClick={() => setHistoryTab('recent')}>체결{trades.length > 0 ? ` (${trades.length})` : ''}</button>
|
||||||
|
<button type="button" className={`ptd-tab${historyTab === 'ledger' ? ' active' : ''}`} onClick={() => setHistoryTab('ledger')}>원장</button>
|
||||||
|
</div>
|
||||||
|
<div className="ptd-right-history-body">
|
||||||
|
{historyTab === 'open' ? (
|
||||||
|
<PaperPendingOrdersList orders={pendingOrders} onChanged={() => void loadData()} />
|
||||||
|
) : historyTab === 'ledger' ? (
|
||||||
|
<PaperLedgerTab refreshKey={dataRefreshKey} />
|
||||||
|
) : (
|
||||||
|
<PaperTradeHistoryList
|
||||||
|
trades={trades}
|
||||||
|
strategyNames={strategyNames}
|
||||||
|
tickers={tickers}
|
||||||
|
selectedTradeId={selectedTradeId}
|
||||||
|
onSelectTrade={handleTradeHistorySelect}
|
||||||
|
emptyText="체결 내역이 없습니다."
|
||||||
|
className="ptd-trade-history--fill"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</TradeRightPanelBody>
|
</TradeRightPanelBody>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
TradeTabOrderPanel,
|
TradeSplitOrderPanel,
|
||||||
TradeOrderbookPanelContent,
|
TradeOrderbookPanelContent,
|
||||||
TradeRightPanelTabs,
|
TradeRightPanelTabs,
|
||||||
TradeRightPanelBody,
|
TradeRightPanelBody,
|
||||||
@@ -158,7 +158,7 @@ const RightSidePanel: React.FC<RightSidePanelProps> = ({
|
|||||||
|
|
||||||
<TradeRightPanelBody anchorRef={tradeSectionRef} className="rsp-body-wrap">
|
<TradeRightPanelBody anchorRef={tradeSectionRef} className="rsp-body-wrap">
|
||||||
{activeTab === 'trade' && (
|
{activeTab === 'trade' && (
|
||||||
<TradeTabOrderPanel
|
<TradeSplitOrderPanel
|
||||||
market={market}
|
market={market}
|
||||||
tradePrice={tradePrice}
|
tradePrice={tradePrice}
|
||||||
availableKrw={availableKrw}
|
availableKrw={availableKrw}
|
||||||
@@ -169,7 +169,6 @@ const RightSidePanel: React.FC<RightSidePanelProps> = ({
|
|||||||
paperTradingEnabled={paperTradingEnabled}
|
paperTradingEnabled={paperTradingEnabled}
|
||||||
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
||||||
onPaperOrderFilled={onPaperOrderFilled}
|
onPaperOrderFilled={onPaperOrderFilled}
|
||||||
hideHistory
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import BacktestAnalysisReportModal from './backtest/BacktestAnalysisReportModal'
|
|||||||
import type { BacktestAnalysisReportModel } from './backtest/BacktestAnalysisReportModal';
|
import type { BacktestAnalysisReportModel } from './backtest/BacktestAnalysisReportModal';
|
||||||
import { coerceFiniteNumber } from '../utils/safeFormat';
|
import { coerceFiniteNumber } from '../utils/safeFormat';
|
||||||
import {
|
import {
|
||||||
TradeTabOrderPanel,
|
TradeSplitOrderPanel,
|
||||||
TradeOrderbookPanelContent,
|
TradeOrderbookPanelContent,
|
||||||
TradeRightPanelTabs,
|
TradeRightPanelTabs,
|
||||||
TradeRightPanelBody,
|
TradeRightPanelBody,
|
||||||
@@ -753,7 +753,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
bodyClassName="tnl-right-body"
|
bodyClassName="tnl-right-body"
|
||||||
>
|
>
|
||||||
{rightTab === 'trade' ? (
|
{rightTab === 'trade' ? (
|
||||||
<TradeTabOrderPanel
|
<TradeSplitOrderPanel
|
||||||
market={selectedMarket}
|
market={selectedMarket}
|
||||||
tradePrice={tradePrice}
|
tradePrice={tradePrice}
|
||||||
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
|
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
|
||||||
@@ -764,7 +764,6 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
paperTradingEnabled={paperTradingEnabled}
|
paperTradingEnabled={paperTradingEnabled}
|
||||||
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
||||||
onPaperOrderFilled={() => void refreshPaperData()}
|
onPaperOrderFilled={() => void refreshPaperData()}
|
||||||
hideHistory
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TradeOrderbookPanelContent
|
<TradeOrderbookPanelContent
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from '../utils/backendApi';
|
} from '../utils/backendApi';
|
||||||
import type { Theme, TradeOrderFillRequest } from '../types';
|
import type { Theme, TradeOrderFillRequest } from '../types';
|
||||||
import type { TickerData } from '../hooks/useMarketTicker';
|
import type { TickerData } from '../hooks/useMarketTicker';
|
||||||
import { TradeTabOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
|
import { TradeSplitOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
|
||||||
import { useUpbitOrderbook } from '../hooks/useUpbitOrderbook';
|
import { useUpbitOrderbook } from '../hooks/useUpbitOrderbook';
|
||||||
import { useUpbitRecentTrades } from '../hooks/useUpbitRecentTrades';
|
import { useUpbitRecentTrades } from '../hooks/useUpbitRecentTrades';
|
||||||
import PaperTradeHistoryList from './paper/PaperTradeHistoryList';
|
import PaperTradeHistoryList from './paper/PaperTradeHistoryList';
|
||||||
@@ -72,7 +72,7 @@ import {
|
|||||||
} from '../utils/virtualTargetHydrate';
|
} from '../utils/virtualTargetHydrate';
|
||||||
import '../styles/virtualTradingDashboard.css';
|
import '../styles/virtualTradingDashboard.css';
|
||||||
|
|
||||||
type RightTab = 'trade' | 'orderbook';
|
type RightTab = 'trade' | 'orderbook' | 'history';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
theme?: Theme;
|
theme?: Theme;
|
||||||
@@ -258,7 +258,7 @@ const VirtualTradingPage: React.FC<Props> = ({
|
|||||||
setTrades(tr);
|
setTrades(tr);
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
onPaperOrderFilled?.();
|
onPaperOrderFilled?.();
|
||||||
if (opts?.focusHistory) setRightTab('trade');
|
if (opts?.focusHistory) setRightTab('history');
|
||||||
}, [onPaperOrderFilled]);
|
}, [onPaperOrderFilled]);
|
||||||
|
|
||||||
const handleGlobalDisplayModeChange = useCallback((mode: VirtualCardDisplayMode) => {
|
const handleGlobalDisplayModeChange = useCallback((mode: VirtualCardDisplayMode) => {
|
||||||
@@ -472,20 +472,10 @@ const VirtualTradingPage: React.FC<Props> = ({
|
|||||||
<div className="bps-right-tabs">
|
<div className="bps-right-tabs">
|
||||||
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}>매매</button>
|
<button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}>매매</button>
|
||||||
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}>호가</button>
|
<button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}>호가</button>
|
||||||
|
<button type="button" className={`bps-right-tab${rightTab === 'history' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('history')}>거래</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const tradeHistorySlot = (
|
|
||||||
<PaperTradeHistoryList
|
|
||||||
trades={trades}
|
|
||||||
strategyNames={strategyNames}
|
|
||||||
tickers={tickers}
|
|
||||||
onSelectMarket={handleSelectMarket}
|
|
||||||
emptyText="수동·자동 매매 체결 내역이 없습니다."
|
|
||||||
className="ptd-trade-history--fill"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BuilderPageShell
|
<BuilderPageShell
|
||||||
theme={theme}
|
theme={theme}
|
||||||
@@ -572,7 +562,7 @@ const VirtualTradingPage: React.FC<Props> = ({
|
|||||||
right={(
|
right={(
|
||||||
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
|
<TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
|
||||||
{rightTab === 'trade' ? (
|
{rightTab === 'trade' ? (
|
||||||
<TradeTabOrderPanel
|
<TradeSplitOrderPanel
|
||||||
market={selectedMarket}
|
market={selectedMarket}
|
||||||
tradePrice={tradePrice}
|
tradePrice={tradePrice}
|
||||||
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
|
availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
|
||||||
@@ -583,9 +573,8 @@ const VirtualTradingPage: React.FC<Props> = ({
|
|||||||
paperTradingEnabled={paperTradingEnabled}
|
paperTradingEnabled={paperTradingEnabled}
|
||||||
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
||||||
onPaperOrderFilled={() => void refreshPaperData({ focusHistory: true })}
|
onPaperOrderFilled={() => void refreshPaperData({ focusHistory: true })}
|
||||||
historySlot={tradeHistorySlot}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : rightTab === 'orderbook' ? (
|
||||||
<TradeOrderbookPanelContent
|
<TradeOrderbookPanelContent
|
||||||
market={selectedMarket}
|
market={selectedMarket}
|
||||||
asks={orderbook.asks}
|
asks={orderbook.asks}
|
||||||
@@ -603,6 +592,15 @@ const VirtualTradingPage: React.FC<Props> = ({
|
|||||||
tradeStrength={tradeStrength ?? undefined}
|
tradeStrength={tradeStrength ?? undefined}
|
||||||
onRowClick={handleOrderbookRowClick}
|
onRowClick={handleOrderbookRowClick}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<PaperTradeHistoryList
|
||||||
|
trades={trades}
|
||||||
|
strategyNames={strategyNames}
|
||||||
|
tickers={tickers}
|
||||||
|
onSelectMarket={handleSelectMarket}
|
||||||
|
emptyText="수동·자동 매매 체결 내역이 없습니다."
|
||||||
|
className="ptd-trade-history--fill"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</TradeRightPanelBody>
|
</TradeRightPanelBody>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user