전략평가 화면 수정

This commit is contained in:
Macbook
2026-06-12 15:32:00 +09:00
parent 8aae8489ae
commit b560b9de5c
10 changed files with 413 additions and 13 deletions
+30 -7
View File
@@ -1472,18 +1472,41 @@ export async function fetchLiveConditionStatus(
strategyId: number,
barTimeSec?: number | null,
indicatorParams?: Record<string, Record<string, unknown>> | null,
chartBars?: Array<{
time: number;
open: number;
high: number;
low: number;
close: number;
volume: number;
}> | null,
chartTimeframe?: string | null,
): Promise<LiveConditionStatusDto | null> {
if (indicatorParams && Object.keys(indicatorParams).length > 0) {
const normalizedBarTime = barTimeSec != null && Number.isFinite(barTimeSec)
? Math.floor(barTimeSec)
: null;
const useChartBars = chartBars != null && chartBars.length > 0 && chartTimeframe;
if (useChartBars || (indicatorParams && Object.keys(indicatorParams).length > 0)) {
return request<LiveConditionStatusDto>('/strategy/live-conditions/evaluate', {
method: 'POST',
cache: 'no-store',
body: JSON.stringify({
market,
strategyId,
barTimeSec: barTimeSec != null && Number.isFinite(barTimeSec)
? Math.floor(barTimeSec)
: null,
indicatorParams,
barTimeSec: normalizedBarTime,
indicatorParams: indicatorParams ?? undefined,
bars: useChartBars
? chartBars.map(b => ({
time: b.time,
open: b.open,
high: b.high,
low: b.low,
close: b.close,
volume: b.volume,
}))
: undefined,
timeframe: useChartBars ? chartTimeframe : undefined,
}),
});
}
@@ -1491,8 +1514,8 @@ export async function fetchLiveConditionStatus(
market,
strategyId: String(strategyId),
});
if (barTimeSec != null && Number.isFinite(barTimeSec)) {
params.set('barTimeSec', String(Math.floor(barTimeSec)));
if (normalizedBarTime != null) {
params.set('barTimeSec', String(normalizedBarTime));
}
return request<LiveConditionStatusDto>(`/strategy/live-conditions?${params}`, {
cache: 'no-store',
@@ -13,12 +13,16 @@ import { normalizeConditionRow } from './virtualSignalMetrics';
import type { VirtualIndicatorSnapshot } from '../hooks/useVirtualIndicatorSnapshots';
import { mergeRows } from './strategyEvaluationMerge';
import type { OHLCVBar } from '../types';
export async function fetchEvaluationSnapshotAtBar(
market: string,
strategyId: number,
strategy: StrategyDto | undefined,
barTimeSec: number | null,
indicatorParams?: Record<string, Record<string, unknown>> | null,
chartBars?: OHLCVBar[] | null,
chartTimeframe?: string | null,
): Promise<VirtualIndicatorSnapshot | null> {
const normalizedMarket = normalizeMarketCode(market);
const hydrated = strategy ? hydrateStrategyDto(strategy) : undefined;
@@ -37,6 +41,8 @@ export async function fetchEvaluationSnapshotAtBar(
strategyId,
barTimeSec,
indicatorParams,
chartBars,
chartTimeframe,
);
} catch {
status = null;
@@ -126,6 +126,25 @@ export function getTrafficLightState(matchRate: number, metrics: ConditionMetric
return 'red';
}
/** side 카드 헤드라인 — DSL 전체 Rule 충족 시 100% (리프 비율과 구분) */
export function resolveSideHeadlineMatchRate(
metrics: ConditionMetric[],
overallMet?: boolean | null,
): number {
if (overallMet === true) return 100;
return computeMatchRate(metrics);
}
export function resolveSideTrafficState(
metrics: ConditionMetric[],
overallMet?: boolean | null,
): TrafficLightState {
const rate = resolveSideHeadlineMatchRate(metrics, overallMet);
if (overallMet === true) return 'blue';
if (overallMet === false && rate === 0) return 'red';
return getTrafficLightState(rate, metrics);
}
export function formatUpdatedTime(ts: number | undefined): string {
if (!ts) return '—';
return new Date(ts).toLocaleTimeString('ko-KR', {