// portfolioData.jsx — 포트폴리오 관리 데이터 + 리스크 모델. Exports to window.
// 리스크 = CVaR 99% / 1일 (억원). 한국 관례: + 빨강 / − 파랑.

// ---- 종목 유니버스 ----
// vol = 개별 연변동성(%), ret = 연수익률(%), dur = 듀레이션(채권), country = 국가
const UNIV = [
  // 국내 주식
  { id:'005930', name:'삼성전자',         type:'주식', sector:'반도체',   country:'한국', ret:12.4, vol:22, beta:1.15 },
  { id:'000660', name:'SK하이닉스',       type:'주식', sector:'반도체',   country:'한국', ret:18.6, vol:31, beta:1.35 },
  { id:'373220', name:'LG에너지솔루션',   type:'주식', sector:'2차전지', country:'한국', ret:-8.3, vol:38, beta:1.30 },
  { id:'005380', name:'현대차',           type:'주식', sector:'자동차',   country:'한국', ret:9.8,  vol:24, beta:1.05 },
  { id:'105560', name:'KB금융',           type:'주식', sector:'금융',     country:'한국', ret:18.6, vol:19, beta:0.92 },
  { id:'035420', name:'NAVER',            type:'주식', sector:'인터넷',   country:'한국', ret:-4.5, vol:32, beta:1.18 },
  { id:'207940', name:'삼성바이오로직스', type:'주식', sector:'바이오',   country:'한국', ret:6.2,  vol:28, beta:0.88 },
  { id:'005490', name:'POSCO홀딩스',      type:'주식', sector:'소재',     country:'한국', ret:-2.1, vol:33, beta:1.22 },
  { id:'010120', name:'LS ELECTRIC',      type:'주식', sector:'전력기기', country:'한국', ret:21.4, vol:29, beta:1.10 },
  { id:'068270', name:'셀트리온',         type:'주식', sector:'바이오',   country:'한국', ret:1.8,  vol:30, beta:0.95 },
  // 해외 주식 (fx = 환노출)
  { id:'AAPL',   name:'애플',             type:'주식', sector:'IT하드웨어', country:'미국', fx:true, ret:15.2, vol:26, beta:1.10 },
  { id:'NVDA',   name:'엔비디아',         type:'주식', sector:'반도체',     country:'미국', fx:true, ret:38.4, vol:44, beta:1.65 },
  { id:'MSFT',   name:'마이크로소프트',   type:'주식', sector:'소프트웨어', country:'미국', fx:true, ret:17.8, vol:24, beta:1.02 },
  { id:'TSM',    name:'TSMC',             type:'주식', sector:'반도체',     country:'대만', fx:true, ret:22.1, vol:33, beta:1.28 },
  { id:'7203',   name:'도요타',           type:'주식', sector:'자동차',     country:'일본', fx:true, ret:8.4,  vol:21, beta:0.85 },
  // 채권 — rating, conv(컨벡시티), call(콜옵션 내재, 베가 노출)
  { id:'KR103502', name:'국고03375-3206', type:'채권', sector:'국채',       country:'한국',   rating:'국가', ret:3.4, vol:5.0, dur:7.5, credit:0, conv:0.66 },
  { id:'KR4900',   name:'통안02000-2706', type:'채권', sector:'국채',       country:'한국',   rating:'국가', ret:3.1, vol:2.6, dur:1.9, credit:0, conv:0.05 },
  { id:'KR350706', name:'도로공사752',    type:'채권', sector:'공사·공단채', country:'한국',   rating:'AAA', ret:3.6, vol:4.1, dur:3.2, credit:1, conv:0.13 },
  { id:'KR3546',   name:'한국전력채712',  type:'채권', sector:'공사·공단채', country:'한국',   rating:'AAA', ret:3.8, vol:4.6, dur:4.1, credit:1, conv:0.20 },
  { id:'KR8085',   name:'신한은행4-12',   type:'채권', sector:'금융채',     country:'한국',   rating:'AA+', ret:3.9, vol:3.4, dur:2.6, credit:1, conv:0.09, call:true, vega:0.8 },
  { id:'KR2253',   name:'회사채AA- 5년',  type:'채권', sector:'회사채',     country:'한국',   rating:'AA-', ret:4.2, vol:6.2, dur:3.5, credit:2, conv:0.15 },
  { id:'KR6000',   name:'IBK캐피탈27-3',  type:'채권', sector:'금융채',     country:'한국',   rating:'AA-', ret:4.4, vol:4.8, dur:2.2, credit:2, conv:0.07, call:true, vega:0.5 },
  { id:'US91282',  name:'미국채 10년',    type:'채권', sector:'해외채권',   country:'미국',   rating:'국가', ret:4.3, vol:8.0, dur:8.5, credit:0, conv:0.84, fx:true },
  { id:'US037833', name:'AAPL 3.85 43',   type:'채권', sector:'해외채권',   country:'미국',   rating:'AA+', ret:4.9, vol:10.5, dur:11.2, credit:1, conv:1.42, fx:true },
  { id:'FR0013',   name:'프랑스국채 OAT31', type:'채권', sector:'해외채권', country:'프랑스', rating:'AA',  ret:3.2, vol:6.5, dur:5.8, credit:0, conv:0.41, fx:true },
  { id:'XS2364',   name:'QNBK 1.71 26',   type:'채권', sector:'해외채권',   country:'카타르', rating:'A+',  ret:4.6, vol:9.2, dur:4.0, credit:2, conv:0.19, fx:true },
];
const UMAP = {}; UNIV.forEach(u=>UMAP[u.id]=u);

const SECTOR_HUE = {
  '반도체':'#0A4FA3','2차전지':'#6D5BD0','자동차':'#1F8A8A','금융':'#0E7C5A','인터넷':'#00A0E0',
  '바이오':'#C76A1F','소재':'#8A6D3B','전력기기':'#3E63B8','IT하드웨어':'#4C7BD9','소프트웨어':'#B0476B',
  '국채':'#0A4FA3','공사·공단채':'#00A0E0','금융채':'#0E7C5A','해외채권':'#E0922A','회사채':'#6D5BD0','특수채':'#1F8A8A'
};
const COUNTRY_HUE = { '한국':'#0A4FA3','미국':'#00A0E0','대만':'#6D5BD0','일본':'#0E7C5A','프랑스':'#C76A1F','카타르':'#E0922A' };

// ---- 리스크 모델 ----
const Z = 2.33;                       // 99% z-score
const DAILY = Z/Math.sqrt(252);       // 일간 CVaR 환산 ≈ 0.1468
// 상관: 동일자산군 0.45, 주식-채권 0.15, 자기 1
function corr(a,b){ if(a.id===b.id) return 1; if(a.type===b.type) return a.type==='주식'?0.50:0.42; return 0.15; }
// 비중(%) 배열 -> 연환산 포트 변동성(%)
function portVol(holds, volOverride){
  let s=0;
  for(const h of holds){ for(const g of holds){
    const a=UMAP[h.id], b=UMAP[g.id];
    const va=(volOverride&&volOverride[h.id])||a.vol, vb=(volOverride&&volOverride[g.id])||b.vol;
    s += (h.w/100)*(g.w/100)*corr(a,b)*va*vb;
  }}
  return Math.sqrt(Math.max(0,s));
}
function portRet(holds){ return holds.reduce((s,h)=>s+(h.w/100)*UMAP[h.id].ret,0); }
function riskAmt(value, volPct){ return value*(volPct/100)*DAILY; } // 억
// 정규화: 비중 합 100
function normalize(holds){ const t=holds.reduce((s,h)=>s+h.w,0)||1; return holds.map(h=>({...h, w:+(h.w/100*100*(100/t)).toFixed(2)})); }

// 한 포트폴리오의 종합 지표 계산
function summarize(p){
  const holds = p.holds;
  const vol = portVol(holds);
  const risk = riskAmt(p.value, vol);
  const ret = portRet(holds);
  const remain = p.limit - risk;
  return { vol, risk, ret, remain, util:risk/p.limit*100, value:p.value, limit:p.limit };
}

// ---- 위기 시나리오 ----
// eq: 주식 충격(%), rateBp: 금리변동(bp,+상승), creditBp: 신용스프레드(bp), fxPct: 원화환산(+원약세), volX: 변동성 배수
const SCENARIOS = [
  { id:'covid', name:'COVID-19 팬데믹', date:'2020.03', icon:'alert', desc:'급격한 위험회피·유동성 경색', eq:-34, rateBp:-110, creditBp:260, fxPct:8, volX:2.6 },
  { id:'ratehike', name:'美 금리 급등', date:'2022.09', icon:'trendUp', desc:'연준 공격적 긴축·실질금리 상승', eq:-18, rateBp:215, creditBp:120, fxPct:7, volX:1.8 },
  { id:'gfc', name:'글로벌 금융위기', date:'2008.10', icon:'alert', desc:'신용경색·자산 동반 급락', eq:-45, rateBp:-90, creditBp:420, fxPct:14, volX:3.0 },
  { id:'infl', name:'인플레이션 쇼크', date:'2021.11', icon:'percent', desc:'물가 급등·금리 상방 압력', eq:-12, rateBp:150, creditBp:70, fxPct:4, volX:1.5 },
  { id:'fx', name:'원화 급락 (환위기)', date:'1997.12', icon:'won', desc:'원화 급락·해외자산 환산 확대', eq:-15, rateBp:120, creditBp:140, fxPct:22, volX:1.9 },
  { id:'geo', name:'지정학 리스크 격화', date:'2022.02', icon:'flag', desc:'에너지·공급망 충격', eq:-14, rateBp:40, creditBp:80, fxPct:6, volX:1.6 },
];

// 시나리오 적용 -> 종목별 손익% + 포트 결과
function simulate(p, sc){
  const holds = p.holds;
  const volOv = {};
  const perHold = holds.map(h=>{
    const u = UMAP[h.id];
    let pnl;
    if(u.type==='주식'){ pnl = sc.eq * (u.beta||1); if(u.fx) pnl += sc.fxPct; }
    else {
      const spread = (u.credit||0)*0.5*sc.creditBp; // 신용등급 민감도
      pnl = -(u.dur||3) * (sc.rateBp + spread)/100; // 가격% = -듀레이션 × Δ수익률(%)
      if(u.fx) pnl += sc.fxPct; // 환산 효과
    }
    volOv[h.id] = u.vol * sc.volX;
    return { id:h.id, name:u.name, type:u.type, sector:u.sector, w:h.w, pnl:+pnl.toFixed(1) };
  });
  const portPnl = perHold.reduce((s,x)=>s+(x.w/100)*x.pnl,0);
  const newValue = p.value*(1+portPnl/100);
  const newVol = portVol(holds, volOv);
  const newRisk = riskAmt(newValue, newVol);
  return { perHold, portPnl:+portPnl.toFixed(2), lossAmt:Math.round(p.value*portPnl/100), newValue:Math.round(newValue), newVol, newRisk:Math.round(newRisk), util:newRisk/p.limit*100 };
}

// ---- AI 최적화 (리스크 관점 비중 추천) ----
// 위험조정 점수 ∝ (기대수익+α)/분산 으로 틸트 후 현재 비중과 블렌드 → 리스크 절감
function optimize(p){
  const holds = p.holds;
  const score = holds.map(h=>{ const u=UMAP[h.id]; return Math.max(0.05,(u.ret+4)/u.vol); });
  const ss = score.reduce((a,b)=>a+b,0);
  const target = holds.map((h,i)=> score[i]/ss*100);
  // 50% 최적 + 50% 현재 (현실적 리밸런싱)
  let blended = holds.map((h,i)=> 0.5*target[i] + 0.5*h.w);
  const t = blended.reduce((a,b)=>a+b,0); blended = blended.map(w=>w/t*100);
  const newHolds = holds.map((h,i)=>({ id:h.id, w:+blended[i].toFixed(2) }));
  const before = summarize(p);
  const after = summarize({ ...p, holds:newHolds });
  return { newHolds, before, after,
    deltas: holds.map((h,i)=>({ id:h.id, name:UMAP[h.id].name, type:UMAP[h.id].type, from:h.w, to:+newHolds[i].w.toFixed(1), diff:+(newHolds[i].w-h.w).toFixed(1) })) };
}

// ---- 조직 / 담당자 / 포트폴리오 ----
// 트레이딩 데스크는 주식/채권 담당이 분리되어 있다. asset = 담당 자산군.
// holds 비중은 자동 정규화. limit/value 단위 억.
function P(holds, value, limit){ return { holds: normalize(holds.map(([id,w])=>({id,w}))), value, limit }; }
const PEOPLE = [
  // ── 주식 데스크 ──
  { emp:'20180417', name:'김진호', asset:'주식', dept:'자금운용부', team:'트레이딩데스크 1팀', title:'팀장',
    pf:P([['005930',22],['000660',16],['105560',12],['005380',12],['NVDA',14],['AAPL',12],['TSM',12]], 3240, 140) },
  { emp:'20191103', name:'박서연', asset:'주식', dept:'자금운용부', team:'트레이딩데스크 1팀', title:'책임',
    pf:P([['005930',18],['373220',12],['035420',12],['207940',12],['068270',10],['MSFT',18],['7203',18]], 2620, 115) },
  { emp:'20200622', name:'이도현', asset:'주식', dept:'자금운용부', team:'트레이딩데스크 2팀', title:'책임',
    pf:P([['000660',18],['010120',14],['005490',12],['005380',12],['NVDA',16],['TSM',16],['MSFT',12]], 2980, 125) },
  { emp:'20190718', name:'오세훈', asset:'주식', dept:'투자금융부', team:'주식운용팀', title:'책임',
    pf:P([['005930',16],['000660',12],['005380',12],['105560',12],['010120',10],['207940',10],['035420',10],['AAPL',18]], 3270, 130) },
  // ── 채권 데스크 ──
  { emp:'20170815', name:'최민준', asset:'채권', dept:'자금운용부', team:'트레이딩데스크 2팀', title:'팀장',
    pf:P([['KR103502',20],['KR350706',14],['US91282',18],['KR8085',14],['FR0013',12],['KR2253',12],['KR4900',10]], 6130, 110) },
  { emp:'20210309', name:'정유진', asset:'채권', dept:'투자금융부', team:'채권운용팀', title:'책임',
    pf:P([['KR103502',22],['KR4900',16],['KR350706',14],['KR3546',13],['US91282',15],['XS2364',12],['KR2253',8]], 7480, 120) },
  { emp:'20160527', name:'한지우', asset:'채권', dept:'투자금융부', team:'채권운용팀', title:'수석',
    pf:P([['KR103502',20],['KR350706',16],['KR3546',14],['KR8085',12],['US91282',16],['XS2364',12],['KR4900',10]], 8920, 150) },
  { emp:'20151001', name:'강하늘', asset:'채권', dept:'프로젝트금융부', team:'대체투자팀', title:'팀장',
    pf:P([['US91282',22],['XS2364',18],['KR2253',16],['KR3546',12],['US037833',16],['FR0013',16]], 5560, 102) },
  { emp:'20220214', name:'윤채원', asset:'채권', dept:'프로젝트금융부', team:'대체투자팀', title:'주임',
    pf:P([['US91282',24],['XS2364',16],['KR2253',14],['KR350706',12],['US037833',12],['KR6000',12],['KR4900',10]], 2980, 58) },
];
const PMAP = {}; PEOPLE.forEach(p=>PMAP[p.emp]=p);
const peopleOf = (asset)=> asset ? PEOPLE.filter(p=>p.asset===asset) : PEOPLE;
const deptsOf = (asset)=> [...new Set(peopleOf(asset).map(p=>p.dept))];

// 조직별 집계 포트폴리오 (부서 단위 합산, asset 필터 가능)
function deptPortfolio(deptName, asset){
  const members = PEOPLE.filter(p=>p.dept===deptName && (!asset || p.asset===asset));
  const value = members.reduce((s,m)=>s+m.pf.value,0);
  const limit = members.reduce((s,m)=>s+m.pf.limit,0);
  const agg = {};
  members.forEach(m=>m.pf.holds.forEach(h=>{ agg[h.id]=(agg[h.id]||0)+h.w*m.pf.value; }));
  const tot = Object.values(agg).reduce((a,b)=>a+b,0)||1;
  const holds = Object.entries(agg).map(([id,v])=>({id, w:+(v/tot*100).toFixed(2)})).sort((a,b)=>b.w-a.w);
  return { holds, value, limit };
}
const DEPT_LIST = ['자금운용부','투자금융부','프로젝트금융부'];

Object.assign(window, {
  UNIV, UMAP, SECTOR_HUE, COUNTRY_HUE, SCENARIOS, PEOPLE, PMAP, DEPT_LIST, corr, DAILY,
  peopleOf, deptsOf,
  portVol, portRet, riskAmt, normalize, summarize, simulate, optimize, deptPortfolio
});
