지표탭 추가 수정

This commit is contained in:
Macbook
2026-05-27 13:16:02 +09:00
parent c72b987ff7
commit f22abbdc19
14 changed files with 505 additions and 76 deletions
+5 -6
View File
@@ -50,7 +50,7 @@ import {
mergeIndicators,
reorderIndicatorGroup,
splitIndicatorPane,
buildIndicatorConfigsFromTypes,
replaceIndicatorConfigsFromTypes,
} from './utils/indicatorPaneMerge';
import { useAppSettings } from './hooks/useAppSettings';
import { clampVirtualTargetMax } from './utils/virtualTargetLimits';
@@ -1368,7 +1368,7 @@ function App() {
});
}, [layoutDef.count, activeSlot, getParams, getVisualConfig]);
/** 지표 추가 팝업 — 탭 전체 추가 (한 번에 상태 반영) */
/** 지표 추가 팝업 — 탭 전체 추가 (기존 보조지표 제거 후 탭 지표만 적용) */
const handleAddIndicators = useCallback((types: string[]) => {
if (!types.length) return;
if (layoutDef.count > 1) {
@@ -1378,10 +1378,9 @@ function App() {
return;
}
}
setIndicators(prev => {
const added = buildIndicatorConfigsFromTypes(types, prev, getParams, getVisualConfig, newIndId);
return added.length > 0 ? [...prev, ...added] : prev;
});
setIndicators(
replaceIndicatorConfigsFromTypes(types, getParams, getVisualConfig, newIndId),
);
}, [layoutDef.count, activeSlot, getParams, getVisualConfig]);
/**
+5 -6
View File
@@ -32,7 +32,7 @@ import {
mergeIndicators,
reorderIndicatorGroup,
splitIndicatorPane,
buildIndicatorConfigsFromTypes,
replaceIndicatorConfigsFromTypes,
} from '../utils/indicatorPaneMerge';
import { saveSlot } from '../utils/backendApi';
import type {
@@ -86,7 +86,7 @@ function newIndId() { return `sind_${++_slotIndCounter}_${Date.now()}`; }
export interface ChartSlotHandle {
/** 지표 추가 */
addIndicator: (type: string, fromConfig?: IndicatorConfig) => void;
/** 지표 일괄 추가 (탭 전체 추가) */
/** 지표 일괄 추가 (탭 전체 추가 — 기존 지표 제거 후 탭 지표만 적용) */
addIndicators: (types: string[]) => void;
/** 지표 제거 (type 기준) */
removeIndicatorByType: (type: string) => void;
@@ -273,10 +273,9 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
},
addIndicators: (types: string[]) => {
if (!types.length) return;
setIndicators(prev => {
const added = buildIndicatorConfigsFromTypes(types, prev, getParams, getVisualConfig, newIndId);
return added.length > 0 ? [...prev, ...added] : prev;
});
setIndicators(
replaceIndicatorConfigsFromTypes(types, getParams, getVisualConfig, newIndId),
);
},
removeIndicatorByType: (type: string) => {
setIndicators(prev => prev.filter(i => i.type !== type));
@@ -65,7 +65,7 @@ export const LiveSignalNotifier = forwardRef<LiveSignalNotifierHandle, Props>(fu
candleTime: marker.time,
strategyName: strategyNameByMarket?.[marker.market] ?? strategyName,
executionType: executionTypeByMarket?.[marker.market] ?? executionType,
candleType: marketSubscriptions?.find(s => s.market === marker.market)?.candleType ?? '1m',
candleType: marker.candleType ?? '1m',
});
// STOMP만 수신·DB 저장 누락 시 배지 동기화
window.setTimeout(() => { void refreshHistory(); }, 800);
+7 -8
View File
@@ -382,17 +382,16 @@ const IndDropdown: React.FC<IndDropdownProps> = ({
const validTypes = addAllContext.types.filter(
type => INDICATOR_REGISTRY.some(d => d.type === type),
);
const toAdd = validTypes.filter(type => !isActive(type));
if (toAdd.length === 0) {
window.alert('추가할 지표가 없습니다. (이미 모두 차트에 있습니다)');
if (validTypes.length === 0) {
window.alert('추가할 지표가 없습니다.');
return;
}
const msg = `${addAllContext.label}에 있는 지표 ${toAdd.length}를 차트에 추가하시겠습니까?`;
const msg = `기존 보조지표를 모두 제거하고 ${addAllContext.label} 지표 ${validTypes.length}로 교체하시겠습니까?`;
if (!window.confirm(msg)) return;
if (onAddMany) {
onAddMany(toAdd);
onAddMany(validTypes);
} else {
toAdd.forEach(type => onAdd(type));
validTypes.forEach(type => onAdd(type));
}
};
@@ -569,8 +568,8 @@ const IndDropdown: React.FC<IndDropdownProps> = ({
type="button"
className="ind-panel-add-all"
disabled={addAllDisabled}
title={addAllDisabled ? '전체 탭에서는 사용할 수 없습니다' : '현재 탭의 모든 지표 추가'}
aria-label="현재 탭의 모든 지표 추가"
title={addAllDisabled ? '전체 탭에서는 사용할 수 없습니다' : '기존 보조지표를 제거하고 현재 탭 지표로 교체'}
aria-label="현재 탭 지표로 보조지표 교체"
onClick={handleAddAllInTab}
>
<IcPlus />
+11 -3
View File
@@ -15,6 +15,7 @@ export interface LiveMarker {
signal: 'BUY' | 'SELL';
price: number;
market: string;
candleType: string;
}
export interface MarketCandleSub {
@@ -54,12 +55,12 @@ export function useLiveStrategyMarkers({
const onSignalRef = useRef(onSignal);
onSignalRef.current = onSignal;
const pushMarker = useCallback((market: string, data: {
const pushMarker = useCallback((market: string, candleType: string, data: {
time: number; close: number; signal: string;
}) => {
if (data.signal !== 'BUY' && data.signal !== 'SELL') return;
const key = `${market}:${data.time}:${data.signal}`;
const key = `${market}:${candleType}:${data.time}:${data.signal}`;
if (seenRef.current.has(key)) return;
seenRef.current.add(key);
@@ -68,6 +69,7 @@ export function useLiveStrategyMarkers({
signal: data.signal as 'BUY' | 'SELL',
price: data.close,
market,
candleType,
};
const prev = markersByMarketRef.current[market] ?? [];
@@ -117,9 +119,15 @@ export function useLiveStrategyMarkers({
time: number;
close: number;
signal?: string;
candleType?: string;
}>(msg);
if (!data?.signal || data.signal === 'NONE') return;
pushMarker(market, { time: data.time, close: data.close, signal: data.signal });
const resolvedCandleType = data.candleType ?? ct;
pushMarker(market, resolvedCandleType, {
time: data.time,
close: data.close,
signal: data.signal,
});
});
});
+10
View File
@@ -153,6 +153,16 @@ type GetVisual = (
defaultHlines?: HLineDef[],
) => { plots: PlotDef[]; hlines: HLineDef[]; cloudColors?: IchimokuCloudColors };
/** 탭 전체 추가 — 기존 차트 지표를 제거하고 types 목록만 새로 구성 */
export function replaceIndicatorConfigsFromTypes(
types: string[],
getParams: GetParams,
getVisualConfig: GetVisual,
newId: () => string,
): IndicatorConfig[] {
return buildIndicatorConfigsFromTypes(types, [], getParams, getVisualConfig, newId);
}
/** 지표 타입 목록 → 차트에 추가할 IndicatorConfig 배열 (이미 활성인 type 제외) */
export function buildIndicatorConfigsFromTypes(
types: string[],