투자관리 화면 수정
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
loadStrategies,
|
||||
loadLiveStrategySettings,
|
||||
@@ -6,13 +6,18 @@ import {
|
||||
type PaperSummaryDto,
|
||||
type StrategyDto,
|
||||
} from '../../utils/backendApi';
|
||||
import { loadVirtualSession } from '../../utils/virtualTradingStorage';
|
||||
import { loadVirtualSession, type VirtualTargetItem } from '../../utils/virtualTradingStorage';
|
||||
import { resolveVirtualTargetNames } from '../../utils/virtualTargetNames';
|
||||
import type { TickerData } from '../../hooks/useMarketTicker';
|
||||
import { fmtKrw } from '../TradeOrderPanel';
|
||||
import PaperSplitPanel from './PaperSplitPanel';
|
||||
|
||||
interface Props {
|
||||
market: string;
|
||||
summary: PaperSummaryDto | null;
|
||||
/** 가상매매 투자대상 — 보유 목록은 이 종목만 표시 */
|
||||
virtualTargets?: VirtualTargetItem[];
|
||||
tickers?: Map<string, TickerData>;
|
||||
paperAutoTradeEnabled?: boolean;
|
||||
onMarketSelect?: (market: string) => void;
|
||||
}
|
||||
@@ -20,6 +25,8 @@ interface Props {
|
||||
const PaperLeftStrategyTab: React.FC<Props> = ({
|
||||
market,
|
||||
summary,
|
||||
virtualTargets = [],
|
||||
tickers,
|
||||
paperAutoTradeEnabled = false,
|
||||
onMarketSelect,
|
||||
}) => {
|
||||
@@ -91,7 +98,23 @@ const PaperLeftStrategyTab: React.FC<Props> = ({
|
||||
</>
|
||||
);
|
||||
|
||||
const positions = summary?.positions ?? [];
|
||||
const targetMarkets = useMemo(
|
||||
() => new Set(virtualTargets.map(t => t.market)),
|
||||
[virtualTargets],
|
||||
);
|
||||
|
||||
const positions = useMemo(() => {
|
||||
const all = (summary?.positions ?? []).filter(p => p.quantity > 0);
|
||||
if (targetMarkets.size === 0) return [];
|
||||
const order = virtualTargets.map(t => t.market);
|
||||
return all
|
||||
.filter(p => targetMarkets.has(p.symbol))
|
||||
.sort((a, b) => {
|
||||
const ia = order.indexOf(a.symbol);
|
||||
const ib = order.indexOf(b.symbol);
|
||||
return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib);
|
||||
});
|
||||
}, [summary?.positions, targetMarkets, virtualTargets]);
|
||||
|
||||
const bottom = (
|
||||
<div className="ptd-item-card-section ptd-holdings-card-section">
|
||||
@@ -99,14 +122,21 @@ const PaperLeftStrategyTab: React.FC<Props> = ({
|
||||
<span>보유 목록</span>
|
||||
<span className="vtd-target-count">{positions.length}종목</span>
|
||||
</div>
|
||||
{positions.length === 0 ? (
|
||||
<p className="ptd-muted ptd-item-card-empty">보유 종목이 없습니다.</p>
|
||||
{targetMarkets.size === 0 ? (
|
||||
<p className="ptd-muted ptd-item-card-empty">가상매매 투자대상이 없습니다. 가상투자에서 종목을 추가하세요.</p>
|
||||
) : positions.length === 0 ? (
|
||||
<p className="ptd-muted ptd-item-card-empty">투자대상 종목 중 보유 수량이 있는 종목이 없습니다.</p>
|
||||
) : (
|
||||
<div className="ptd-item-card-scroll">
|
||||
<div className="vtd-target-list ptd-item-card-list">
|
||||
{positions.map(p => {
|
||||
const code = p.symbol.replace(/^KRW-/, '');
|
||||
const name = p.koreanName || code;
|
||||
const ticker = tickers?.get(p.symbol);
|
||||
const vt = virtualTargets.find(t => t.market === p.symbol);
|
||||
const { koreanName: ko, englishName: en } = resolveVirtualTargetNames(
|
||||
p.symbol,
|
||||
p.koreanName ?? vt?.koreanName ?? ticker?.koreanName,
|
||||
vt?.englishName,
|
||||
);
|
||||
const up = (p.profitLoss ?? 0) >= 0;
|
||||
const selected = market === p.symbol;
|
||||
const retPct = p.profitLossPct;
|
||||
@@ -132,8 +162,8 @@ const PaperLeftStrategyTab: React.FC<Props> = ({
|
||||
>
|
||||
<div className="vtd-target-item-main">
|
||||
<div className="vtd-target-names">
|
||||
<span className="vtd-target-ko">{name}</span>
|
||||
<span className="vtd-target-en">{code}/KRW</span>
|
||||
<span className="vtd-target-ko">{ko}</span>
|
||||
<span className="vtd-target-en">{en}</span>
|
||||
</div>
|
||||
{p.profitLoss != null && (
|
||||
<div className="vtd-target-item-actions">
|
||||
|
||||
Reference in New Issue
Block a user