전략 시간봉 오류 수정

This commit is contained in:
Macbook
2026-05-28 02:01:36 +09:00
parent 01218b99c9
commit 5f9b20d907
9 changed files with 362 additions and 82 deletions
@@ -9,9 +9,11 @@ import { createPortal } from 'react-dom';
import type { Theme } from '../types';
import { useDraggablePanel } from '../hooks/useDraggablePanel';
import {
repairStrategyTimeframes,
saveLiveStrategySettings,
type LiveStrategySettingsDto,
} from '../utils/backendApi';
import { warnStrategyTimeframeMismatch } from '../utils/strategyTimeframeSync';
import { getKoreanName } from '../utils/marketNameCache';
interface Strategy {
@@ -70,6 +72,15 @@ const LiveStrategyPanel: React.FC<LiveStrategyPanelProps> = ({
if (virtualDriven) return;
const next: LiveStrategySettingsDto = { ...settings, ...patch, market };
const prev = settings;
if (next.isLiveCheck && next.strategyId != null) {
const ok = await warnStrategyTimeframeMismatch(next.strategyId);
if (!ok) return;
try {
await repairStrategyTimeframes(next.strategyId);
} catch {
/* repair optional */
}
}
onSettingsChange?.(next);
setSaving(true);
try {
@@ -34,6 +34,7 @@ import {
updateStartCandleTypes,
type EditorConditionState,
} from '../utils/strategyConditionSerde';
import { persistStrategyEvaluationTimeframes } from '../utils/strategyTimeframeSync';
import {
START_NODE_ID,
defaultStartMeta,
@@ -190,8 +191,13 @@ export default function StrategyEditorPage({ theme }: Props) {
}));
const buyLayoutRef = useRef(buyLayout);
const sellLayoutRef = useRef(sellLayout);
const buyConditionRef = useRef(buyCondition);
const sellConditionRef = useRef(sellCondition);
buyLayoutRef.current = buyLayout;
sellLayoutRef.current = sellLayout;
buyConditionRef.current = buyCondition;
sellConditionRef.current = sellCondition;
const timeframeAutosaveRef = useRef<number | null>(null);
const layoutRevisionRef = useRef(0);
const persistLayoutTimerRef = useRef<number | null>(null);
const layoutPersistReadyRef = useRef(false);
@@ -395,6 +401,22 @@ export default function StrategyEditorPage({ theme }: Props) {
schedulePersistFlowLayout(layoutStrategyKey);
}, [setCurrentRoot, setCurrentLayout, layoutStrategyKey, schedulePersistFlowLayout]);
const scheduleTimeframePersist = useCallback(() => {
if (selectedId == null || !stratName.trim()) return;
if (timeframeAutosaveRef.current != null) window.clearTimeout(timeframeAutosaveRef.current);
const id = selectedId;
const name = stratName;
const desc = stratDesc;
timeframeAutosaveRef.current = window.setTimeout(() => {
timeframeAutosaveRef.current = null;
const buy = toEditorState(buyConditionRef.current, buyLayoutRef.current);
const sell = toEditorState(sellConditionRef.current, sellLayoutRef.current);
void persistStrategyEvaluationTimeframes(id, name, desc, buy, sell).catch(err => {
console.warn('[StrategyEditor] START 분봉 자동저장 실패:', err);
});
}, 700);
}, [selectedId, stratName, stratDesc]);
const handleStartCandleTypesChange = useCallback((startId: string, candleTypes: string[]) => {
if (startId === START_NODE_ID) {
const synced = syncBuySellPrimaryStartCandleTypes(buyEditorState, sellEditorState, candleTypes);
@@ -415,16 +437,20 @@ export default function StrategyEditorPage({ theme }: Props) {
startCombineOp: normalizeStartCombineOp(synced.sell.startCombineOp),
}));
schedulePersistFlowLayout(layoutStrategyKey);
scheduleTimeframePersist();
return;
}
const state = signalTab === 'buy' ? buyEditorState : sellEditorState;
handleEditorStateChange(updateStartCandleTypes(state, startId, candleTypes));
scheduleTimeframePersist();
}, [
buyEditorState,
sellEditorState,
handleEditorStateChange,
layoutStrategyKey,
schedulePersistFlowLayout,
scheduleTimeframePersist,
signalTab,
]);
const handleStartCombineOpChange = useCallback((op: StartCombineOp) => {
@@ -51,8 +51,8 @@ import { coerceFiniteNumber } from '../utils/safeFormat';
import {
syncVirtualTargetsToBackend,
stopVirtualLiveOnBackend,
warnStrategyTimeframeMismatch,
} from '../utils/virtualLiveStrategySync';
import { warnStrategyTimeframeMismatch } from '../utils/strategyTimeframeSync';
import { pinStrategyEvaluationTimeframes } from '../utils/strategyTimeframePin';
import { persistVirtualTargetPinned } from '../utils/virtualTargetMutations';
import {
@@ -284,7 +284,7 @@ const VirtualTradingPage: React.FC<Props> = ({
return;
}
const globalStrat = strategies.find(s => s.id === session.globalStrategyId);
const ok = await warnStrategyTimeframeMismatch(globalStrat, session.globalStrategyId);
const ok = await warnStrategyTimeframeMismatch(session.globalStrategyId, globalStrat);
if (!ok) return;
try {
await syncVirtualTargetsToBackend(targets, session, true);