백테스팅 분석결과 레포트 출력

This commit is contained in:
Macbook
2026-06-03 12:14:08 +09:00
parent c228f18c25
commit ee7b1a6123
11 changed files with 1207 additions and 15 deletions
@@ -11,6 +11,7 @@ import {
type BacktestResultRecord,
type BacktestAnalysis,
type BacktestSignal,
type StrategyDto,
} from '../utils/backendApi';
import type { Theme } from '../types';
import { buildEquityFromSignals } from '../utils/backtestEquity';
@@ -25,6 +26,11 @@ import BacktestExecutionList, { type ExecutionListTab } from './backtest/Backtes
import BacktestAnalysisChart from './backtest/BacktestAnalysisChart';
import BacktestKpiPanel from './backtest/BacktestKpiPanel';
import BacktestTradeHistoryCard from './backtest/BacktestTradeHistoryCard';
import BacktestAnalysisReportModal from './backtest/BacktestAnalysisReportModal';
import {
buildBacktestReportModel,
buildLiveReportModel,
} from '../utils/backtestReportModel';
import { buildLiveExecutionItems, paperTradesToSignals, type LiveExecutionItem } from '../utils/liveExecutionGroups';
import { PAPER_TRADES_CHANGED_EVENT } from '../utils/paperTradeEvents';
import {
@@ -34,6 +40,7 @@ import {
} from './strategyEditor/usePanelResize';
import '../styles/strategyEditorTheme.css';
import { getKoreanName } from '../utils/marketNameCache';
import { repairUtf8Mojibake } from '../utils/textEncoding';
const LEFT_KEY = 'btd-left-width';
const RIGHT_KEY = 'btd-right-width';
@@ -65,6 +72,8 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
const [selectedBacktest, setSelectedBacktest] = useState<BacktestResultRecord | null>(null);
const [selectedLive, setSelectedLive] = useState<LiveExecutionItem | null>(null);
const [loading, setLoading] = useState(true);
const [reportOpen, setReportOpen] = useState(false);
const [strategies, setStrategies] = useState<StrategyDto[]>([]);
const [leftWidth, setLeftWidth] = useState(() => readMinWidth(LEFT_KEY, LEFT_DEFAULT));
const [rightWidth, setRightWidth] = useState(() => readMinWidth(RIGHT_KEY, RIGHT_DEFAULT));
@@ -121,6 +130,7 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
]);
const strategyNames = Object.fromEntries(strategies.map(s => [s.id, s.name]));
const live = buildLiveExecutionItems(trades, summary, strategyNames);
setStrategies(strategies);
setRecords(list);
setLiveItems(live);
setSelectedBacktest(prev => (prev && list.some(x => x.id === prev.id) ? prev : list[0] ?? null));
@@ -224,6 +234,32 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
const footerTrades = equityData.trades;
const strategyNamesById = useMemo(
() => Object.fromEntries(
strategies.map(s => [s.id, repairUtf8Mojibake(s.name)]),
),
[strategies],
);
const reportModel = useMemo(() => {
if (tab === 'backtest' && selectedBacktest && backtestDetail?.analysis) {
return buildBacktestReportModel(
selectedBacktest,
backtestDetail.analysis,
backtestDetail.signals,
strategyNamesById,
);
}
if (tab === 'live' && selectedLive) {
return buildLiveReportModel(
selectedLive,
activeSignals,
chartProps?.timeframe ?? '1h',
);
}
return null;
}, [tab, selectedBacktest, backtestDetail, selectedLive, activeSignals, chartProps?.timeframe, strategyNamesById]);
const showRightDetail = tab === 'backtest'
? backtestDetail?.analysis != null
: selectedLive != null;
@@ -305,6 +341,8 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
signals={activeSignals}
strategyId={chartProps.strategyId}
theme={theme}
onOpenReport={() => setReportOpen(true)}
reportDisabled={!reportModel}
/>
) : (
<div className="btd-empty">
@@ -347,6 +385,12 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
</div>
</aside>
</div>
<BacktestAnalysisReportModal
open={reportOpen}
onClose={() => setReportOpen(false)}
model={reportModel}
/>
</div>
);
}