전략 평가 이전종목, 다음종목 이동 기능
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import type { MarketInfo, TickerData } from '../hooks/useMarketTicker';
|
||||
|
||||
export type MarketSortKey = 'name' | 'price' | 'change' | 'volume';
|
||||
export type MarketSortDir = 'asc' | 'desc';
|
||||
|
||||
export interface MarketSearchRow {
|
||||
market: string;
|
||||
korean_name: string;
|
||||
english_name: string;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
function compareNullableNum(
|
||||
a: number | null | undefined,
|
||||
b: number | null | undefined,
|
||||
mul: number,
|
||||
): number {
|
||||
if (a == null && b == null) return 0;
|
||||
if (a == null) return 1;
|
||||
if (b == null) return -1;
|
||||
return mul * (a - b);
|
||||
}
|
||||
|
||||
export function marketInfosToRows(infos: MarketInfo[]): MarketSearchRow[] {
|
||||
return infos.map(m => ({
|
||||
market: m.market,
|
||||
korean_name: m.koreanName,
|
||||
english_name: m.englishName,
|
||||
symbol: m.market.replace('KRW-', ''),
|
||||
}));
|
||||
}
|
||||
|
||||
export function filterMarketRows(rows: MarketSearchRow[], query: string): MarketSearchRow[] {
|
||||
const q = query.trim();
|
||||
if (!q) return rows;
|
||||
const upper = q.toUpperCase();
|
||||
const lower = q.toLowerCase();
|
||||
return rows.filter(m =>
|
||||
m.korean_name.includes(q)
|
||||
|| m.symbol.toUpperCase().includes(upper)
|
||||
|| m.english_name.toLowerCase().includes(lower)
|
||||
|| m.market.toUpperCase().includes(upper),
|
||||
);
|
||||
}
|
||||
|
||||
/** MarketSearchPanel 과 동일 — 관심종목 우선·거래대금 또는 사용자 정렬 */
|
||||
export function buildSortedMarketRows(
|
||||
rows: MarketSearchRow[],
|
||||
tickers: Map<string, TickerData>,
|
||||
favs: Set<string>,
|
||||
sortKey: MarketSortKey | null,
|
||||
sortDir: MarketSortDir,
|
||||
): MarketSearchRow[] {
|
||||
if (!sortKey) {
|
||||
return [...rows].sort((a, b) => {
|
||||
const fa = favs.has(a.market) ? 1 : 0;
|
||||
const fb = favs.has(b.market) ? 1 : 0;
|
||||
if (fb !== fa) return fb - fa;
|
||||
const va = tickers.get(a.market)?.accTradePrice24 ?? 0;
|
||||
const vb = tickers.get(b.market)?.accTradePrice24 ?? 0;
|
||||
return vb - va;
|
||||
});
|
||||
}
|
||||
const mul = sortDir === 'asc' ? 1 : -1;
|
||||
return [...rows].sort((a, b) => {
|
||||
switch (sortKey) {
|
||||
case 'name':
|
||||
return mul * a.korean_name.localeCompare(b.korean_name, 'ko');
|
||||
case 'price':
|
||||
return compareNullableNum(
|
||||
tickers.get(a.market)?.tradePrice,
|
||||
tickers.get(b.market)?.tradePrice,
|
||||
mul,
|
||||
);
|
||||
case 'change':
|
||||
return compareNullableNum(
|
||||
tickers.get(a.market)?.changeRate,
|
||||
tickers.get(b.market)?.changeRate,
|
||||
mul,
|
||||
);
|
||||
case 'volume':
|
||||
return mul * (
|
||||
(tickers.get(a.market)?.accTradePrice24 ?? 0)
|
||||
- (tickers.get(b.market)?.accTradePrice24 ?? 0)
|
||||
);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function buildSortedMarketCodes(
|
||||
marketInfos: MarketInfo[],
|
||||
tickers: Map<string, TickerData>,
|
||||
favs: Set<string>,
|
||||
query: string,
|
||||
sortKey: MarketSortKey | null,
|
||||
sortDir: MarketSortDir,
|
||||
): string[] {
|
||||
const rows = filterMarketRows(marketInfosToRows(marketInfos), query);
|
||||
return buildSortedMarketRows(rows, tickers, favs, sortKey, sortDir).map(r => r.market);
|
||||
}
|
||||
|
||||
export function adjacentMarketInList(
|
||||
orderedMarkets: string[],
|
||||
currentMarket: string,
|
||||
direction: 'prev' | 'next',
|
||||
): string | null {
|
||||
if (orderedMarkets.length === 0) return null;
|
||||
let idx = orderedMarkets.indexOf(currentMarket);
|
||||
if (idx < 0) {
|
||||
idx = direction === 'next' ? -1 : orderedMarkets.length;
|
||||
}
|
||||
const nextIdx = direction === 'prev' ? idx - 1 : idx + 1;
|
||||
if (nextIdx < 0 || nextIdx >= orderedMarkets.length) return null;
|
||||
return orderedMarkets[nextIdx] ?? null;
|
||||
}
|
||||
Reference in New Issue
Block a user