백테스팅 종목 선택란을 MarketSearchPanel 한글 검색 드롭다운으로 교체
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
import '../styles/backtestDashboard.css';
|
import '../styles/backtestDashboard.css';
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { MarketSearchPanel } from './MarketSearchPanel';
|
||||||
import {
|
import {
|
||||||
loadBacktestResults,
|
loadBacktestResults,
|
||||||
deleteBacktestResult,
|
deleteBacktestResult,
|
||||||
@@ -17,7 +19,6 @@ import {
|
|||||||
type BacktestSignal,
|
type BacktestSignal,
|
||||||
type StrategyDto,
|
type StrategyDto,
|
||||||
} from '../utils/backendApi';
|
} from '../utils/backendApi';
|
||||||
import { UPBIT_MARKETS } from '../utils/upbitApi';
|
|
||||||
import { timeframeToCandleType } from '../utils/chartCandleType';
|
import { timeframeToCandleType } from '../utils/chartCandleType';
|
||||||
import type { Theme, Timeframe } from '../types';
|
import type { Theme, Timeframe } from '../types';
|
||||||
import { buildEquityFromSignals } from '../utils/backtestEquity';
|
import { buildEquityFromSignals } from '../utils/backtestEquity';
|
||||||
@@ -101,6 +102,9 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
|
|||||||
const [runStrategyId, setRunStrategyId] = useState<number | null>(null);
|
const [runStrategyId, setRunStrategyId] = useState<number | null>(null);
|
||||||
const [runLoading, setRunLoading] = useState(false);
|
const [runLoading, setRunLoading] = useState(false);
|
||||||
const [runError, setRunError] = useState<string | null>(null);
|
const [runError, setRunError] = useState<string | null>(null);
|
||||||
|
const [showMarketSearch, setShowMarketSearch] = useState(false);
|
||||||
|
const [marketQuery, setMarketQuery] = useState('');
|
||||||
|
const marketBtnRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const [leftWidth, setLeftWidth] = useState(() => readMinWidth(LEFT_KEY, LEFT_DEFAULT));
|
const [leftWidth, setLeftWidth] = useState(() => readMinWidth(LEFT_KEY, LEFT_DEFAULT));
|
||||||
const [rightWidth, setRightWidth] = useState(() => readMinWidth(RIGHT_KEY, RIGHT_DEFAULT));
|
const [rightWidth, setRightWidth] = useState(() => readMinWidth(RIGHT_KEY, RIGHT_DEFAULT));
|
||||||
@@ -372,17 +376,42 @@ export function BacktestHistoryPage({ theme = 'dark' }: Props) {
|
|||||||
|
|
||||||
{/* 빠른 실행 툴바 */}
|
{/* 빠른 실행 툴바 */}
|
||||||
<div className="btd-header-run">
|
<div className="btd-header-run">
|
||||||
<select
|
<button
|
||||||
className="btd-run-select"
|
ref={marketBtnRef}
|
||||||
value={runMarket}
|
type="button"
|
||||||
onChange={e => setRunMarket(e.target.value)}
|
className="btd-run-market-btn"
|
||||||
disabled={runLoading}
|
disabled={runLoading}
|
||||||
title="종목"
|
title="종목 검색"
|
||||||
|
onClick={() => {
|
||||||
|
setMarketQuery('');
|
||||||
|
setShowMarketSearch(v => !v);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{UPBIT_MARKETS.map(m => (
|
<span className="btd-run-market-ko">{getKoreanName(runMarket)}</span>
|
||||||
<option key={m} value={m}>{m.replace('KRW-', '')}</option>
|
<span className="btd-run-market-sym">{runMarket.replace('KRW-', '')}</span>
|
||||||
))}
|
<svg className="btd-run-market-caret" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
|
||||||
</select>
|
<polyline points="6 9 12 15 18 9"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{showMarketSearch && ReactDOM.createPortal(
|
||||||
|
<MarketSearchPanel
|
||||||
|
currentMarket={runMarket}
|
||||||
|
query={marketQuery}
|
||||||
|
onQueryChange={setMarketQuery}
|
||||||
|
onSelect={market => {
|
||||||
|
setRunMarket(market);
|
||||||
|
setShowMarketSearch(false);
|
||||||
|
setMarketQuery('');
|
||||||
|
}}
|
||||||
|
onClose={() => {
|
||||||
|
setShowMarketSearch(false);
|
||||||
|
setMarketQuery('');
|
||||||
|
}}
|
||||||
|
anchorRect={marketBtnRef.current?.getBoundingClientRect() ?? null}
|
||||||
|
anchorPlacement="dropdown"
|
||||||
|
/>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
<select
|
<select
|
||||||
className="btd-run-select"
|
className="btd-run-select"
|
||||||
value={runTimeframe}
|
value={runTimeframe}
|
||||||
|
|||||||
@@ -848,6 +848,51 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 종목 검색 버튼 (MarketSearchPanel 드롭다운 트리거) */
|
||||||
|
.btd-run-market-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
background: var(--se-bg-elevated, var(--bg2));
|
||||||
|
border: 1px solid var(--se-border);
|
||||||
|
color: var(--se-text);
|
||||||
|
border-radius: 7px;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 0.76rem;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 30px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.15s, background 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 90px;
|
||||||
|
max-width: 140px;
|
||||||
|
}
|
||||||
|
.btd-run-market-btn:hover:not(:disabled) {
|
||||||
|
border-color: var(--se-accent, #3d9be9);
|
||||||
|
background: color-mix(in srgb, var(--se-accent, #3d9be9) 8%, var(--se-bg-elevated, var(--bg2)));
|
||||||
|
}
|
||||||
|
.btd-run-market-btn:disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.btd-run-market-ko {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.76rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.btd-run-market-sym {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
color: var(--se-text-muted, #888);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.btd-run-market-caret {
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0.55;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.btd-run-select {
|
.btd-run-select {
|
||||||
background: var(--se-bg-elevated, var(--bg2));
|
background: var(--se-bg-elevated, var(--bg2));
|
||||||
border: 1px solid var(--se-border);
|
border: 1px solid var(--se-border);
|
||||||
|
|||||||
Reference in New Issue
Block a user