매매 시그널 알림 화면 수정
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
encodeConditionForSave,
|
||||
mergeStartMetaForLoad,
|
||||
normalizeStartCombineOp,
|
||||
syncEditorStateCandleTypesForSave,
|
||||
type EditorConditionState,
|
||||
} from './strategyConditionSerde';
|
||||
import { getStartCandleTypes, normalizeStartCandleType } from './strategyStartNodes';
|
||||
@@ -59,8 +60,20 @@ export function collectUiEvaluationTimeframes(
|
||||
return collectFromStrategyDto(strategy);
|
||||
}
|
||||
|
||||
function evaluationTimeframesMatch(uiTfs: string[], serverTfs: string[]): boolean {
|
||||
const uiSet = new Set(uiTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const serverSet = new Set(serverTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const missingOnServer = uiTfs.some(tf => !serverSet.has(normalizeStartCandleType(tf)));
|
||||
const extraOnServer = [...serverSet].some(tf => !uiSet.has(tf));
|
||||
if (!missingOnServer && !extraOnServer) return true;
|
||||
if (uiSet.size === 1 && uiSet.has('1m') && serverSet.size === 1 && serverSet.has('1m')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* UI 분봉과 서버 DSL 불일치 시 안내.
|
||||
* UI 분봉과 서버 DSL 불일치 시 자동 동기화 시도 후, 여전히 다르면 안내.
|
||||
* @returns false — 진행 불가(저장 필요)
|
||||
*/
|
||||
export async function warnStrategyTimeframeMismatch(
|
||||
@@ -68,7 +81,8 @@ export async function warnStrategyTimeframeMismatch(
|
||||
strategy?: StrategyDto | null,
|
||||
editorState?: { buy: EditorConditionState; sell: EditorConditionState },
|
||||
): Promise<boolean> {
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strategy, editorState);
|
||||
const strat = strategy ?? await loadStrategy(strategyId);
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strat, editorState);
|
||||
if (uiTfs.length === 0) return true;
|
||||
|
||||
let serverTfs: string[];
|
||||
@@ -77,18 +91,27 @@ export async function warnStrategyTimeframeMismatch(
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (evaluationTimeframesMatch(uiTfs, serverTfs)) return true;
|
||||
|
||||
const synced = await syncStrategyTimeframesFromLayoutIfNeeded(strategyId, strat);
|
||||
if (synced) {
|
||||
try {
|
||||
serverTfs = await loadStrategyTimeframes(strategyId);
|
||||
if (evaluationTimeframesMatch(uiTfs, serverTfs)) return true;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const uiSet = new Set(uiTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const serverSet = new Set(serverTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const missingOnServer = uiTfs.filter(tf => !serverSet.has(normalizeStartCandleType(tf)));
|
||||
const extraOnServer = [...serverSet].filter(tf => !uiSet.has(tf));
|
||||
|
||||
if (missingOnServer.length === 0 && extraOnServer.length === 0) return true;
|
||||
if (uiSet.size === 1 && uiSet.has('1m') && serverSet.size === 1 && serverSet.has('1m')) {
|
||||
const uiLabel = uiTfs.map(tf => normalizeStartCandleType(tf)).join(', ') || '1m';
|
||||
const serverLabel = [...serverSet].join(', ') || '1m';
|
||||
if (uiSet.size === serverSet.size && [...uiSet].every(tf => serverSet.has(tf))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uiLabel = uiTfs.map(tf => normalizeStartCandleType(tf)).join(', ') || '1m';
|
||||
const serverLabel = [...serverSet].join(', ') || '1m';
|
||||
window.alert(
|
||||
`전략 화면에는 ${uiLabel} 봉으로 설정되어 있지만, 서버에는 ${serverLabel} 만 저장되어 있습니다.\n\n`
|
||||
+ '전략편집기에서 「저장」을 누르거나 START 분봉을 다시 선택해 DB에 반영한 뒤 실시간 체크를 켜 주세요.',
|
||||
@@ -105,13 +128,14 @@ function buildEditorStateFromStrategy(
|
||||
(side === 'buy' ? strategy.buyCondition : strategy.sellCondition) as LogicNode | null,
|
||||
);
|
||||
const snap = layout?.[side];
|
||||
return {
|
||||
const merged: EditorConditionState = {
|
||||
root: decoded.root,
|
||||
startMeta: mergeStartMetaForLoad(decoded.startMeta, snap?.startMeta),
|
||||
extraStartIds: snap?.extraStartIds?.length ? snap.extraStartIds : decoded.extraStartIds,
|
||||
extraRoots: snap?.extraRoots ?? decoded.extraRoots,
|
||||
startCombineOp: normalizeStartCombineOp(snap?.startCombineOp ?? decoded.startCombineOp),
|
||||
};
|
||||
return syncEditorStateCandleTypesForSave(merged);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +145,7 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
strategyId: number,
|
||||
strategy?: StrategyDto | null,
|
||||
): Promise<boolean> {
|
||||
const strat = strategy ?? await loadStrategy(strategyId);
|
||||
const strat = (await loadStrategy(strategyId)) ?? strategy ?? null;
|
||||
if (!strat) return true;
|
||||
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strat);
|
||||
@@ -133,14 +157,18 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
const uiSet = new Set(uiTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const serverSet = new Set(serverTfs.map(tf => normalizeStartCandleType(tf)));
|
||||
const missingOnServer = uiTfs.filter(tf => !serverSet.has(normalizeStartCandleType(tf)));
|
||||
if (missingOnServer.length === 0) return true;
|
||||
|
||||
if (evaluationTimeframesMatch(uiTfs, serverTfs)) return true;
|
||||
|
||||
const layout = strat.flowLayout;
|
||||
if (!layout) {
|
||||
return warnStrategyTimeframeMismatch(strategyId, strat);
|
||||
try {
|
||||
await repairStrategyTimeframes(strategyId);
|
||||
const repaired = await loadStrategyTimeframes(strategyId);
|
||||
return evaluationTimeframesMatch(uiTfs, repaired);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const buyState = buildEditorStateFromStrategy(strat, layout, 'buy');
|
||||
|
||||
Reference in New Issue
Block a user