fix: 데이트레이딩 보유 종목이 화면에 안 보이던 문제 — 엔진 포지션 직접 표시
Portfolio 테이블은 stock_code가 unique라 같은 종목을 스윙+데이트레이딩으로 동시 보유하면 한 행에 합쳐지고 strategy_type도 하나만 남는다(LG씨엔에스 스윙 8 + DT 4 = 12주 'swing'). - 프론트: /daytrading/positions(엔진 인메모리)를 직접 조회해 '데이트레이딩' 그룹으로 표시하고, 스윙 행에서는 그 수량을 차감 - 백엔드 동기화: 전량이 DT 포지션일 때만 'daytrading' 태깅 (부분 혼재 시 스윙 청산 대상 유지) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
* - 종목 코드가 6자리 숫자면 KR, 그 외는 US
|
||||
*/
|
||||
import React from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import api from '../lib/api'
|
||||
import { Activity, TrendingUp, Clock } from 'lucide-react'
|
||||
import { Position } from '../types/trading'
|
||||
import { isUsStock } from '../utils/stockUtils'
|
||||
@@ -32,6 +34,17 @@ const formatPrice = (price: number, isUs: boolean): string => {
|
||||
return `${price.toLocaleString()}원`
|
||||
}
|
||||
|
||||
/** 데이트레이딩 엔진 포지션 (GET /daytrading/positions) */
|
||||
interface DayTradePosition {
|
||||
stock_code: string
|
||||
stock_name: string
|
||||
quantity: number
|
||||
entry_price: number
|
||||
current_price?: number
|
||||
pnl_pct?: number
|
||||
strategy?: string
|
||||
}
|
||||
|
||||
/** PortfolioItem Props 정의 */
|
||||
interface PortfolioItemProps {
|
||||
/** 보유 종목 데이터 */
|
||||
@@ -193,9 +206,37 @@ const PortfolioPanel = React.memo(({
|
||||
accountSetupRequired,
|
||||
accountSetupMessage,
|
||||
}: PortfolioPanelProps) => {
|
||||
const dayStocks = portfolio?.filter(s => s.strategy_type === 'daytrading') ?? []
|
||||
const swingStocks = portfolio?.filter(s => s.strategy_type !== 'daytrading') ?? []
|
||||
const total = portfolio?.length ?? 0
|
||||
// 데이트레이딩 포지션은 매매 엔진(인메모리)이 진실 — Portfolio 테이블은 종목당 1행이라
|
||||
// 같은 종목을 스윙+데이트레이딩으로 동시 보유하면 구분이 사라진다. 엔진에서 직접 조회해
|
||||
// 데이트레이딩 그룹으로 표시하고, 스윙 행에서는 그 수량을 뺀다. (2026-09-23)
|
||||
const { data: dtPositions } = useQuery<DayTradePosition[]>({
|
||||
queryKey: ['daytrading-positions'],
|
||||
queryFn: async () => (await api.get('/daytrading/positions')).data?.positions ?? [],
|
||||
refetchInterval: 30000,
|
||||
staleTime: 15000,
|
||||
})
|
||||
|
||||
const dayStocks: Position[] = (dtPositions ?? [])
|
||||
.filter(p => p.quantity > 0)
|
||||
.map(p => ({
|
||||
stock_code: p.stock_code,
|
||||
stock_name: p.stock_name,
|
||||
quantity: p.quantity,
|
||||
average_price: p.entry_price,
|
||||
current_price: p.current_price ?? p.entry_price,
|
||||
profit_loss_rate: p.pnl_pct ?? 0,
|
||||
strategy_type: 'daytrading',
|
||||
market: 'KRX',
|
||||
}))
|
||||
|
||||
const dtQty = new Map(dayStocks.map(d => [d.stock_code, d.quantity]))
|
||||
const swingStocks = (portfolio ?? [])
|
||||
.map(s => {
|
||||
const dq = dtQty.get(s.stock_code) ?? 0
|
||||
return dq > 0 ? { ...s, quantity: s.quantity - dq } : s
|
||||
})
|
||||
.filter(s => s.quantity > 0)
|
||||
const total = dayStocks.length + swingStocks.length
|
||||
|
||||
const renderGroup = (stocks: Position[], label: string, icon: React.ReactNode, color: string) => {
|
||||
if (stocks.length === 0) return null
|
||||
|
||||
Reference in New Issue
Block a user