지표탭 추가 수정
This commit is contained in:
@@ -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]);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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[],
|
||||
|
||||
Reference in New Issue
Block a user