// 세계 주요국 증시지수 도메인 타입 — Backend Application/Features/Api/Stocks/GetWorldIndices/Response.cs 대응 // 직렬화 주의: 필드명 camelCase. flucRateBp = 등락률 basis point(%×100). enum 없음(전부 primitive). // ── GET /api/market/world-indices ── export interface WorldIndexRow { countryCode: string; // ISO2 대문자 — EXCHANGE_GEO 좌표맵 키 name: string; // 지수명 (예: "코스피", "S&P 500") exchangeName: string; close: number; changeVal: number; flucRateBp: number; // %×100 (예: -70 = -0.70%) tradeDate: string; // yyyy-MM-dd } export interface WorldIndicesResponse { list: WorldIndexRow[]; } // ── 지역(흥국 지역 필터 대응) ── // 'major'(주요국)는 지역 교차 큐레이션 탭 — 지리적 region 과 별개로 major 플래그로 표기. export type MarketRegion = 'asia'|'europe'|'america'|'mideast'; export const REGION_LABELS: Record<'major'|MarketRegion, string> = { major: '주요국', asia: '아시아', america: '아메리카', europe: '유럽', mideast: '중동·아프리카' }; // 지역 탭 노출 순서 (major 우선, 이후 데이터 있는 지역만 렌더) export const REGION_ORDER: ('major'|MarketRegion)[] = ['major', 'asia', 'america', 'europe', 'mideast']; // ── 국가 → 지도 좌표(위경도) + 표시명. 나라당 1개(대표 지수) 기준 ── // 프론트가 국가 코드로 세계지도 핀 위치를 정한다(백엔드는 좌표를 보내지 않음 — 응답 경량화). export interface ExchangeGeo { lat: number; lng: number; label: string; // 국가 표시명 (한글) region: MarketRegion; // 지리적 지역 탭 major?: boolean; // 주요국 탭 포함 여부 } export const EXCHANGE_GEO: Record = { US: { lat: 40.7, lng: -74.0, label: '미국', region: 'america', major: true }, KR: { lat: 37.5, lng: 127.0, label: '한국', region: 'asia', major: true }, JP: { lat: 35.7, lng: 139.7, label: '일본', region: 'asia', major: true }, CN: { lat: 31.2, lng: 121.5, label: '중국', region: 'asia', major: true }, HK: { lat: 22.3, lng: 114.2, label: '홍콩', region: 'asia' }, TW: { lat: 25.0, lng: 121.6, label: '대만', region: 'asia' }, SG: { lat: 1.35, lng: 103.8, label: '싱가포르', region: 'asia' }, IN: { lat: 19.1, lng: 72.9, label: '인도', region: 'asia' }, AU: { lat: -33.9, lng: 151.2, label: '호주', region: 'asia' }, GB: { lat: 51.5, lng: -0.1, label: '영국', region: 'europe', major: true }, DE: { lat: 50.1, lng: 8.7, label: '독일', region: 'europe', major: true }, FR: { lat: 48.9, lng: 2.3, label: '프랑스', region: 'europe' }, CA: { lat: 43.7, lng: -79.4, label: '캐나다', region: 'america' }, BR: { lat: -23.5, lng: -46.6, label: '브라질', region: 'america' }, // 이하 확장(백엔드 WorldIndex Items 구성 시 활성) — 지역 탭 커버리지 대비 ID: { lat: -6.2, lng: 106.85, label: '인도네시아', region: 'asia' }, TH: { lat: 13.75, lng: 100.52, label: '태국', region: 'asia' }, MY: { lat: 3.14, lng: 101.69, label: '말레이시아', region: 'asia' }, VN: { lat: 10.82, lng: 106.63, label: '베트남', region: 'asia' }, IT: { lat: 45.46, lng: 9.19, label: '이탈리아', region: 'europe' }, ES: { lat: 40.42, lng: -3.70, label: '스페인', region: 'europe' }, CH: { lat: 47.37, lng: 8.54, label: '스위스', region: 'europe' }, NL: { lat: 52.37, lng: 4.90, label: '네덜란드', region: 'europe' }, SE: { lat: 59.33, lng: 18.07, label: '스웨덴', region: 'europe' }, MX: { lat: 19.43, lng: -99.13, label: '멕시코', region: 'america' }, SA: { lat: 24.71, lng: 46.68, label: '사우디', region: 'mideast' }, ZA: { lat: -26.20, lng: 28.05, label: '남아공', region: 'mideast' }, AE: { lat: 25.20, lng: 55.27, label: 'UAE', region: 'mideast' } }; // ── 국가 → TradingView 대표지수 심볼 (드릴다운 차트). best-effort 매핑 ── // 없는 국가(예: CA)는 차트 버튼이 지도 하이라이트로 폴백. 심볼은 운영에서 조정 가능. export const TV_SYMBOL: Record = { US: 'SPX', KR: 'KRX:KOSPI', JP: 'TVC:NI225', CN: 'SSE:000001', HK: 'TVC:HSI', TW: 'TVC:TWII', SG: 'TVC:STI', IN: 'BSE:SENSEX', GB: 'TVC:UKX', DE: 'XETR:DAX', FR: 'EURONEXT:PX1', AU: 'ASX:XJO', BR: 'BMFBOVESPA:IBOV' }; // ── 등락 상태 ── export type MoveDir = 'up' | 'down' | 'flat'; export function moveDir(flucRateBp: number): MoveDir { if (flucRateBp > 0) { return 'up'; } if (flucRateBp < 0) { return 'down'; } return 'flat'; } // 등락률 Bp → "+1.45%" / "-0.70%" / "0.00%" export function formatFlucRate(flucRateBp: number): string { const pct = flucRateBp / 100; const sign = flucRateBp > 0 ? '+' : ''; return `${sign}${pct.toFixed(2)}%`; } // 종가 표시 — 천단위 콤마 + 소수 2자리 export function formatIndexClose(close: number): string { return close.toLocaleString('ko-KR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }