가상매매 시간봉 제거
This commit is contained in:
@@ -5,8 +5,7 @@ import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSn
|
||||
import { useLiveReceiveFlash } from '../../hooks/useLiveReceiveFlash';
|
||||
import type { ChartRealtimeSource } from '../../hooks/useChartRealtimeData';
|
||||
import { buildConditionMetrics } from '../../utils/virtualSignalMetrics';
|
||||
import { candleTypeToTimeframe } from '../../utils/strategyToChartIndicators';
|
||||
import { normalizeStartCandleType } from '../../utils/strategyStartNodes';
|
||||
import { resolveStrategyPrimaryTimeframe } from '../../utils/strategyToChartIndicators';
|
||||
import type { Timeframe } from '../../types';
|
||||
import VirtualLiveBadge from './VirtualLiveBadge';
|
||||
import type { VirtualLiveStatus } from '../../hooks/useVirtualTargetLiveStatus';
|
||||
@@ -27,9 +26,8 @@ interface Props {
|
||||
strategyId: number | null;
|
||||
globalStrategyId: number | null;
|
||||
onStrategyChange?: (strategyId: number | null) => void;
|
||||
candleType: string;
|
||||
onCandleTypeChange?: (candleType: string) => void;
|
||||
snapshot: VirtualIndicatorSnapshot | undefined;
|
||||
signalLoading?: boolean;
|
||||
running: boolean;
|
||||
liveStatus?: VirtualLiveStatus;
|
||||
lastTickAt?: number;
|
||||
@@ -54,9 +52,8 @@ const VirtualTargetCard: React.FC<Props> = ({
|
||||
strategyId,
|
||||
globalStrategyId,
|
||||
onStrategyChange,
|
||||
candleType,
|
||||
onCandleTypeChange,
|
||||
snapshot,
|
||||
signalLoading = false,
|
||||
running,
|
||||
liveStatus = 'idle',
|
||||
lastTickAt,
|
||||
@@ -83,8 +80,10 @@ const VirtualTargetCard: React.FC<Props> = ({
|
||||
|
||||
const isDetail = viewMode === 'detail';
|
||||
const isChart = displayMode === 'chart';
|
||||
const evalCandleType = normalizeStartCandleType(candleType);
|
||||
const chartTimeframe = candleTypeToTimeframe(evalCandleType) as Timeframe;
|
||||
const chartTimeframe = useMemo(
|
||||
() => resolveStrategyPrimaryTimeframe(strategy) as Timeframe,
|
||||
[strategy],
|
||||
);
|
||||
|
||||
const receiveSignal = useMemo(() => {
|
||||
if (!running) return null;
|
||||
@@ -184,6 +183,7 @@ const VirtualTargetCard: React.FC<Props> = ({
|
||||
snapshot={snapshot}
|
||||
viewMode={viewMode}
|
||||
receiving={highlightReceiving}
|
||||
loading={signalLoading}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -192,9 +192,7 @@ const VirtualTargetCard: React.FC<Props> = ({
|
||||
strategies={strategies}
|
||||
strategyId={strategyId}
|
||||
globalStrategyId={globalStrategyId}
|
||||
candleType={evalCandleType}
|
||||
onStrategyChange={onStrategyChange}
|
||||
onCandleTypeChange={onCandleTypeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ interface Props {
|
||||
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
|
||||
/** 전체보기 분할 pane — 캔버스 높이 100% */
|
||||
fillHeight?: boolean;
|
||||
/** 카드 푸터 평가 분봉과 동기화 */
|
||||
/** 미지정 시 전략 DSL 대표 분봉 사용 */
|
||||
chartTimeframe?: Timeframe;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import type { StrategyDto } from '../../utils/backendApi';
|
||||
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
|
||||
import { formatUpdatedTime } from '../../utils/virtualSignalMetrics';
|
||||
import { STRATEGY_CANDLE_TYPE_OPTIONS } from '../../utils/strategyStartNodes';
|
||||
import {
|
||||
defaultStrategyOptionLabel,
|
||||
parseTargetStrategySelectValue,
|
||||
@@ -14,20 +13,16 @@ interface Props {
|
||||
strategies: StrategyDto[];
|
||||
strategyId: number | null;
|
||||
globalStrategyId: number | null;
|
||||
candleType: string;
|
||||
onStrategyChange?: (strategyId: number | null) => void;
|
||||
onCandleTypeChange?: (candleType: string) => void;
|
||||
}
|
||||
|
||||
/** 카드 하단 — 갱신 시각(좌) · 전략·시간봉 드롭다운(우) */
|
||||
/** 카드 하단 — 갱신 시각(좌) · 전략 드롭다운(우) */
|
||||
const VirtualTargetCardFoot: React.FC<Props> = ({
|
||||
snapshot,
|
||||
strategies,
|
||||
strategyId,
|
||||
globalStrategyId,
|
||||
candleType,
|
||||
onStrategyChange,
|
||||
onCandleTypeChange,
|
||||
}) => (
|
||||
<div className="vtd-card-foot">
|
||||
<span className="vtd-card-updated">갱신 {formatUpdatedTime(snapshot?.updatedAt)}</span>
|
||||
@@ -49,18 +44,6 @@ const VirtualTargetCardFoot: React.FC<Props> = ({
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="vtd-card-foot-field">
|
||||
<select
|
||||
className="vtd-card-foot-select vtd-card-foot-select--tf"
|
||||
aria-label="시간봉"
|
||||
value={candleType}
|
||||
onChange={e => onCandleTypeChange?.(e.target.value)}
|
||||
>
|
||||
{STRATEGY_CANDLE_TYPE_OPTIONS.map(o => (
|
||||
<option key={o.value} value={o.value}>{o.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,8 +13,7 @@ import VirtualTargetQuote from './VirtualTargetQuote';
|
||||
import VirtualTargetCardChart from './VirtualTargetCardChart';
|
||||
import VirtualTargetSignalPanel from './VirtualTargetSignalPanel';
|
||||
import VirtualTargetCardFoot from './VirtualTargetCardFoot';
|
||||
import { candleTypeToTimeframe } from '../../utils/strategyToChartIndicators';
|
||||
import { normalizeStartCandleType } from '../../utils/strategyStartNodes';
|
||||
import { resolveStrategyPrimaryTimeframe } from '../../utils/strategyToChartIndicators';
|
||||
import type { Timeframe } from '../../types';
|
||||
|
||||
interface Props {
|
||||
@@ -24,9 +23,8 @@ interface Props {
|
||||
strategyId: number | null;
|
||||
globalStrategyId: number | null;
|
||||
onStrategyChange?: (strategyId: number | null) => void;
|
||||
candleType: string;
|
||||
onCandleTypeChange?: (candleType: string) => void;
|
||||
snapshot: VirtualIndicatorSnapshot | undefined;
|
||||
signalLoading?: boolean;
|
||||
running: boolean;
|
||||
liveStatus?: VirtualLiveStatus;
|
||||
lastTickAt?: number;
|
||||
@@ -48,9 +46,8 @@ const VirtualTargetFocusView: React.FC<Props> = ({
|
||||
strategyId,
|
||||
globalStrategyId,
|
||||
onStrategyChange,
|
||||
candleType,
|
||||
onCandleTypeChange,
|
||||
snapshot,
|
||||
signalLoading = false,
|
||||
running,
|
||||
liveStatus = 'idle',
|
||||
lastTickAt,
|
||||
@@ -72,8 +69,10 @@ const VirtualTargetFocusView: React.FC<Props> = ({
|
||||
}, [running, lastTickAt, snapshot?.updatedAt]);
|
||||
const receiving = useLiveReceiveFlash(receiveSignal, running);
|
||||
const highlightReceiving = chartLiveReceiveHighlight && receiving;
|
||||
const evalCandleType = normalizeStartCandleType(candleType);
|
||||
const chartTimeframe = candleTypeToTimeframe(evalCandleType) as Timeframe;
|
||||
const chartTimeframe = useMemo(
|
||||
() => resolveStrategyPrimaryTimeframe(strategy) as Timeframe,
|
||||
[strategy],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`vtd-focus-wrap${highlightReceiving ? ' vtd-focus-wrap--receiving' : ''}`}>
|
||||
@@ -129,6 +128,7 @@ const VirtualTargetFocusView: React.FC<Props> = ({
|
||||
snapshot={snapshot}
|
||||
viewMode={viewMode}
|
||||
receiving={highlightReceiving}
|
||||
loading={signalLoading}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
@@ -139,9 +139,7 @@ const VirtualTargetFocusView: React.FC<Props> = ({
|
||||
strategies={strategies}
|
||||
strategyId={strategyId}
|
||||
globalStrategyId={globalStrategyId}
|
||||
candleType={evalCandleType}
|
||||
onStrategyChange={onStrategyChange}
|
||||
onCandleTypeChange={onCandleTypeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
type VirtualTargetItem,
|
||||
type VirtualCardViewMode,
|
||||
type VirtualSessionConfig,
|
||||
resolveTargetCandleType,
|
||||
} from '../../utils/virtualTradingStorage';
|
||||
import type { VirtualLiveStatus } from '../../hooks/useVirtualTargetLiveStatus';
|
||||
import type { VirtualIndicatorSnapshot } from '../../hooks/useVirtualIndicatorSnapshots';
|
||||
@@ -19,9 +18,9 @@ interface Props {
|
||||
targets: VirtualTargetItem[];
|
||||
strategies: StrategyDto[];
|
||||
snapshots: Record<string, VirtualIndicatorSnapshot>;
|
||||
snapshotLoadingByMarket?: Record<string, boolean>;
|
||||
session: Pick<VirtualSessionConfig, 'globalStrategyId' | 'executionType' | 'positionMode' | 'running'>;
|
||||
onTargetStrategyChange: (market: string, strategyId: number | null) => void;
|
||||
onTargetCandleTypeChange: (market: string, candleType: string) => void;
|
||||
liveStatusByMarket?: Record<string, VirtualLiveStatus>;
|
||||
lastTickAtByMarket?: Record<string, number>;
|
||||
selectedMarket?: string;
|
||||
@@ -38,9 +37,8 @@ interface Props {
|
||||
}
|
||||
|
||||
const VirtualTargetGrid: React.FC<Props> = ({
|
||||
targets, strategies, snapshots, session,
|
||||
targets, strategies, snapshots, snapshotLoadingByMarket = {}, session,
|
||||
onTargetStrategyChange,
|
||||
onTargetCandleTypeChange,
|
||||
liveStatusByMarket = {}, lastTickAtByMarket = {}, selectedMarket,
|
||||
onSelectMarket,
|
||||
theme = 'dark',
|
||||
@@ -119,9 +117,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
|
||||
strategyId={focusTarget.strategyId}
|
||||
globalStrategyId={session.globalStrategyId}
|
||||
onStrategyChange={id => onTargetStrategyChange(focusTarget.market, id)}
|
||||
candleType={resolveTargetCandleType(focusTarget, snapshots[focusTarget.market]?.timeframe)}
|
||||
onCandleTypeChange={ct => onTargetCandleTypeChange(focusTarget.market, ct)}
|
||||
snapshot={snapshots[focusTarget.market]}
|
||||
signalLoading={snapshotLoadingByMarket[focusTarget.market]}
|
||||
running={session.running}
|
||||
liveStatus={liveStatusByMarket[focusTarget.market] ?? (session.running ? 'connecting' : 'idle')}
|
||||
lastTickAt={lastTickAtByMarket[focusTarget.market]}
|
||||
@@ -147,9 +144,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
|
||||
strategyId={t.strategyId}
|
||||
globalStrategyId={session.globalStrategyId}
|
||||
onStrategyChange={id => onTargetStrategyChange(t.market, id)}
|
||||
candleType={resolveTargetCandleType(t, snapshots[t.market]?.timeframe)}
|
||||
onCandleTypeChange={ct => onTargetCandleTypeChange(t.market, ct)}
|
||||
snapshot={snapshots[t.market]}
|
||||
signalLoading={snapshotLoadingByMarket[t.market]}
|
||||
running={session.running}
|
||||
liveStatus={liveStatusByMarket[t.market] ?? (session.running ? 'connecting' : 'idle')}
|
||||
lastTickAt={lastTickAtByMarket[t.market]}
|
||||
|
||||
@@ -15,6 +15,8 @@ interface Props {
|
||||
snapshot: VirtualIndicatorSnapshot | undefined;
|
||||
viewMode?: VirtualCardViewMode;
|
||||
receiving?: boolean;
|
||||
/** 업비트 캔들 warm-up · live-conditions 수집 중 */
|
||||
loading?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -22,6 +24,7 @@ const VirtualTargetSignalPanel: React.FC<Props> = ({
|
||||
snapshot,
|
||||
viewMode = 'summary',
|
||||
receiving = false,
|
||||
loading = false,
|
||||
className = '',
|
||||
}) => {
|
||||
const rows = snapshot?.rows ?? [];
|
||||
@@ -36,8 +39,10 @@ const VirtualTargetSignalPanel: React.FC<Props> = ({
|
||||
|
||||
if (rows.length === 0) {
|
||||
return (
|
||||
<p className={`vtd-muted vtd-card-empty${className ? ` ${className}` : ''}`}>
|
||||
전략 조건·지표 데이터 없음
|
||||
<p className={`vtd-muted vtd-card-empty${loading ? ' vtd-card-empty--loading' : ''}${className ? ` ${className}` : ''}`}>
|
||||
{loading
|
||||
? '지표 데이터 수집 중… (업비트 캔들 · 실시간 연결)'
|
||||
: '전략 조건·지표 데이터 없음'}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user