전략평가 상단툴바
This commit is contained in:
@@ -774,7 +774,78 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`btd-page se-page se-page--${theme}`}>
|
<div
|
||||||
|
className={`btd-page se-page se-page--${theme}${leftOpen ? ' seval-page--left-open' : ''}${rightOpen ? ' seval-page--right-open' : ''}`}
|
||||||
|
style={{
|
||||||
|
'--seval-left-width': `${leftWidth}px`,
|
||||||
|
'--seval-right-width': `${rightWidth}px`,
|
||||||
|
} as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<header className="btd-header seval-top-toolbar">
|
||||||
|
<div className="seval-top-toolbar__left">
|
||||||
|
<h1 className="seval-left-panel-title">전략 평가</h1>
|
||||||
|
<div className="seval-left-panel-toolbar">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="seval-strategy-add-btn seval-strategy-add-btn--panel"
|
||||||
|
title="전략 추가·편집"
|
||||||
|
aria-label="전략 추가·편집"
|
||||||
|
onClick={() => setEditorModalOpen(true)}
|
||||||
|
>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" aria-hidden>
|
||||||
|
<path d="M12 5v14M5 12h14" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<StrategyBulkEvalToolbar
|
||||||
|
isRunning={bulkEval.isRunning}
|
||||||
|
canStart={bulkEval.canStart}
|
||||||
|
onStart={() => { void bulkEval.startBulkEvaluation({ restart: true }); }}
|
||||||
|
onCancel={bulkEval.cancelBulkEvaluation}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="seval-top-toolbar__main">
|
||||||
|
{selectedStrategy && signalScanRunning && (
|
||||||
|
<span className="seval-header-analysis-busy">시그널 계산 중…</span>
|
||||||
|
)}
|
||||||
|
{selectedStrategy && analysisSummary && !signalScanRunning && (
|
||||||
|
<span
|
||||||
|
className="seval-analysis-summary"
|
||||||
|
title="일괄매수(최초 전액) · 분할매수(40/30/잔여) · 레포트와 동일 필터 기준"
|
||||||
|
>
|
||||||
|
<span className="seval-analysis-item">
|
||||||
|
매수:<strong>{padEvaluationSignalCount(analysisSummary.buyCount)}</strong>건
|
||||||
|
</span>
|
||||||
|
<span className="seval-analysis-sep" aria-hidden>·</span>
|
||||||
|
<span className="seval-analysis-item">
|
||||||
|
매도:<strong>{padEvaluationSignalCount(analysisSummary.sellCount)}</strong>건
|
||||||
|
</span>
|
||||||
|
<span className="seval-analysis-sep" aria-hidden>·</span>
|
||||||
|
<span className={`seval-analysis-item seval-analysis-return${
|
||||||
|
analysisSummary.fullBuyReturnPct > 0
|
||||||
|
? ' seval-analysis-return--pos'
|
||||||
|
: analysisSummary.fullBuyReturnPct < 0
|
||||||
|
? ' seval-analysis-return--neg'
|
||||||
|
: ''
|
||||||
|
}`}>
|
||||||
|
일괄:<strong>{formatEvaluationReturnPct(analysisSummary.fullBuyReturnPct)}</strong>
|
||||||
|
</span>
|
||||||
|
<span className="seval-analysis-sep" aria-hidden>·</span>
|
||||||
|
<span className={`seval-analysis-item seval-analysis-return${
|
||||||
|
analysisSummary.splitBuyReturnPct > 0
|
||||||
|
? ' seval-analysis-return--pos'
|
||||||
|
: analysisSummary.splitBuyReturnPct < 0
|
||||||
|
? ' seval-analysis-return--neg'
|
||||||
|
: ''
|
||||||
|
}`}>
|
||||||
|
분할:<strong>{formatEvaluationReturnPct(analysisSummary.splitBuyReturnPct)}</strong>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="seval-top-toolbar__right" aria-hidden />
|
||||||
|
</header>
|
||||||
|
|
||||||
<div className="btd-body">
|
<div className="btd-body">
|
||||||
<div
|
<div
|
||||||
className={`seval-left-wrap${leftOpen ? ' seval-left-wrap--open' : ''}`}
|
className={`seval-left-wrap${leftOpen ? ' seval-left-wrap--open' : ''}`}
|
||||||
@@ -782,28 +853,6 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
|||||||
>
|
>
|
||||||
<aside className={`btd-sidebar seval-left-sidebar${leftOpen ? ' seval-left-sidebar--open' : ''}`}>
|
<aside className={`btd-sidebar seval-left-sidebar${leftOpen ? ' seval-left-sidebar--open' : ''}`}>
|
||||||
<div className="btd-exec-list seval-left-panel">
|
<div className="btd-exec-list seval-left-panel">
|
||||||
<div className="seval-left-panel-header">
|
|
||||||
<h1 className="seval-left-panel-title">전략 평가</h1>
|
|
||||||
<div className="seval-left-panel-toolbar">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="seval-strategy-add-btn seval-strategy-add-btn--panel"
|
|
||||||
title="전략 추가·편집"
|
|
||||||
aria-label="전략 추가·편집"
|
|
||||||
onClick={() => setEditorModalOpen(true)}
|
|
||||||
>
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" aria-hidden>
|
|
||||||
<path d="M12 5v14M5 12h14" strokeLinecap="round" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<StrategyBulkEvalToolbar
|
|
||||||
isRunning={bulkEval.isRunning}
|
|
||||||
canStart={bulkEval.canStart}
|
|
||||||
onStart={() => { void bulkEval.startBulkEvaluation({ restart: true }); }}
|
|
||||||
onCancel={bulkEval.cancelBulkEvaluation}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="seval-left-tabs-row">
|
<div className="seval-left-tabs-row">
|
||||||
<div className="btd-exec-tabs" role="tablist">
|
<div className="btd-exec-tabs" role="tablist">
|
||||||
<button
|
<button
|
||||||
@@ -934,47 +983,6 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
|||||||
|
|
||||||
<main className={`btd-main${leftOpen ? '' : ' seval-main--left-collapsed'}`}>
|
<main className={`btd-main${leftOpen ? '' : ' seval-main--left-collapsed'}`}>
|
||||||
<div className="btd-main-content seval-main-content">
|
<div className="btd-main-content seval-main-content">
|
||||||
{selectedStrategy && (signalScanRunning || analysisSummary) && (
|
|
||||||
<div className="seval-chart-summary-bar">
|
|
||||||
{signalScanRunning && (
|
|
||||||
<span className="seval-header-analysis-busy">시그널 계산 중…</span>
|
|
||||||
)}
|
|
||||||
{analysisSummary && !signalScanRunning && (
|
|
||||||
<span
|
|
||||||
className="seval-analysis-summary"
|
|
||||||
title="일괄매수(최초 전액) · 분할매수(40/30/잔여) · 레포트와 동일 필터 기준"
|
|
||||||
>
|
|
||||||
<span className="seval-analysis-item">
|
|
||||||
매수:<strong>{padEvaluationSignalCount(analysisSummary.buyCount)}</strong>건
|
|
||||||
</span>
|
|
||||||
<span className="seval-analysis-sep" aria-hidden>·</span>
|
|
||||||
<span className="seval-analysis-item">
|
|
||||||
매도:<strong>{padEvaluationSignalCount(analysisSummary.sellCount)}</strong>건
|
|
||||||
</span>
|
|
||||||
<span className="seval-analysis-sep" aria-hidden>·</span>
|
|
||||||
<span className={`seval-analysis-item seval-analysis-return${
|
|
||||||
analysisSummary.fullBuyReturnPct > 0
|
|
||||||
? ' seval-analysis-return--pos'
|
|
||||||
: analysisSummary.fullBuyReturnPct < 0
|
|
||||||
? ' seval-analysis-return--neg'
|
|
||||||
: ''
|
|
||||||
}`}>
|
|
||||||
일괄:<strong>{formatEvaluationReturnPct(analysisSummary.fullBuyReturnPct)}</strong>
|
|
||||||
</span>
|
|
||||||
<span className="seval-analysis-sep" aria-hidden>·</span>
|
|
||||||
<span className={`seval-analysis-item seval-analysis-return${
|
|
||||||
analysisSummary.splitBuyReturnPct > 0
|
|
||||||
? ' seval-analysis-return--pos'
|
|
||||||
: analysisSummary.splitBuyReturnPct < 0
|
|
||||||
? ' seval-analysis-return--neg'
|
|
||||||
: ''
|
|
||||||
}`}>
|
|
||||||
분할:<strong>{formatEvaluationReturnPct(analysisSummary.splitBuyReturnPct)}</strong>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<StrategyEvaluationChart
|
<StrategyEvaluationChart
|
||||||
market={market}
|
market={market}
|
||||||
timeframe={chartTimeframe}
|
timeframe={chartTimeframe}
|
||||||
|
|||||||
@@ -1,5 +1,55 @@
|
|||||||
/* ── 전략 평가 — btd-page 레이아웃 위 보조 스타일 ─────────────────────────── */
|
/* ── 전략 평가 — btd-page 레이아웃 위 보조 스타일 ─────────────────────────── */
|
||||||
|
|
||||||
|
/* 상단 툴바 — btd-body 열 너비와 맞춤, 항상 표시 */
|
||||||
|
.seval-top-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 6px 0;
|
||||||
|
min-height: 44px;
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seval-top-toolbar__left {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: auto;
|
||||||
|
min-width: min-content;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 10px 0 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seval-page--left-open .seval-top-toolbar__left {
|
||||||
|
flex: 0 0 calc(var(--seval-left-width, 380px) + 16px + 4px);
|
||||||
|
width: calc(var(--seval-left-width, 380px) + 16px + 4px);
|
||||||
|
padding: 0 8px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seval-top-toolbar__main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seval-top-toolbar__right {
|
||||||
|
flex: 0 0 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seval-page--right-open .seval-top-toolbar__right {
|
||||||
|
flex-basis: calc(var(--seval-right-width, 440px) + 16px + 4px);
|
||||||
|
width: calc(var(--seval-right-width, 440px) + 16px + 4px);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── 좌측 패널 접기 ───────────────────────────────────────────────────────── */
|
/* ── 좌측 패널 접기 ───────────────────────────────────────────────────────── */
|
||||||
.seval-left-wrap {
|
.seval-left-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -216,15 +266,8 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.seval-left-panel-header {
|
.seval-left-panel {
|
||||||
display: flex;
|
padding-top: 2px;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 8px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 2px 2px 10px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
border-bottom: 1px solid var(--btd-divider, var(--se-border));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.seval-left-panel-title {
|
.seval-left-panel-title {
|
||||||
@@ -334,15 +377,6 @@
|
|||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.seval-chart-summary-bar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 34px;
|
|
||||||
padding: 0 2px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.seval-main-content .seval-chart {
|
.seval-main-content .seval-chart {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user