<!DOCTYPE html>

<html lang="ko">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">

<meta name="apple-mobile-web-app-capable" content="yes">

<title>주식 노트</title>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>

<style>

*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}

body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#f9fafb;color:#111827;min-height:100vh}

button{cursor:pointer;font-family:inherit;border:none}input,select{font-family:inherit;font-size:14px}

.hd{background:#fff;padding:16px 20px 0;position:sticky;top:0;z-index:10;border-bottom:1px solid #f3f4f6}

.hd-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}

.hd h1{font-size:20px;font-weight:800;display:flex;align-items:center;gap:8px;margin:0}

.rst-btn{font-size:10px;color:#9ca3af;background:none;border:1px solid #e5e7eb;border-radius:6px;padding:3px 8px}

.tabs{display:flex;background:#f3f4f6;border-radius:10px;padding:3px}

.tabs button{flex:1;padding:10px 6px;font-size:13px;font-weight:500;color:#6b7280;background:none;border-radius:8px;transition:all .2s}

.tabs button.on{background:#fff;color:#111;font-weight:700;box-shadow:0 1px 3px rgba(0,0,0,.08)}

.wrap{max-width:960px;margin:0 auto;padding:12px 16px 80px}

.cards{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:20px}

.card{border-radius:14px;padding:14px}.card .lb{font-size:11px;color:#6b7280;margin-bottom:3px}.card .vl{font-size:16px;font-weight:800}

.c1{background:linear-gradient(135deg,#eff6ff,#dbeafe)}.c1 .vl{color:#2563eb}

.c2{background:linear-gradient(135deg,#f0fdf4,#dcfce7)}.c2 .vl{color:#16a34a}

.c3{background:linear-gradient(135deg,#fef2f2,#fecaca)}.c3 .vl{color:#dc2626}

.c4{background:linear-gradient(135deg,#faf5ff,#e9d5ff)}.c4 .vl{color:#7c3aed}

.sec{background:#fff;border-radius:14px;padding:16px;margin-bottom:16px;border:1px solid #f3f4f6}

.sec h3{font-size:15px;font-weight:700;margin-bottom:12px}

.form-row{display:flex;gap:8px;flex-wrap:wrap;align-items:end}

.fg{flex:1;min-width:80px}.fg label{display:block;font-size:11px;color:#6b7280;margin-bottom:3px}

.fg input{width:100%;padding:8px 10px;border:1px solid #d1d5db;border-radius:8px;font-size:13px}

.btn-add{padding:8px 18px;background:#2563eb;color:#fff;border-radius:8px;font-size:13px;font-weight:700;white-space:nowrap;height:38px}

.tbl{width:100%;border-collapse:collapse;font-size:12px}

.tbl th{padding:8px 6px;text-align:right;border-bottom:2px solid #e5e7eb;font-weight:700;font-size:11px;color:#6b7280}

.tbl th:first-child{text-align:left}.tbl td{padding:7px 6px;border-bottom:1px solid #f3f4f6;text-align:right}

.tbl td:first-child{text-align:left;font-weight:600}.tbl tr:nth-child(even){background:#f9fafb}

.tbl-wrap{overflow-x:auto;border:1px solid #e5e7eb;border-radius:14px}

.chart-wrap{position:relative;width:100%;max-height:300px}

.badge-row{display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap}

.badge{background:#fff;border-radius:8px;padding:5px 10px;border:1px solid #e5e7eb;display:flex;align-items:center;gap:5px;font-size:12px}

.badge .bv{font-weight:700;font-size:13px}.badge .dot{width:7px;height:7px;border-radius:50%;display:inline-block}

.red{color:#dc2626}.blue{color:#2563eb}

.eval-form{display:flex;gap:8px;flex-wrap:wrap;align-items:end;margin-bottom:16px;background:#f0f9ff;padding:14px;border-radius:12px}

</style>

</head>

<body>

<div class="hd">

<div class="hd-top">

<h1>📈 주식 노트</h1>

<button class="rst-btn" onclick="resetData()">데이터 초기화</button>

</div>

<div class="tabs" id="tabs"></div>

</div>

<div class="wrap" id="app"></div>

<script>

var tab=0,tabNames=["대시보드","투자기록"];

var records=[],evals=[];

var myCharts={},DATA_VER="v20";


function fmt(n){return Math.round(n).toLocaleString("ko-KR")}

function fmtP(n){return(n>=0?"+":"")+n.toFixed(1)+"%"}

function pNum(v){if(!v)return 0;return Number(String(v).replace(/,/g,""))||0}

function sGet(k){try{var v=localStorage.getItem("sn_"+k);return v?JSON.parse(v):null}catch(e){return null}}

function sSet(k,v){try{localStorage.setItem("sn_"+k,JSON.stringify(v))}catch(e){}}


var INIT_DATA=[

["2019-02-23",100000,0,""],["2019-08-08",100000,0,""],["2019-09-02",200000,0,""],["2019-09-10",200000,0,""],

["2019-10-28",100000,0,""],["2019-11-07",100000,0,""],["2019-12-13",100000,0,""],["2019-12-13",5000,0,""],

["2020-02-07",100000,0,""],["2020-02-27",140000,0,""],["2020-03-02",1000000,0,""],["2020-03-04",1000000,0,""],

["2020-03-09",400000,0,""],["2020-05-11",100000,0,""],["2020-05-12",1000000,0,""],["2020-05-26",1000000,0,""],

["2020-06-01",1000000,0,""],["2020-07-21",520000,0,""],["2020-07-23",100000,0,""],["2020-07-23",150000,0,""],

["2020-07-27",1750000,0,""],["2020-07-27",50000,0,""],["2020-07-27",10000,0,""],["2020-07-28",100000,0,""],

["2020-08-24",2300000,0,""],["2020-08-25",200000,0,""],["2020-08-25",50000,0,""],["2020-08-25",60000,0,""],

["2020-12-14",2000000,0,""],["2021-01-07",3500000,0,""],["2021-01-07",100000,0,""],["2021-01-07",100000,0,""],

["2021-01-07",100000,0,""],["2021-01-08",1000000,0,""],["2021-01-08",1000000,0,""],["2021-01-12",1000000,0,""],

["2021-01-12",50000,0,""],["2021-01-12",500000,0,""],["2021-01-12",300000,0,""],["2021-01-12",50000,0,""],

["2021-01-12",500000,0,""],["2021-01-12",400000,0,""],["2021-01-14",300000,0,""],["2021-01-14",467444,0,""],

["2021-01-15",1000000,0,""],["2021-01-19",5000000,0,""],["2021-01-22",1000000,0,""],["2021-01-28",500000,0,""],

["2021-02-01",1000000,0,""],["2021-02-19",150000,0,""],["2021-02-23",2300000,0,""],["2021-03-06",1834803,0,""],

["2021-03-11",140000,0,""],["2021-03-17",500000,0,""],["2021-03-18",400000,0,""],["2021-03-19",1000000,0,""],

["2021-03-22",50000,0,""],["2021-03-24",400000,0,""],["2021-03-26",200000,0,""],["2021-04-01",1000000,0,""],

["2021-04-10",1500000,0,""],["2021-04-12",500000,0,""],["2021-04-16",1000000,0,""],["2021-05-05",500000,0,""],

["2021-05-21",700000,0,""],["2021-05-22",300000,0,""],["2021-06-14",250000,0,""],["2021-06-22",130000,0,""],

["2021-06-24",150000,0,""],["2021-06-29",600000,0,""],["2021-07-06",1000000,0,""],["2021-07-07",140000,0,""],

["2021-07-09",310000,0,""],["2021-07-17",60000,0,""],["2021-09-16",200000,0,""],["2021-10-04",300000,0,""],

["2021-10-22",0,1250000,""],["2021-11-07",900000,0,""],["2021-12-22",195000,0,""],

["2022-01-04",1000000,0,""],["2022-01-05",2000000,0,""],["2022-01-14",1100000,0,""],["2022-02-14",1000000,0,""],

["2022-04-11",2000000,0,""],["2022-04-13",1500000,0,""],["2022-04-22",2300000,0,""],["2022-04-27",1000000,0,""],

["2022-04-28",1000000,0,""],["2022-05-31",3200000,0,""],["2022-06-10",500000,0,""],["2022-06-15",500000,0,""],

["2022-06-24",2000000,0,""],["2022-07-26",1000000,0,""],["2022-08-29",0,1200000,""],["2022-09-01",0,1149752,""],

["2022-09-29",100000,0,""],["2022-10-12",1000000,0,""],["2023-02-07",0,686860,""],["2023-02-27",0,1328523,""],

["2023-04-26",200000,0,""],["2023-10-18",2000000,0,""],["2023-10-19",2000000,0,""],["2023-10-20",1000000,0,""],

["2024-04-01",0,120888,""],["2024-05-09",0,3400000,"반클리프 이어링"],["2024-09-04",200000,0,""],

["2024-09-06",1000000,0,""],["2024-09-11",1000000,0,""],["2024-12-03",450000,0,""],

["2024-12-24",0,6200000,"청미 증여"],["2025-02-07",500000,0,""],["2025-02-18",200000,0,""],

["2025-02-26",500000,0,""],["2025-02-28",500000,0,""],["2025-07-17",1000000,0,""],["2025-08-24",1000000,0,""],

["2025-08-25",200000,0,""],["2025-08-30",200000,0,""],["2025-09-16",0,2940000,"청미 증여, 엔비디아"],

["2025-10-30",200000,0,""],["2025-10-31",100000,0,""],["2025-11-25",1000000,1800000,"비트코인, 주식 출금"],

["2025-12-16",400000,0,""],["2026-01-05",1050000,0,""],["2026-01-13",10000000,0,"비트코인"],

["2026-01-14",5000000,0,"비트코인"],["2026-01-14",2000000,0,""],["2026-01-15",3000000,0,""],

["2026-01-20",10000000,0,""],["2026-01-21",10000000,0,"비트코인"],["2026-02-05",500000,0,"비트코인"],

["2026-02-13",100000,0,""],["2026-02-19",1000000,0,""],["2026-05-14",0,1839936,"청미 선물 코히어런트 3주"]

];


var INIT_EVALS=[

{date:"2024-10-24",val:128103000},{date:"2024-11-08",val:136220000},{date:"2024-11-09",val:142083000},

{date:"2024-11-12",val:148564784},{date:"2024-12-03",val:149956456},{date:"2024-12-05",val:152559288},

{date:"2024-12-07",val:160883711},{date:"2024-12-12",val:171916969},{date:"2024-12-18",val:177568501},

{date:"2024-12-20",val:164673637},{date:"2024-12-24",val:168587268},{date:"2025-01-09",val:160856324},

{date:"2025-02-12",val:142400712},{date:"2025-03-01",val:131488000},{date:"2025-09-04",val:155459000},

{date:"2025-09-11",val:159312000},{date:"2025-09-13",val:172033000},{date:"2025-09-18",val:179000000},

{date:"2025-09-30",val:184404000},{date:"2025-10-30",val:198911000},{date:"2026-02-13",val:221649000}

];


function resetData(){localStorage.clear();location.reload()}


function buildRecords(){

  records=[];

  for(var i=0;i<INIT_DATA.length;i++){var d=INIT_DATA[i];records.push({id:i+1,date:d[0],deposit:d[1],withdraw:d[2],invested:0,memo:d[3]})}

  records.sort(function(a,b){return a.date.localeCompare(b.date)});

  var cD=0,cW=0;for(var j=0;j<records.length;j++){cD+=records[j].deposit;cW+=records[j].withdraw;records[j].invested=cD-cW}

}


function getInvestedAt(date){

  var inv=0;

  for(var i=0;i<records.length;i++){

    if(records[i].date<=date)inv=records[i].invested;else break;

  }

  return inv;

}


function init(){

  var sv=sGet("ver");

  if(sv!==DATA_VER){

    buildRecords();sSet("records",records);

    evals=INIT_EVALS.slice();sSet("evals",evals);

    sSet("ver",DATA_VER);

  }else{

    records=sGet("records")||[];

    evals=sGet("evals")||[];

    if(!records.length){buildRecords();sSet("records",records);sSet("ver",DATA_VER)}

    if(!evals.length){evals=INIT_EVALS.slice();sSet("evals",evals)}

  }

  renderTabs();render();

}


function save(){sSet("records",records);sSet("evals",evals)}

function renderTabs(){var h="";for(var i=0;i<2;i++)h+='<button class="'+(tab===i?"on":"")+'" onclick="tab='+i+';renderTabs();render()">'+tabNames[i]+"</button>";document.getElementById("tabs").innerHTML=h}

function render(){var el=document.getElementById("app");if(tab===0)renderDash(el);else renderHist(el)}


function renderDash(el){

  var tD=0,tW=0;for(var i=0;i<records.length;i++){tD+=records[i].deposit;tW+=records[i].withdraw}

  var tI=tD-tW;

  var lv=evals.length>0?evals[evals.length-1]:null;

  var cV=lv?lv.val:0;

  var cR=tI>0?((cV-tI)/tI*100):0;


  var h='<div class="cards" style="grid-template-columns:1fr 1fr">';

  h+='<div class="card c1"><div class="lb">총 입금액</div><div class="vl">'+fmt(tD)+'<small style="font-size:11px;font-weight:400">원</small></div></div>';

  h+='<div class="card c4"><div class="lb">총 출금액</div><div class="vl">'+fmt(tW)+'<small style="font-size:11px;font-weight:400">원</small></div></div>';

  h+='<div class="card c2" style="grid-column:span 2"><div class="lb">순 투자금액 (입금 - 출금)</div><div class="vl" style="font-size:20px">'+fmt(tI)+'<small style="font-size:11px;font-weight:400">원</small></div></div>';

  h+='<div class="card c2"><div class="lb">현재 평가금액</div><div class="vl">'+fmt(cV)+'<small style="font-size:11px;font-weight:400">원</small></div></div>';

  h+='<div class="card c3"><div class="lb">수익률</div><div class="vl '+(cR>=0?"red":"blue")+'">'+fmtP(cR)+'</div></div></div>';


  // 평가금액 입력

  h+='<div class="eval-form">';

  h+='<div class="fg"><label>날짜</label><input type="date" id="eD"></div>';

  h+='<div class="fg"><label>평가금액 (원)</label><input type="number" id="eV" placeholder="0"></div>';

  h+='<button class="btn-add" onclick="addEval()">평가 기록</button>';

  h+='</div>';


  // 투자금 vs 평가금액 추이 그래프

  if(evals.length>0){

    h+='<div class="sec"><h3>투자금 vs 평가금액 추이</h3>';

    h+='<div class="chart-wrap"><canvas id="c1"></canvas></div></div>';


    h+='<div class="sec"><h3>수익률 추이</h3>';

    h+='<div class="chart-wrap"><canvas id="c2"></canvas></div></div>';


    // 평가 기록 테이블

    h+='<div class="sec"><h3>평가 기록</h3>';

    h+='<div class="tbl-wrap"><table class="tbl"><thead><tr><th>날짜</th><th>투자금액</th><th>평가금액</th><th>수익률</th><th></th></tr></thead><tbody>';

    for(var i=evals.length-1;i>=0;i--){

      var e=evals[i],inv=getInvestedAt(e.date),r=inv>0?((e.val-inv)/inv*100):0;

      h+='<tr><td>'+e.date+'</td><td>'+fmt(inv)+'</td><td style="font-weight:700">'+fmt(e.val)+'</td>';

      h+='<td style="font-weight:700;color:'+(r>=0?"#dc2626":"#2563eb")+'">'+fmtP(r)+'</td>';

      h+='<td><button onclick="delEval('+i+')" style="background:none;border:none;color:#d1d5db;cursor:pointer;font-size:14px">✕</button></td></tr>';

    }

    h+='</tbody></table></div></div>';

  }


  el.innerHTML=h;

  setTimeout(function(){drawCharts()},50);

}


function drawCharts(){

  if(!evals.length)return;

  // 차트1: 투자금 vs 평가금액

  var labels=[],invData=[],valData=[],retData=[];

  for(var i=0;i<evals.length;i++){

    var e=evals[i],inv=getInvestedAt(e.date),r=inv>0?((e.val-inv)/inv*100):0;

    labels.push(e.date);invData.push(inv);valData.push(e.val);retData.push(parseFloat(r.toFixed(1)));

  }

  var c1=document.getElementById("c1");

  if(c1){

    if(myCharts.c1)myCharts.c1.destroy();

    myCharts.c1=new Chart(c1,{type:"line",data:{labels:labels,datasets:[

      {label:"투자금액",data:invData,borderColor:"#6b7280",borderWidth:2,pointRadius:2,tension:0.3,fill:false},

      {label:"평가금액",data:valData,borderColor:"#2563eb",borderWidth:2.5,pointRadius:3,tension:0.3,backgroundColor:"rgba(37,99,235,0.08)",fill:true}

    ]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{font:{size:11}}}},scales:{x:{ticks:{font:{size:9},maxRotation:45,maxTicksLimit:10}},y:{ticks:{font:{size:10},callback:function(v){return(v/1e6).toFixed(0)+"M"}}}}}});

  }

  // 차트2: 수익률

  var c2=document.getElementById("c2");

  if(c2){

    if(myCharts.c2)myCharts.c2.destroy();

    myCharts.c2=new Chart(c2,{type:"line",data:{labels:labels,datasets:[

      {label:"수익률",data:retData,borderColor:"#10b981",borderWidth:2,pointRadius:3,tension:0.3,backgroundColor:"rgba(16,185,129,0.08)",fill:true}

    ]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{x:{ticks:{font:{size:9},maxRotation:45,maxTicksLimit:10}},y:{ticks:{font:{size:10},callback:function(v){return v+"%"}}}}}});

  }

}


function addEval(){

  var d=document.getElementById("eD").value,v=pNum(document.getElementById("eV").value);

  if(!d||!v)return;

  // 같은 날짜면 덮어쓰기

  var found=false;

  for(var i=0;i<evals.length;i++){if(evals[i].date===d){evals[i].val=v;found=true;break}}

  if(!found)evals.push({date:d,val:v});

  evals.sort(function(a,b){return a.date.localeCompare(b.date)});

  save();render();

}


function delEval(idx){

  evals.splice(idx,1);

  save();render();

}


function renderHist(el){

  var h='<div class="sec"><div class="form-row">';

  h+='<div class="fg"><label>날짜</label><input type="date" id="hD"></div>';

  h+='<div class="fg"><label>입금액</label><input type="number" id="hI" placeholder="0"></div>';

  h+='<div class="fg"><label>출금액</label><input type="number" id="hW" placeholder="0"></div>';

  h+='<div class="fg"><label>메모</label><input type="text" id="hM" placeholder="선택"></div>';

  h+='<button class="btn-add" onclick="addHist()">+ 추가</button></div></div>';

  h+='<div class="tbl-wrap"><table class="tbl"><thead><tr><th>날짜</th><th>입금</th><th>출금</th><th>누적투자금액</th><th>메모</th></tr></thead><tbody>';

  for(var i=records.length-1;i>=0;i--){var r=records[i];h+='<tr><td>'+r.date+'</td><td style="color:#2563eb">'+(r.deposit>0?fmt(r.deposit):"")+'</td><td style="color:#7c3aed">'+(r.withdraw>0?fmt(r.withdraw):"")+'</td><td>'+(r.invested>0?fmt(r.invested):"")+'</td><td style="text-align:left;font-weight:400;font-size:11px;color:#6b7280">'+(r.memo||"")+'</td></tr>'}

  h+='</tbody></table></div><div style="font-size:11px;color:#9ca3af;margin-top:6px;text-align:right">총 '+records.length+'건</div>';

  el.innerHTML=h;

}


function addHist(){

  var d=document.getElementById("hD").value,dep=pNum(document.getElementById("hI").value),wit=pNum(document.getElementById("hW").value),m=document.getElementById("hM").value;

  if(!d)return;records.push({id:Date.now(),date:d,deposit:dep,withdraw:wit,invested:0,memo:m});

  records.sort(function(a,b){return a.date.localeCompare(b.date)});

  var cD=0,cW=0;for(var k=0;k<records.length;k++){cD+=records[k].deposit;cW+=records[k].withdraw;records[k].invested=cD-cW}

  save();render();

}


init();

</script>

</body>

</html>