차트 설정 수정

This commit is contained in:
Macbook
2026-06-01 01:35:31 +09:00
parent 332019866a
commit f2208aab99
28 changed files with 546 additions and 333 deletions
@@ -42,103 +42,65 @@ export const VirtualSessionHeaderControls: React.FC<SessionProps> = ({
onPaperAutoTradeEnabled,
paperTradingEnabled = true,
}) => (
<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-header-session vtd-header-session--compact">
<select
className="vtd-header-select vtd-header-select--strategy"
aria-label="투자전략"
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 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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="실행방식"
value={session.executionType}
onChange={e => onExecutionTypeChange(e.target.value as VirtualSessionConfig['executionType'])}
>
<option value="CANDLE_CLOSE">봉 마감 직후</option>
<option value="REALTIME_TICK">실시간 틱</option>
</select>
<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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="시그널 모드"
value={session.positionMode}
onChange={e => onPositionModeChange(e.target.value as VirtualSessionConfig['positionMode'])}
>
<option value="LONG_ONLY">보유 자산 기준</option>
<option value="SIGNAL_ONLY">순수 지표 기준</option>
</select>
<div className="vtd-session-field">
<span className="vtd-session-label">자동매매</span>
<div
className="vtd-view-toggle"
role="group"
aria-label="자동매매"
title={
!paperTradingEnabled
? '설정 › 가상투자에서 가상매매를 활성화하세요'
: paperAutoTradeEnabled
? 'Match 시 서버 자동 체결(모의·자동매매 ON, 화면 불필요)'
: '수동 주문만 가능'
}
>
<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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="자동매매"
title={
!paperTradingEnabled
? '설정 › 가상투자에서 가상매매를 활성화하세요'
: undefined
}
value={paperAutoTradeEnabled ? 'on' : 'off'}
disabled={!paperTradingEnabled || !onPaperAutoTradeEnabled}
onChange={e => onPaperAutoTradeEnabled?.(e.target.value === 'on')}
>
<option value="off">자동매매 OFF</option>
<option value="on">자동매매 ON</option>
</select>
<div className="vtd-session-actions">
<div className="vtd-session-actions vtd-session-actions--compact">
<button
type="button"
className="bps-btn vtd-session-btn vtd-session-btn--start"
disabled={session.running}
onClick={onStart}
title="가상투자 시작"
>
시작
</button>
@@ -147,6 +109,7 @@ export const VirtualSessionHeaderControls: React.FC<SessionProps> = ({
className="bps-btn bps-btn--danger vtd-session-btn"
disabled={!session.running}
onClick={onStop}
title="가상투자 종료"
>
종료
</button>
@@ -165,63 +128,36 @@ export const VirtualViewHeaderControls: React.FC<ViewProps> = ({
onGridPresetChange,
showGridLayoutPicker = true,
}) => (
<div className="vtd-header-view">
<div className="vtd-header-view vtd-header-view--compact">
{showGridLayoutPicker && onGridPresetChange && (
<>
<VirtualGridLayoutPicker
value={gridPreset}
onChange={onGridPresetChange}
disabled={!showSignalChartToggle}
/>
<span className="vtd-grid-head-divider" aria-hidden="true" />
</>
<VirtualGridLayoutPicker
value={gridPreset}
onChange={onGridPresetChange}
disabled={!showSignalChartToggle}
/>
)}
{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" />
</>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="신호 또는 차트"
value={globalDisplayMode}
onChange={e => onGlobalDisplayModeChange(e.target.value as VirtualCardDisplayMode)}
>
<option value="signal">신호</option>
<option value="chart">차트</option>
</select>
)}
<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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="카드 표시 방식"
value={viewMode}
onChange={e => onViewModeChange(e.target.value as VirtualCardViewMode)}
>
<option value="summary">요약표시</option>
<option value="detail">상세표시</option>
</select>
{session.running && (
<>
<span className="vtd-grid-head-divider" aria-hidden="true" />
<span className="vtd-grid-live">실시간 갱신 3초</span>
</>
<span className="vtd-grid-live" title="3초마다 갱신">실시간</span>
)}
</div>
);
@@ -88,8 +88,6 @@ const VirtualLeftTargetPanel: React.FC<Props> = ({
}, [addedSet, targets, globalStrategyId, onTargetsChange, onSelectMarket, virtualTargetMaxCount]);
const handleRemove = useCallback((market: string) => {
const item = targets.find(t => t.market === market);
if (item?.pinned) return;
onTargetsChange(targets.filter(t => t.market !== market));
}, [targets, onTargetsChange]);
@@ -119,6 +117,7 @@ const VirtualLeftTargetPanel: React.FC<Props> = ({
addedMarkets={addedSet}
maxTargetsReached={atTargetLimit}
onAdd={handleAdd}
onRemove={handleRemove}
onSelect={onSelectMarket}
onClose={closeDropdown}
/>
@@ -174,9 +173,8 @@ const VirtualLeftTargetPanel: React.FC<Props> = ({
<button
type="button"
className="vtd-target-remove"
title={item.pinned ? '고정된 종목은 삭제할 수 없습니다' : '목록에서 제거'}
title="목록에서 제거"
aria-label="목록에서 제거"
disabled={!!item.pinned}
onClick={e => { e.stopPropagation(); handleRemove(item.market); }}
>
✕
@@ -12,6 +12,7 @@ interface Props {
onStop: () => void;
}
/** 레거시 — VirtualSessionHeaderControls 와 동일 드롭다운 레이아웃 */
const VirtualSessionToolbar: React.FC<Props> = ({
session,
strategies,
@@ -21,65 +22,43 @@ const VirtualSessionToolbar: React.FC<Props> = ({
onStart,
onStop,
}) => (
<div className="vtd-session-toolbar">
<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-toolbar vtd-header-session--compact">
<select
className="vtd-header-select vtd-header-select--strategy"
aria-label="투자전략"
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 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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="실행방식"
value={session.executionType}
onChange={e => onExecutionTypeChange(e.target.value as VirtualSessionConfig['executionType'])}
>
<option value="CANDLE_CLOSE">봉 마감 직후</option>
<option value="REALTIME_TICK">실시간 틱</option>
</select>
<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>
<select
className="vtd-header-select vtd-header-select--narrow"
aria-label="시그널 모드"
value={session.positionMode}
onChange={e => onPositionModeChange(e.target.value as VirtualSessionConfig['positionMode'])}
>
<option value="LONG_ONLY">보유 자산 기준</option>
<option value="SIGNAL_ONLY">순수 지표 기준</option>
</select>
<div className="vtd-session-actions">
<div className="vtd-session-actions vtd-session-actions--compact">
<button
type="button"
className="bps-btn vtd-session-btn vtd-session-btn--start"