전략편집기 수정

This commit is contained in:
Macbook
2026-05-25 17:56:25 +09:00
parent aac6454724
commit 02d14e4b2b
39 changed files with 1974 additions and 288 deletions
+7 -6
View File
@@ -8,6 +8,7 @@
*/
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { coerceFiniteNumber } from '../utils/safeFormat';
// ── 타입 ────────────────────────────────────────────────────────────────────
export interface OrderbookUnit {
@@ -74,18 +75,18 @@ export function useUpbitOrderbook(market: string) {
// 매도: 높은 가격 → 낮은 가격 (역순)
const asks: OrderbookDisplayUnit[] = units
.map(u => ({
price: u.ask_price,
size: u.ask_size,
percentage: maxSize > 0 ? (u.ask_size / maxSize) * 100 : 0,
price: coerceFiniteNumber(u.ask_price) ?? 0,
size: coerceFiniteNumber(u.ask_size) ?? 0,
percentage: maxSize > 0 ? ((coerceFiniteNumber(u.ask_size) ?? 0) / maxSize) * 100 : 0,
type: 'ask' as const,
}))
.reverse();
// 매수: 높은 가격 → 낮은 가격 (정순 1번 unit이 best bid)
const bids: OrderbookDisplayUnit[] = units.map(u => ({
price: u.bid_price,
size: u.bid_size,
percentage: maxSize > 0 ? (u.bid_size / maxSize) * 100 : 0,
price: coerceFiniteNumber(u.bid_price) ?? 0,
size: coerceFiniteNumber(u.bid_size) ?? 0,
percentage: maxSize > 0 ? ((coerceFiniteNumber(u.bid_size) ?? 0) / maxSize) * 100 : 0,
type: 'bid' as const,
}));
@@ -10,11 +10,12 @@ import {
type LiveConditionRowDto,
} from '../utils/backendApi';
import { formatIndicatorDisplayLabel } from '../utils/indicatorRegistry';
import { coerceFiniteNumber } from '../utils/safeFormat';
import {
coerceFiniteNumber,
extractVirtualConditions,
type VirtualConditionRow,
} from '../utils/virtualStrategyConditions';
import { normalizeConditionRow } from '../utils/virtualSignalMetrics';
export interface VirtualIndicatorSnapshot {
market: string;
@@ -37,20 +38,20 @@ function liveFallbackKey(r: LiveConditionRowDto): string {
function liveRowToVirtual(r: LiveConditionRowDto): VirtualConditionRow & { currentValue: number | null } {
const plotKey = r.plotKey ?? r.indicatorType;
return {
return normalizeConditionRow({
id: r.id,
indicatorType: r.indicatorType,
displayName: formatIndicatorDisplayLabel(r.indicatorType),
conditionType: r.conditionType,
conditionLabel: r.conditionLabel,
targetValue: coerceFiniteNumber(r.targetValue),
targetValue: r.targetValue,
timeframe: r.timeframe,
side: r.side,
plotKey,
satisfied: r.satisfied,
thresholdLabel: r.thresholdLabel,
currentValue: coerceFiniteNumber(r.currentValue),
};
currentValue: r.currentValue,
});
}
function mergeRows(
@@ -71,15 +72,15 @@ function mergeRows(
return base.map(row => {
const liveRow = liveById.get(row.id) ?? liveByKey.get(rowFallbackKey(row));
if (!liveRow) {
return { ...row, currentValue: null as number | null, satisfied: null };
return normalizeConditionRow({ ...row, currentValue: null, satisfied: null });
}
return {
return normalizeConditionRow({
...row,
satisfied: liveRow.satisfied,
thresholdLabel: liveRow.thresholdLabel,
currentValue: coerceFiniteNumber(liveRow.currentValue),
targetValue: coerceFiniteNumber(liveRow.targetValue) ?? row.targetValue,
};
currentValue: liveRow.currentValue,
targetValue: liveRow.targetValue ?? row.targetValue,
});
});
}
@@ -97,7 +98,7 @@ async function buildSnapshot(
market,
strategyId,
timeframe: [...new Set(baseRows.map(r => r.timeframe))].join(', '),
rows: baseRows.map(r => ({ ...r, currentValue: null, satisfied: null })),
rows: baseRows.map(r => normalizeConditionRow({ ...r, currentValue: null, satisfied: null })),
updatedAt: Date.now(),
matchRate: 0,
};
@@ -115,7 +116,7 @@ async function buildSnapshot(
market,
strategyId,
timeframe: [...new Set(baseRows.map(r => r.timeframe))].join(', '),
rows: baseRows.map(r => ({ ...r, currentValue: null, satisfied: null })),
rows: baseRows.map(r => normalizeConditionRow({ ...r, currentValue: null, satisfied: null })),
updatedAt: Date.now(),
matchRate: 0,
};