mobile download
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { loadStrategy, loadStrategyTimeframes, repairStrategyTimeframes, saveStrategy, type StrategyDto } from './backendApi';
|
||||
import { loadStrategyFlowLayout } from './strategyEditorLayoutStorage';
|
||||
import {
|
||||
alignBuySellStartCandleTypesForSave,
|
||||
collectDslBranches,
|
||||
@@ -14,8 +13,7 @@ import { getStartCandleTypes, normalizeStartCandleType } from './strategyStartNo
|
||||
import type { LogicNode } from './strategyTypes';
|
||||
import type { StrategyFlowLayoutStore } from './strategyEditorLayoutStorage';
|
||||
|
||||
function collectFromFlowLayout(strategyId: number): string[] {
|
||||
const layout = loadStrategyFlowLayout(String(strategyId));
|
||||
function collectFromFlowLayout(layout: StrategyFlowLayoutStore | null | undefined): string[] {
|
||||
if (!layout) return [];
|
||||
const set = new Set<string>();
|
||||
for (const side of ['buy', 'sell'] as const) {
|
||||
@@ -43,9 +41,9 @@ function collectFromStrategyDto(strategy: StrategyDto | null | undefined): strin
|
||||
return [...set];
|
||||
}
|
||||
|
||||
/** 편집기·flow layout·전략 DTO에서 UI 평가 분봉 수집 (서버 DSL보다 우선) */
|
||||
/** 편집기·flow layout·전략 DTO에서 UI 평가 분봉 수집 */
|
||||
export function collectUiEvaluationTimeframes(
|
||||
strategyId: number,
|
||||
_strategyId: number,
|
||||
strategy?: StrategyDto | null,
|
||||
editorState?: { buy: EditorConditionState; sell: EditorConditionState },
|
||||
): string[] {
|
||||
@@ -56,7 +54,7 @@ export function collectUiEvaluationTimeframes(
|
||||
].map(normalizeStartCandleType);
|
||||
if (fromEditor.length > 0) return [...new Set(fromEditor)];
|
||||
}
|
||||
const fromLayout = collectFromFlowLayout(strategyId);
|
||||
const fromLayout = collectFromFlowLayout(strategy?.flowLayout);
|
||||
if (fromLayout.length > 0) return fromLayout;
|
||||
return collectFromStrategyDto(strategy);
|
||||
}
|
||||
@@ -85,7 +83,6 @@ export async function warnStrategyTimeframeMismatch(
|
||||
const extraOnServer = [...serverSet].filter(tf => !uiSet.has(tf));
|
||||
|
||||
if (missingOnServer.length === 0 && extraOnServer.length === 0) return true;
|
||||
// 양쪽 모두 1m만 — 일치로 간주
|
||||
if (uiSet.size === 1 && uiSet.has('1m') && serverSet.size === 1 && serverSet.has('1m')) {
|
||||
return true;
|
||||
}
|
||||
@@ -94,14 +91,14 @@ export async function warnStrategyTimeframeMismatch(
|
||||
const serverLabel = [...serverSet].join(', ') || '1m';
|
||||
window.alert(
|
||||
`전략 화면에는 ${uiLabel} 봉으로 설정되어 있지만, 서버에는 ${serverLabel} 만 저장되어 있습니다.\n\n`
|
||||
+ '전략편집기에서 「저장」을 누르거나 START 분봉을 다시 선택해 자동 저장된 뒤 실시간 체크를 켜 주세요.',
|
||||
+ '전략편집기에서 「저장」을 누르거나 START 분봉을 다시 선택해 DB에 반영한 뒤 실시간 체크를 켜 주세요.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildEditorStateFromStrategy(
|
||||
strategy: StrategyDto,
|
||||
layout: StrategyFlowLayoutStore | null,
|
||||
layout: StrategyFlowLayoutStore | null | undefined,
|
||||
side: 'buy' | 'sell',
|
||||
): EditorConditionState {
|
||||
const decoded = decodeConditionForEditor(
|
||||
@@ -118,14 +115,16 @@ function buildEditorStateFromStrategy(
|
||||
}
|
||||
|
||||
/**
|
||||
* flow layout(localStorage)의 START 분봉이 서버 DSL과 다르면 편집기 저장 없이 자동 동기화.
|
||||
* @returns false — layout 없음 등으로 동기화 불가
|
||||
* DB flow layout의 START 분봉이 서버 DSL과 다르면 자동 동기화.
|
||||
*/
|
||||
export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
strategyId: number,
|
||||
strategy?: StrategyDto | null,
|
||||
): Promise<boolean> {
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strategy);
|
||||
const strat = strategy ?? await loadStrategy(strategyId);
|
||||
if (!strat) return true;
|
||||
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strat);
|
||||
if (uiTfs.length === 0) return true;
|
||||
|
||||
let serverTfs: string[];
|
||||
@@ -139,14 +138,11 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
const missingOnServer = uiTfs.filter(tf => !serverSet.has(normalizeStartCandleType(tf)));
|
||||
if (missingOnServer.length === 0) return true;
|
||||
|
||||
const layout = loadStrategyFlowLayout(String(strategyId));
|
||||
const layout = strat.flowLayout;
|
||||
if (!layout) {
|
||||
return warnStrategyTimeframeMismatch(strategyId, strategy);
|
||||
return warnStrategyTimeframeMismatch(strategyId, strat);
|
||||
}
|
||||
|
||||
const strat = strategy ?? await loadStrategy(strategyId);
|
||||
if (!strat) return true;
|
||||
|
||||
const buyState = buildEditorStateFromStrategy(strat, layout, 'buy');
|
||||
const sellState = buildEditorStateFromStrategy(strat, layout, 'sell');
|
||||
|
||||
@@ -156,6 +152,7 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
strat.description ?? '',
|
||||
buyState,
|
||||
sellState,
|
||||
layout,
|
||||
);
|
||||
try {
|
||||
await repairStrategyTimeframes(strategyId);
|
||||
@@ -165,25 +162,46 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
return true;
|
||||
}
|
||||
|
||||
/** START 분봉 변경 시 전략 DSL을 서버에 즉시 반영 */
|
||||
/** 편집기 상태(DSL + flow layout)를 DB에 저장 — 실시간 평가 캐시는 서버에서 무효화됨 */
|
||||
export async function persistStrategyEditorState(
|
||||
strategyId: number,
|
||||
name: string,
|
||||
description: string,
|
||||
buy: EditorConditionState,
|
||||
sell: EditorConditionState,
|
||||
flowLayout: StrategyFlowLayoutStore,
|
||||
): Promise<StrategyDto> {
|
||||
const aligned = alignBuySellStartCandleTypesForSave(buy, sell);
|
||||
const encodedBuy = encodeConditionForSave(aligned.buy);
|
||||
const encodedSell = encodeConditionForSave(aligned.sell);
|
||||
const saved = await saveStrategy({
|
||||
id: strategyId,
|
||||
name,
|
||||
description,
|
||||
buyCondition: encodedBuy,
|
||||
sellCondition: encodedSell,
|
||||
flowLayout,
|
||||
enabled: true,
|
||||
});
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(new CustomEvent('gc:strategy-saved', {
|
||||
detail: { strategyId, buyCondition: encodedBuy, sellCondition: encodedSell, flowLayout },
|
||||
}));
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
/** @deprecated persistStrategyEditorState 사용 */
|
||||
export async function persistStrategyEvaluationTimeframes(
|
||||
strategyId: number,
|
||||
name: string,
|
||||
description: string,
|
||||
buy: EditorConditionState,
|
||||
sell: EditorConditionState,
|
||||
flowLayout?: StrategyFlowLayoutStore,
|
||||
): Promise<void> {
|
||||
const aligned = alignBuySellStartCandleTypesForSave(buy, sell);
|
||||
const encodedBuy = encodeConditionForSave(aligned.buy);
|
||||
const encodedSell = encodeConditionForSave(aligned.sell);
|
||||
await saveStrategy({
|
||||
id: strategyId,
|
||||
name,
|
||||
description,
|
||||
buyCondition: encodedBuy,
|
||||
sellCondition: encodedSell,
|
||||
enabled: true,
|
||||
});
|
||||
if (!flowLayout) return;
|
||||
await persistStrategyEditorState(strategyId, name, description, buy, sell, flowLayout);
|
||||
}
|
||||
|
||||
export type { StrategyFlowLayoutStore };
|
||||
|
||||
Reference in New Issue
Block a user