시간봉 알림 수정

This commit is contained in:
Macbook
2026-05-27 16:32:02 +09:00
parent 972e8f7178
commit d7ceb8cbfd
4 changed files with 105 additions and 20 deletions
+67 -9
View File
@@ -1,7 +1,53 @@
import { saveLiveStrategySettings } from './backendApi';
import { loadStrategyTimeframes, saveLiveStrategySettings } from './backendApi';
import { collectDslBranches } from './strategyConditionSerde';
import { pinStrategyEvaluationTimeframes } from './strategyTimeframePin';
import { normalizeStartCandleType } from './strategyStartNodes';
import type { StrategyDto } from './backendApi';
import type { LogicNode } from './strategyTypes';
import type { VirtualSessionConfig, VirtualTargetItem } from './virtualTradingStorage';
import { resolveTargetCandleType } from './virtualTradingStorage';
function collectUiTimeframes(strategy: StrategyDto | null | undefined): string[] {
if (!strategy) return [];
const set = new Set<string>();
const buy = strategy.buyCondition as LogicNode | null | undefined;
const sell = strategy.sellCondition as LogicNode | null | undefined;
for (const b of [
...collectDslBranches(buy ?? null),
...collectDslBranches(sell ?? null),
]) {
set.add(normalizeStartCandleType(b.candleType));
}
return [...set];
}
/**
* UI(편집기)와 서버 DSL의 평가 분봉이 다르면 안내.
* @returns false — 계속 진행 불가(재저장 필요)
*/
export async function warnStrategyTimeframeMismatch(
strategy: StrategyDto | null | undefined,
strategyId: number,
): Promise<boolean> {
if (!strategy) return true;
const uiTfs = collectUiTimeframes(strategy);
if (uiTfs.length === 0 || (uiTfs.length === 1 && uiTfs[0] === '1m')) return true;
let serverTfs: string[];
try {
serverTfs = await loadStrategyTimeframes(strategyId);
} catch {
return true;
}
const serverSet = new Set(serverTfs.map(tf => normalizeStartCandleType(tf)));
const missingOnServer = uiTfs.filter(tf => !serverSet.has(tf));
if (missingOnServer.length === 0) return true;
window.alert(
`전략 화면에는 ${uiTfs.join(', ')} 봉으로 보이지만, 서버에는 ${[...serverSet].join(', ') || '1m'} 만 저장되어 있습니다.\n\n`
+ '전략편집기에서 「저장」을 눌러 다시 저장한 뒤 가상매매를 시작하세요.',
);
return false;
}
/** 가상투자 대상 종목을 백엔드 live strategy 설정과 동기화 */
export async function syncVirtualTargetsToBackend(
@@ -18,23 +64,35 @@ export async function syncVirtualTargetsToBackend(
skipWatchlistSync: true,
} as const;
if (isLiveCheck && session.globalStrategyId != null) {
await saveLiveStrategySettings({
market: targets[0].market,
strategyId: session.globalStrategyId,
isLiveCheck: true,
executionType: session.executionType,
positionMode: session.positionMode,
skipWatchlistSync: true,
});
}
const pinJobs = targets.map(async t => {
const strategyId = t.strategyId ?? session.globalStrategyId;
if (strategyId == null) return;
await pinStrategyEvaluationTimeframes(t.market, strategyId);
});
await Promise.all(pinJobs);
await Promise.all(
targets.map(t =>
saveLiveStrategySettings({
market: t.market,
strategyId: t.strategyId ?? session.globalStrategyId,
candleType: normalizeStartCandleType(resolveTargetCandleType(t)),
isPinned: !!t.pinned,
skipGlobalTemplate: true,
...shared,
}),
),
);
await saveLiveStrategySettings({
market: targets[0].market,
strategyId: session.globalStrategyId,
...shared,
});
}
/** 가상투자 중지 — 대상 종목 체크 해제 + 전역 liveStrategyCheck OFF */