전략알림 수정

This commit is contained in:
Macbook
2026-05-27 16:51:57 +09:00
parent d7ceb8cbfd
commit aca895e9fd
13 changed files with 701 additions and 123 deletions
+5
View File
@@ -1129,6 +1129,11 @@ export async function loadStrategyTimeframes(strategyId: number): Promise<string
return list?.length ? list : ['1m'];
}
/** DB DSL TIMEFRAME 래핑 보정 (3분봉 전략이 1m으로 평가되는 레거시 수정) */
export async function repairStrategyTimeframes(strategyId: number): Promise<void> {
await request(`/strategies/${strategyId}/repair-timeframes`, { method: 'POST' });
}
/** 차트 실시간 파이프라인 pin (STOMP warm-up) */
export async function pinCandleWatch(market: string, candleType: string): Promise<void> {
try {
+71 -5
View File
@@ -1,4 +1,4 @@
import type { LogicNode } from './strategyTypes';
import type { LogicNode, ConditionDSL } from './strategyTypes';
import { generateNodeId } from './strategyTypes';
import { subtreeToFlatOrphans } from './strategyFlowLayout';
import {
@@ -82,16 +82,79 @@ export function updateBranchRoot(
};
}
/** START 분봉 변경 시 조건 노드의 left/rightCandleType 동기화 */
function syncSubtreeCandleTypes(node: LogicNode | null, candleType: string): LogicNode | null {
if (!node) return null;
const ct = normalizeStartCandleType(candleType);
if (node.type === 'CONDITION' && node.condition) {
const cond = node.condition as ConditionDSL;
return {
...node,
condition: {
...cond,
leftCandleType: ct,
rightCandleType: ct,
},
};
}
if (node.children?.length) {
return {
...node,
children: node.children.map(c => syncSubtreeCandleTypes(c, ct)).filter(Boolean) as LogicNode[],
};
}
return node;
}
function collectExplicitCandleTypesFromTree(node: LogicNode | null, out: Set<string>): void {
if (!node) return;
if (node.type === 'TIMEFRAME' && node.candleType) {
out.add(normalizeStartCandleType(node.candleType));
}
if (node.type === 'CONDITION' && node.condition) {
const c = node.condition as ConditionDSL;
if (c.leftCandleType) out.add(normalizeStartCandleType(c.leftCandleType));
if (c.rightCandleType) out.add(normalizeStartCandleType(c.rightCandleType));
}
node.children?.forEach(ch => collectExplicitCandleTypesFromTree(ch, out));
}
function inferPrimaryCandleFromTree(root: LogicNode | null): string {
const set = new Set<string>();
collectExplicitCandleTypesFromTree(root, set);
const non1m = [...set].filter(ct => ct !== '1m');
if (non1m.length >= 1) return non1m[0];
if (set.size >= 1) return [...set][0];
return DEFAULT_START_CANDLE;
}
export function updateStartCandleType(
state: EditorConditionState,
startId: string,
candleType: string,
): EditorConditionState {
const ct = normalizeStartCandleType(candleType);
const nextMeta = {
...state.startMeta,
[startId]: { candleType: ct },
};
if (startId === START_NODE_ID) {
return {
...state,
startMeta: nextMeta,
root: syncSubtreeCandleTypes(state.root, ct),
};
}
return {
...state,
startMeta: {
...state.startMeta,
[startId]: { candleType: normalizeStartCandleType(candleType) },
startMeta: nextMeta,
extraRoots: {
...state.extraRoots,
[startId]: syncSubtreeCandleTypes(state.extraRoots[startId] ?? null, ct),
},
};
}
@@ -225,9 +288,12 @@ export function decodeConditionForEditor(dsl: LogicNode | null): EditorCondition
};
}
const inferred = inferPrimaryCandleFromTree(dsl);
return {
root: dsl,
startMeta: defaultStartMeta(),
startMeta: {
[START_NODE_ID]: { candleType: inferred },
},
extraStartIds: [],
extraRoots: {},
startCombineOp: DEFAULT_START_COMBINE_OP,
+6 -1
View File
@@ -1,4 +1,4 @@
import { loadStrategyTimeframes, pinCandleWatch } from './backendApi';
import { loadStrategyTimeframes, pinCandleWatch, repairStrategyTimeframes } from './backendApi';
import { normalizeStartCandleType } from './strategyStartNodes';
/** 전략 DSL 평가 분봉 전체 pin — 1m 집계·상위 봉 warm-up */
@@ -6,6 +6,11 @@ export async function pinStrategyEvaluationTimeframes(
market: string,
strategyId: number,
): Promise<string[]> {
try {
await repairStrategyTimeframes(strategyId);
} catch {
/* 서버 미배포·권한 등 — timeframes 조회만 진행 */
}
const raw = await loadStrategyTimeframes(strategyId);
const timeframes = [...new Set(raw.map(tf => normalizeStartCandleType(tf)))];
if (timeframes.length === 0) timeframes.push('1m');