<!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>