merge: 주식 보유종목 인텔리전스 탭 (액션·이슈·포트건강·현재가)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -697,6 +697,10 @@ export const refreshScreenerSnap = () => apiPost('/api/stock/screener
|
|||||||
export const listScreenerRuns = (limit = 30) => apiGet (`/api/stock/screener/runs?limit=${limit}`);
|
export const listScreenerRuns = (limit = 30) => apiGet (`/api/stock/screener/runs?limit=${limit}`);
|
||||||
export const getScreenerRun = (id) => apiGet (`/api/stock/screener/runs/${id}`);
|
export const getScreenerRun = (id) => apiGet (`/api/stock/screener/runs/${id}`);
|
||||||
|
|
||||||
|
// ---- Stock Holdings Intelligence ----
|
||||||
|
export const stockHoldingsIntel = () => apiGet('/api/stock/holdings/intel');
|
||||||
|
export const stockHoldingsHistory = (ticker, days = 30) => apiGet(`/api/stock/holdings/intel/history?ticker=${ticker}&days=${days}`);
|
||||||
|
|
||||||
// --- Lotto Weight Evolver ---
|
// --- Lotto Weight Evolver ---
|
||||||
|
|
||||||
export async function fetchEvolverStatus() {
|
export async function fetchEvolverStatus() {
|
||||||
|
|||||||
@@ -3016,3 +3016,219 @@
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════
|
||||||
|
Holdings Intelligence Tab
|
||||||
|
══════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.hi-panel {
|
||||||
|
/* reuses stock-panel--wide layout */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 포트 건강 요약 줄 ── */
|
||||||
|
.hi-health {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #94a3b8;
|
||||||
|
background: rgba(148, 163, 184, 0.06);
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-health__sep {
|
||||||
|
margin: 0 8px;
|
||||||
|
color: rgba(148, 163, 184, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-health__pnl {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-health__pnl.is-up { color: #22c55e; }
|
||||||
|
.hi-health__pnl.is-down { color: #ef4444; }
|
||||||
|
|
||||||
|
/* ── 분석 기준일 ── */
|
||||||
|
.hi-date {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #64748b;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 카드 그리드 ── */
|
||||||
|
.hi-cards {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 개별 카드 ── */
|
||||||
|
.hi-card {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-action-badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__ticker {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #64748b;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__pnl {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__pnl.is-up { color: #22c55e; }
|
||||||
|
.hi-card__pnl.is-down { color: #ef4444; }
|
||||||
|
|
||||||
|
.hi-card__close {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #94a3b8;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__reasons {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #94a3b8;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 기술강도 미니 바 ── */
|
||||||
|
.hi-card__score {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-card__score strong {
|
||||||
|
color: #93c5fd;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-score-bar {
|
||||||
|
flex: 1;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(148, 163, 184, 0.15);
|
||||||
|
border-radius: 2px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-score-bar::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0; bottom: 0;
|
||||||
|
width: var(--score, 0%);
|
||||||
|
background: #93c5fd;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 이슈 목록 ── */
|
||||||
|
.hi-card__issues {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-issue {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-issue__dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 빈 상태 ── */
|
||||||
|
.hi-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px 16px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-empty__icon {
|
||||||
|
font-size: 36px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-empty__sub {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 면책 고지 ── */
|
||||||
|
.hi-disclaimer {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #475569;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid rgba(148, 163, 184, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 탭 버튼 (holdings intel) ── */
|
||||||
|
.stock-main-tab--holdings-intel {
|
||||||
|
/* reuses stock-main-tab base styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.hi-card__head {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-health {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hi-score-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import SwipeableView from '../../components/SwipeableView';
|
|||||||
import {
|
import {
|
||||||
formatNumber, formatPercent,
|
formatNumber, formatPercent,
|
||||||
toNumeric, profitColorClass,
|
toNumeric, profitColorClass,
|
||||||
TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR,
|
TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL,
|
||||||
} from './stockUtils';
|
} from './stockUtils';
|
||||||
|
|
||||||
/* ── hooks ──────────────────────────────────────────────────────── */
|
/* ── hooks ──────────────────────────────────────────────────────── */
|
||||||
@@ -22,6 +22,7 @@ import useAdvisor from './hooks/useAdvisor';
|
|||||||
import PortfolioTab from './components/PortfolioTab';
|
import PortfolioTab from './components/PortfolioTab';
|
||||||
import ReportTab from './components/ReportTab';
|
import ReportTab from './components/ReportTab';
|
||||||
import AdvisorTab from './components/AdvisorTab';
|
import AdvisorTab from './components/AdvisorTab';
|
||||||
|
import HoldingsIntelTab from './components/HoldingsIntelTab';
|
||||||
import SellHistoryDrawer from './components/SellHistoryDrawer';
|
import SellHistoryDrawer from './components/SellHistoryDrawer';
|
||||||
|
|
||||||
/* ── component ───────────────────────────────────────────────────── */
|
/* ── component ───────────────────────────────────────────────────── */
|
||||||
@@ -30,8 +31,8 @@ const StockTrade = () => {
|
|||||||
const [activeTab, setActiveTab] = React.useState(TAB_REPORT);
|
const [activeTab, setActiveTab] = React.useState(TAB_REPORT);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const TAB_ORDER = [TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR];
|
const TAB_ORDER = [TAB_PORTFOLIO, TAB_REPORT, TAB_ADVISOR, TAB_HOLDINGS_INTEL];
|
||||||
const tabLabels = ['포트폴리오', '리포트', '어드바이저'];
|
const tabLabels = ['포트폴리오', '리포트', '어드바이저', '보유종목 인텔'];
|
||||||
const tabIndex = TAB_ORDER.indexOf(activeTab);
|
const tabIndex = TAB_ORDER.indexOf(activeTab);
|
||||||
const handleTabChange = useCallback((idx) => setActiveTab(TAB_ORDER[idx]), []); // eslint-disable-line react-hooks/exhaustive-deps
|
const handleTabChange = useCallback((idx) => setActiveTab(TAB_ORDER[idx]), []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
@@ -166,7 +167,9 @@ const StockTrade = () => {
|
|||||||
? <PortfolioTab pf={pf} asset={asset} handleSell={handleSell} handleSaveSnapshot={handleSaveSnapshot} />
|
? <PortfolioTab pf={pf} asset={asset} handleSell={handleSell} handleSaveSnapshot={handleSaveSnapshot} />
|
||||||
: tabId === TAB_REPORT
|
: tabId === TAB_REPORT
|
||||||
? <ReportTab pf={pf} report={report} ai={ai} marketCtx={marketCtx} />
|
? <ReportTab pf={pf} report={report} ai={ai} marketCtx={marketCtx} />
|
||||||
: <AdvisorTab pf={pf} advisor={advisor} />,
|
: tabId === TAB_ADVISOR
|
||||||
|
? <AdvisorTab pf={pf} advisor={advisor} />
|
||||||
|
: <HoldingsIntelTab />,
|
||||||
}))}
|
}))}
|
||||||
activeIndex={tabIndex}
|
activeIndex={tabIndex}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
@@ -178,6 +181,7 @@ const StockTrade = () => {
|
|||||||
{ id: TAB_PORTFOLIO, icon: '💼', label: '쟁승토리 계좌', badge: pf.portfolioHoldings.length || null },
|
{ id: TAB_PORTFOLIO, icon: '💼', label: '쟁승토리 계좌', badge: pf.portfolioHoldings.length || null },
|
||||||
{ id: TAB_REPORT, icon: '📊', label: '리포트', sub: '분석·AI코치' },
|
{ id: TAB_REPORT, icon: '📊', label: '리포트', sub: '분석·AI코치' },
|
||||||
{ id: TAB_ADVISOR, icon: '🧠', label: 'AI 어드바이저', sub: 'Gemini Pro', className: 'stock-main-tab--advisor' },
|
{ id: TAB_ADVISOR, icon: '🧠', label: 'AI 어드바이저', sub: 'Gemini Pro', className: 'stock-main-tab--advisor' },
|
||||||
|
{ id: TAB_HOLDINGS_INTEL, icon: '🔍', label: '보유종목 인텔', sub: '신호·이슈', className: 'stock-main-tab--holdings-intel' },
|
||||||
].map(({ id, icon, label, sub, badge, className: cls }) => (
|
].map(({ id, icon, label, sub, badge, className: cls }) => (
|
||||||
<button
|
<button
|
||||||
key={id}
|
key={id}
|
||||||
@@ -198,6 +202,7 @@ const StockTrade = () => {
|
|||||||
)}
|
)}
|
||||||
{activeTab === TAB_REPORT && <ReportTab pf={pf} report={report} ai={ai} marketCtx={marketCtx} />}
|
{activeTab === TAB_REPORT && <ReportTab pf={pf} report={report} ai={ai} marketCtx={marketCtx} />}
|
||||||
{activeTab === TAB_ADVISOR && <AdvisorTab pf={pf} advisor={advisor} />}
|
{activeTab === TAB_ADVISOR && <AdvisorTab pf={pf} advisor={advisor} />}
|
||||||
|
{activeTab === TAB_HOLDINGS_INTEL && <HoldingsIntelTab />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
155
src/pages/stock/components/HoldingsIntelTab.jsx
Normal file
155
src/pages/stock/components/HoldingsIntelTab.jsx
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Loading from '../../../components/Loading';
|
||||||
|
import { stockHoldingsIntel } from '../../../api';
|
||||||
|
|
||||||
|
/* ── action config ────────────────────────────────────────────────── */
|
||||||
|
const ACTION_MAP = {
|
||||||
|
add: { label: '추가매수', color: '#22c55e', bg: 'rgba(34,197,94,0.12)' },
|
||||||
|
hold: { label: '보유', color: '#94a3b8', bg: 'rgba(148,163,184,0.10)' },
|
||||||
|
trim: { label: '축소', color: '#f59e0b', bg: 'rgba(245,158,11,0.12)' },
|
||||||
|
sell: { label: '매도', color: '#ef4444', bg: 'rgba(239,68,68,0.12)' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const SEV_COLOR = { high: '#ef4444', med: '#f59e0b', low: '#94a3b8' };
|
||||||
|
|
||||||
|
/* ── helpers ──────────────────────────────────────────────────────── */
|
||||||
|
const fmtRate = (v) => (v != null ? `${v >= 0 ? '+' : ''}${v.toFixed(1)}%` : '—');
|
||||||
|
const fmtPct = (v) => (v != null ? `${(v * 100).toFixed(0)}%` : '—');
|
||||||
|
|
||||||
|
/* ── sub-components ───────────────────────────────────────────────── */
|
||||||
|
const HealthBar = ({ ph }) => (
|
||||||
|
<div className="hi-health">
|
||||||
|
<span className={`hi-health__pnl ${(ph.total_pnl_rate ?? 0) >= 0 ? 'is-up' : 'is-down'}`}>
|
||||||
|
포트 손익 {fmtRate(ph.total_pnl_rate)}
|
||||||
|
</span>
|
||||||
|
<span className="hi-health__sep">·</span>
|
||||||
|
<span>종목 {ph.positions ?? 0}</span>
|
||||||
|
<span className="hi-health__sep">·</span>
|
||||||
|
<span>최대비중 {fmtPct(ph.max_weight)}</span>
|
||||||
|
<span className="hi-health__sep">·</span>
|
||||||
|
<span>현금 {fmtPct(ph.cash_ratio)}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const HoldingCard = ({ h }) => {
|
||||||
|
const cfg = ACTION_MAP[h.action] ?? { label: h.action, color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' };
|
||||||
|
const issues = (h.issues || []).slice(0, 3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hi-card">
|
||||||
|
<div className="hi-card__head">
|
||||||
|
<span
|
||||||
|
className="hi-action-badge"
|
||||||
|
style={{ color: cfg.color, background: cfg.bg }}
|
||||||
|
>
|
||||||
|
{cfg.label}
|
||||||
|
</span>
|
||||||
|
<strong className="hi-card__name">{h.name || h.ticker}</strong>
|
||||||
|
<span className="hi-card__ticker">{h.ticker}</span>
|
||||||
|
{h.close != null && (
|
||||||
|
<span className="hi-card__close">{h.close.toLocaleString()}원</span>
|
||||||
|
)}
|
||||||
|
<span className={`hi-card__pnl ${(h.pnl_rate ?? 0) >= 0 ? 'is-up' : 'is-down'}`}>
|
||||||
|
{fmtRate(h.pnl_rate)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{h.reasons && (
|
||||||
|
<div className="hi-card__reasons">{h.reasons}</div>
|
||||||
|
)}
|
||||||
|
{h.tech_score != null && (
|
||||||
|
<div className="hi-card__score">
|
||||||
|
기술강도 <strong>{h.tech_score.toFixed(0)}</strong>
|
||||||
|
<span className="hi-score-bar" style={{ '--score': `${h.tech_score}%` }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{issues.length > 0 && (
|
||||||
|
<div className="hi-card__issues">
|
||||||
|
{issues.map((iss, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="hi-issue"
|
||||||
|
style={{ color: SEV_COLOR[iss.severity] ?? '#94a3b8' }}
|
||||||
|
>
|
||||||
|
<span className="hi-issue__dot" style={{ background: SEV_COLOR[iss.severity] ?? '#94a3b8' }} />
|
||||||
|
{iss.summary}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── main component ───────────────────────────────────────────────── */
|
||||||
|
const HoldingsIntelTab = () => {
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
setError('');
|
||||||
|
stockHoldingsIntel()
|
||||||
|
.then(setData)
|
||||||
|
.catch((err) => setError(err?.message ?? String(err)))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const ph = data?.portfolio_health ?? {};
|
||||||
|
const holdings = data?.holdings ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="stock-panel stock-panel--wide hi-panel">
|
||||||
|
<div className="stock-panel__head">
|
||||||
|
<div>
|
||||||
|
<p className="stock-panel__eyebrow">보유종목 인텔리전스</p>
|
||||||
|
<h3>보유종목 신호 분석</h3>
|
||||||
|
<p className="stock-panel__sub">
|
||||||
|
스크리너 엔진 기반 기술분석·매도룰·이슈를 보유종목에 적용합니다 (어드바이저리).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="stock-panel__actions">
|
||||||
|
{loading && <Loading type="spinner" message="" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && <p className="stock-error">{error}</p>}
|
||||||
|
|
||||||
|
{!loading && !error && !data && (
|
||||||
|
<p className="stock-empty">데이터가 없습니다.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!loading && data && (
|
||||||
|
<>
|
||||||
|
{Object.keys(ph).length > 0 && <HealthBar ph={ph} />}
|
||||||
|
|
||||||
|
{data.date && (
|
||||||
|
<p className="hi-date">분석 기준일: {data.date}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{holdings.length === 0 ? (
|
||||||
|
<div className="hi-empty">
|
||||||
|
<span className="hi-empty__icon">📊</span>
|
||||||
|
<p>아직 분석 데이터가 없습니다.</p>
|
||||||
|
<p className="hi-empty__sub">
|
||||||
|
보유종목 등록 후 EOD 계산이 완료되면 표시됩니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="hi-cards">
|
||||||
|
{holdings.map((h) => (
|
||||||
|
<HoldingCard key={h.ticker} h={h} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="hi-disclaimer">
|
||||||
|
※ 투자 판단 보조용 제안입니다. 자동매매가 아니며 최종 결정은 본인 책임입니다.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HoldingsIntelTab;
|
||||||
@@ -149,3 +149,4 @@ export const computeBrokerSummary = (items) => {
|
|||||||
export const TAB_PORTFOLIO = 'portfolio';
|
export const TAB_PORTFOLIO = 'portfolio';
|
||||||
export const TAB_REPORT = 'report';
|
export const TAB_REPORT = 'report';
|
||||||
export const TAB_ADVISOR = 'advisor';
|
export const TAB_ADVISOR = 'advisor';
|
||||||
|
export const TAB_HOLDINGS_INTEL = 'holdings_intel';
|
||||||
|
|||||||
Reference in New Issue
Block a user