전략편집기 기준값 보조지표 설정값 동기화

This commit is contained in:
Macbook
2026-05-28 09:20:06 +09:00
parent 9137864f48
commit e2816b037f
18 changed files with 710 additions and 153 deletions
+11 -6
View File
@@ -34,6 +34,7 @@ import {
updateStartCandleTypes,
type EditorConditionState,
} from '../utils/strategyConditionSerde';
import { migrateLogicRootForEditor } from '../utils/strategyConditionNormalize';
import { persistStrategyEvaluationTimeframes } from '../utils/strategyTimeframeSync';
import {
START_NODE_ID,
@@ -141,7 +142,7 @@ export default function StrategyEditorPage({ theme }: Props) {
() => buildStrategyEditorDefFromSettings(getParams, getVisualConfig),
[getParams, getVisualConfig, settingsRevision],
);
const settingsSyncRef = useRef(0);
const settingsSyncRef = useRef(-1);
const [strategies, setStrategies] = useState<StrategyDto[]>(() => loadStratsLocal());
const [selectedId, setSelectedId] = useState<number | null>(null);
@@ -289,6 +290,7 @@ export default function StrategyEditorPage({ theme }: Props) {
/** 보조지표 설정 변경 시 오버라이드되지 않은 조건 노드·캔버스 기준값 동기화 */
useEffect(() => {
if (!settingsLoaded) return;
if (settingsRevision <= settingsSyncRef.current) return;
settingsSyncRef.current = settingsRevision;
@@ -316,7 +318,7 @@ export default function StrategyEditorPage({ theme }: Props) {
extraRoots: syncExtraRoots(prev.extraRoots ?? {}, 'sell'),
}));
bumpLayoutSeed(layoutStrategyKey, signalTab);
}, [settingsRevision, DEF, layoutStrategyKey, signalTab, bumpLayoutSeed]);
}, [settingsLoaded, settingsRevision, DEF, layoutStrategyKey, signalTab, bumpLayoutSeed]);
const resetFlowLayout = useCallback((strategyKey: string, tab: 'buy' | 'sell' = 'buy') => {
const emptyBuy = emptySignalFlowLayout();
@@ -553,15 +555,18 @@ export default function StrategyEditorPage({ theme }: Props) {
const sellDecoded = decodeConditionForEditor(s.sellCondition ?? null);
const stored = loadStrategyFlowLayout(String(s.id));
const buyMigrated = migrateLogicRootForEditor(buyDecoded.root, stored?.buy.orphans ?? [], DEF, 'buy');
const sellMigrated = migrateLogicRootForEditor(sellDecoded.root, stored?.sell.orphans ?? [], DEF, 'sell');
const buySynced = syncLogicRootWithGlobalDef(
buyDecoded.root,
stored?.buy.orphans ?? [],
buyMigrated.root,
buyMigrated.orphans,
DEF,
'buy',
);
const sellSynced = syncLogicRootWithGlobalDef(
sellDecoded.root,
stored?.sell.orphans ?? [],
sellMigrated.root,
sellMigrated.orphans,
DEF,
'sell',
);
+1 -1
View File
@@ -511,7 +511,7 @@ const IndDropdown: React.FC<IndDropdownProps> = ({
onHeaderPointerDown, headerTouchStyle,
panelStyle,
headerCursor,
} = useDraggablePanel({ centerOnMount: true, initialPosition: { x: 0, y: 56 } });
} = useDraggablePanel({ centerOnMount: true });
return (
// 배경 클릭 시 닫힘
@@ -6,6 +6,8 @@ import {
getCompositeRightCandleType,
getConditionRightPeriod,
getConditionThreshold,
getChartReferenceThreshold,
isThresholdOverridden,
getConditionValuePeriod,
getCompositePeriodPresetOptions,
getPeriodPresetOptions,
@@ -52,8 +54,9 @@ export default function ConditionNodeSettings({
}, [onClose]);
const inheritedThreshold = getDefaultConditionFields(condition.indicatorType, signalType, def).r;
const rightThreshold = getConditionThreshold(condition, 'right', inheritedThreshold);
const showThreshold = hasEditableThreshold(condition) && rightThreshold != null;
const chartRef = getChartReferenceThreshold(condition, 'right', inheritedThreshold, def);
const strategyThreshold = getConditionThreshold(condition, 'right', inheritedThreshold, def);
const showThreshold = hasEditableThreshold(condition) && chartRef != null;
const periodPresets = getPeriodPresetOptions(condition.indicatorType, def);
const thresholdPresets = getThresholdPresetOptions(condition.indicatorType);
const thresholdBounds = getThresholdBounds(condition.indicatorType);
@@ -139,18 +142,24 @@ export default function ConditionNodeSettings({
/>
</label>
) : null}
{showThreshold && rightThreshold != null && (
<label className="se-flow-settings-field">
<span></span>
<ComboNumberInput
value={rightThreshold}
options={thresholdPresets}
min={thresholdBounds.min}
max={thresholdBounds.max}
allowDecimal
onChange={v => onChange(setConditionThreshold(condition, 'right', v))}
/>
</label>
{showThreshold && chartRef != null && (
<>
<label className="se-flow-settings-field se-flow-settings-field--readonly">
<span> ( )</span>
<output className="se-flow-settings-readout">{chartRef}</output>
</label>
<label className="se-flow-settings-field">
<span> {isThresholdOverridden(condition) ? ' (전용)' : ''}</span>
<ComboNumberInput
value={strategyThreshold ?? chartRef}
options={thresholdPresets}
min={thresholdBounds.min}
max={thresholdBounds.max}
allowDecimal
onChange={v => onChange(setConditionThreshold(condition, 'right', v, def))}
/>
</label>
</>
)}
{condition.composite && (
<p className="se-flow-settings-hint">{compositeDisplayName(condition.indicatorType)}</p>