가상투자 설정 수정

This commit is contained in:
Macbook
2026-05-25 22:30:54 +09:00
parent cae1c3a624
commit 1e950c7db4
24 changed files with 1015 additions and 362 deletions
@@ -3,7 +3,7 @@ import type { StrategyDto } from '../../utils/backendApi';
import type { VirtualSessionConfig, VirtualCardViewMode } from '../../utils/virtualTradingStorage';
import type { VirtualCardDisplayMode } from './VirtualTargetCard';
interface Props {
interface SessionProps {
session: Pick<VirtualSessionConfig, 'globalStrategyId' | 'executionType' | 'positionMode' | 'running'>;
strategies: StrategyDto[];
onStrategyChange: (strategyId: number | null) => void;
@@ -11,15 +11,21 @@ interface Props {
onPositionModeChange: (mode: VirtualSessionConfig['positionMode']) => void;
onStart: () => void;
onStop: () => void;
viewMode?: VirtualCardViewMode;
onViewModeChange?: (mode: VirtualCardViewMode) => void;
globalDisplayMode?: VirtualCardDisplayMode;
onGlobalDisplayModeChange?: (mode: VirtualCardDisplayMode) => void;
/** 우측 뷰 토글: full=신호·차트+요약·상세, display-only=요약·상세만, hidden=숨김 */
viewControls?: 'full' | 'display-only' | 'hidden';
paperAutoTradeEnabled?: boolean;
onPaperAutoTradeEnabled?: (enabled: boolean) => void;
paperTradingEnabled?: boolean;
}
const VirtualGridUnifiedHeader: React.FC<Props> = ({
interface ViewProps {
session: Pick<VirtualSessionConfig, 'running'>;
viewMode: VirtualCardViewMode;
onViewModeChange: (mode: VirtualCardViewMode) => void;
globalDisplayMode?: VirtualCardDisplayMode;
onGlobalDisplayModeChange?: (mode: VirtualCardDisplayMode) => void;
showSignalChartToggle?: boolean;
}
export const VirtualSessionHeaderControls: React.FC<SessionProps> = ({
session,
strategies,
onStrategyChange,
@@ -27,155 +33,187 @@ const VirtualGridUnifiedHeader: React.FC<Props> = ({
onPositionModeChange,
onStart,
onStop,
viewMode = 'summary',
onViewModeChange,
globalDisplayMode = 'signal',
onGlobalDisplayModeChange,
viewControls = 'full',
paperAutoTradeEnabled = false,
onPaperAutoTradeEnabled,
paperTradingEnabled = true,
}) => (
<div className="vtd-unified-head">
<span className="vtd-unified-head-title"> </span>
<div className="vtd-header-session">
<div className="vtd-session-field">
<span className="vtd-session-label"></span>
<select
className="vtd-session-select"
value={session.globalStrategyId ?? ''}
onChange={e => {
const v = e.target.value;
onStrategyChange(v ? Number(v) : null);
}}
>
<option value=""> </option>
{strategies.map(s => (
<option key={s.id} value={s.id}>{s.name}</option>
))}
</select>
</div>
<div className="vtd-unified-head-center">
<div className="vtd-session-field">
<span className="vtd-session-label"></span>
<select
className="vtd-session-select"
value={session.globalStrategyId ?? ''}
onChange={e => {
const v = e.target.value;
onStrategyChange(v ? Number(v) : null);
}}
>
<option value=""> </option>
{strategies.map(s => (
<option key={s.id} value={s.id}>{s.name}</option>
))}
</select>
</div>
<div className="vtd-session-field">
<span className="vtd-session-label"></span>
<div className="vtd-view-toggle" role="group" aria-label="실행방식">
<button
type="button"
className={`vtd-view-toggle-btn${session.executionType === 'CANDLE_CLOSE' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onExecutionTypeChange('CANDLE_CLOSE')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${session.executionType === 'REALTIME_TICK' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onExecutionTypeChange('REALTIME_TICK')}
>
</button>
</div>
</div>
<div className="vtd-session-field">
<span className="vtd-session-label"> </span>
<div className="vtd-view-toggle" role="group" aria-label="시그널 모드">
<button
type="button"
className={`vtd-view-toggle-btn${session.positionMode === 'LONG_ONLY' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onPositionModeChange('LONG_ONLY')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${session.positionMode === 'SIGNAL_ONLY' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onPositionModeChange('SIGNAL_ONLY')}
>
</button>
</div>
</div>
<div className="vtd-session-actions">
<div className="vtd-session-field">
<span className="vtd-session-label"></span>
<div className="vtd-view-toggle" role="group" aria-label="실행방식">
<button
type="button"
className="bps-btn vtd-session-btn vtd-session-btn--start"
disabled={session.running}
onClick={onStart}
className={`vtd-view-toggle-btn${session.executionType === 'CANDLE_CLOSE' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onExecutionTypeChange('CANDLE_CLOSE')}
>
</button>
<button
type="button"
className="bps-btn bps-btn--danger vtd-session-btn"
disabled={!session.running}
onClick={onStop}
className={`vtd-view-toggle-btn${session.executionType === 'REALTIME_TICK' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onExecutionTypeChange('REALTIME_TICK')}
>
</button>
</div>
</div>
{viewControls !== 'hidden' && onViewModeChange && (
<div className="vtd-unified-head-right">
{viewControls === 'full' && onGlobalDisplayModeChange && (
<>
<div className="vtd-grid-head-group" role="group" aria-label="전체 종목 신호·차트">
<div className="vtd-view-toggle">
<button
type="button"
className={`vtd-view-toggle-btn${globalDisplayMode === 'signal' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onGlobalDisplayModeChange('signal')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${globalDisplayMode === 'chart' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onGlobalDisplayModeChange('chart')}
>
</button>
</div>
</div>
<span className="vtd-grid-head-divider" aria-hidden="true" />
</>
)}
<div className="vtd-grid-head-group" role="group" aria-label="카드 표시 방식">
<div className="vtd-view-toggle">
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'summary' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onViewModeChange('summary')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'detail' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onViewModeChange('detail')}
>
</button>
</div>
</div>
{viewMode === 'detail' && (
<>
<span className="vtd-grid-head-divider" aria-hidden="true" />
<div className="vtd-grid-head-legend">
<span className="vtd-legend vtd-legend--match"> </span>
<span className="vtd-legend vtd-legend--pending"> </span>
<span className="vtd-legend vtd-legend--nomatch"> </span>
</div>
</>
)}
{session.running && (
<>
<span className="vtd-grid-head-divider" aria-hidden="true" />
<span className="vtd-grid-live"> 3</span>
</>
)}
<div className="vtd-session-field">
<span className="vtd-session-label"> </span>
<div className="vtd-view-toggle" role="group" aria-label="시그널 모드">
<button
type="button"
className={`vtd-view-toggle-btn${session.positionMode === 'LONG_ONLY' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onPositionModeChange('LONG_ONLY')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${session.positionMode === 'SIGNAL_ONLY' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onPositionModeChange('SIGNAL_ONLY')}
>
</button>
</div>
)}
</div>
<div className="vtd-session-field">
<span className="vtd-session-label"></span>
<div
className="vtd-view-toggle"
role="group"
aria-label="자동매매"
title={
!paperTradingEnabled
? '설정 › 가상투자에서 가상투자를 활성화하세요'
: paperAutoTradeEnabled
? 'Match 충족 시 자동 매수·매도'
: '수동 주문만 가능'
}
>
<button
type="button"
className={`vtd-view-toggle-btn${paperAutoTradeEnabled ? ' vtd-view-toggle-btn--on' : ''}`}
disabled={!paperTradingEnabled || !onPaperAutoTradeEnabled}
onClick={() => onPaperAutoTradeEnabled?.(true)}
>
ON
</button>
<button
type="button"
className={`vtd-view-toggle-btn${!paperAutoTradeEnabled ? ' vtd-view-toggle-btn--on' : ''}`}
disabled={!paperTradingEnabled || !onPaperAutoTradeEnabled}
onClick={() => onPaperAutoTradeEnabled?.(false)}
>
OFF
</button>
</div>
</div>
<div className="vtd-session-actions">
<button
type="button"
className="bps-btn vtd-session-btn vtd-session-btn--start"
disabled={session.running}
onClick={onStart}
>
</button>
<button
type="button"
className="bps-btn bps-btn--danger vtd-session-btn"
disabled={!session.running}
onClick={onStop}
>
</button>
</div>
</div>
);
export default VirtualGridUnifiedHeader;
export const VirtualViewHeaderControls: React.FC<ViewProps> = ({
session,
viewMode,
onViewModeChange,
globalDisplayMode = 'signal',
onGlobalDisplayModeChange,
showSignalChartToggle = true,
}) => (
<div className="vtd-header-view">
{showSignalChartToggle && onGlobalDisplayModeChange && (
<>
<div className="vtd-grid-head-group" role="group" aria-label="전체 종목 신호·차트">
<div className="vtd-view-toggle">
<button
type="button"
className={`vtd-view-toggle-btn${globalDisplayMode === 'signal' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onGlobalDisplayModeChange('signal')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${globalDisplayMode === 'chart' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onGlobalDisplayModeChange('chart')}
>
</button>
</div>
</div>
<span className="vtd-grid-head-divider" aria-hidden="true" />
</>
)}
<div className="vtd-grid-head-group" role="group" aria-label="카드 표시 방식">
<div className="vtd-view-toggle">
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'summary' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onViewModeChange('summary')}
>
</button>
<button
type="button"
className={`vtd-view-toggle-btn${viewMode === 'detail' ? ' vtd-view-toggle-btn--on' : ''}`}
onClick={() => onViewModeChange('detail')}
>
</button>
</div>
</div>
{viewMode === 'detail' && (
<>
<span className="vtd-grid-head-divider" aria-hidden="true" />
<div className="vtd-grid-head-legend">
<span className="vtd-legend vtd-legend--match"> </span>
<span className="vtd-legend vtd-legend--pending"> </span>
<span className="vtd-legend vtd-legend--nomatch"> </span>
</div>
</>
)}
{session.running && (
<>
<span className="vtd-grid-head-divider" aria-hidden="true" />
<span className="vtd-grid-live"> 3</span>
</>
)}
</div>
);