{"spec_id":"drawdown-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// drawdown-basic: Drawdown Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-08-24\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Fixed-seed LCG + Box-Muller for reproducible pseudo-normal daily returns.\nfunction lcg(seed) {\n  let state = seed >>> 0;\n  return function () {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(20260111);\nfunction gaussian() {\n  const u1 = rand() || 1e-9;\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// Trading dates: ~3 years of weekdays starting 2021-01-04.\nconst dates = [];\nconst cursor = new Date(Date.UTC(2021, 0, 4));\nwhile (dates.length < 756) {\n  const weekday = cursor.getUTCDay();\n  if (weekday !== 0 && weekday !== 6) dates.push(new Date(cursor.getTime()));\n  cursor.setUTCDate(cursor.getUTCDate() + 1);\n}\n\n// Simulated NAV: uptrend, then a bear-market decline, then a recovery leg.\nconst nav = [100];\nfor (let i = 1; i < dates.length; i++) {\n  let mu, sigma;\n  if (i < 350) {\n    mu = 0.0009;\n    sigma = 0.0085;\n  } else if (i < 500) {\n    mu = -0.0022;\n    sigma = 0.016;\n  } else {\n    mu = 0.0012;\n    sigma = 0.01;\n  }\n  nav.push(nav[i - 1] * (1 + mu + sigma * gaussian()));\n}\n\n// Drawdown = decline from the running maximum NAV, as a percentage.\nconst runningMax = [];\nlet peak = -Infinity;\nfor (let i = 0; i < nav.length; i++) {\n  peak = Math.max(peak, nav[i]);\n  runningMax.push(peak);\n}\nconst drawdown = nav.map((v, i) => ((v - runningMax[i]) / runningMax[i]) * 100);\n\nlet maxDdIdx = 0;\nfor (let i = 1; i < drawdown.length; i++) {\n  if (drawdown[i] < drawdown[maxDdIdx]) maxDdIdx = i;\n}\nlet peakIdx = maxDdIdx;\nfor (let i = maxDdIdx; i >= 0; i--) {\n  if (drawdown[i] === 0) {\n    peakIdx = i;\n    break;\n  }\n}\nlet recoveryIdx = null;\nfor (let i = maxDdIdx + 1; i < drawdown.length; i++) {\n  if (drawdown[i] >= -0.001) {\n    recoveryIdx = i;\n    break;\n  }\n}\n\nconst navSeriesData = dates.map((d, i) => [d.getTime(), Number(nav[i].toFixed(2))]);\nconst drawdownSeriesData = dates.map((d, i) => [d.getTime(), Number(drawdown[i].toFixed(2))]);\n\nconst maxDdPct = drawdown[maxDdIdx].toFixed(1);\nconst declineDays = maxDdIdx - peakIdx;\nconst statsText =\n  recoveryIdx !== null\n    ? `Max Drawdown: ${maxDdPct}% · Decline: ${declineDays}d · Recovery: ${recoveryIdx - maxDdIdx}d`\n    : `Max Drawdown: ${maxDdPct}% · Decline: ${declineDays}d · Recovery: ongoing`;\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nconst markPointData = [\n  {\n    name: \"Max Drawdown\",\n    coord: [dates[maxDdIdx].getTime(), drawdown[maxDdIdx]],\n    symbol: \"pin\",\n    symbolSize: 52,\n    itemStyle: { color: t.palette[4] },\n    label: { color: \"#FFFFFF\", fontSize: 13, fontWeight: 600, formatter: () => `${maxDdPct}%` },\n  },\n];\nif (recoveryIdx !== null) {\n  markPointData.push({\n    name: \"Recovery\",\n    coord: [dates[recoveryIdx].getTime(), drawdown[recoveryIdx]],\n    symbol: \"circle\",\n    symbolSize: 14,\n    itemStyle: { color: t.ink },\n    label: { show: true, position: \"top\", distance: 10, color: t.ink, fontSize: 13, formatter: \"Recovery\" },\n  });\n}\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"Tech Growth Fund · drawdown-basic · javascript · echarts · anyplot.ai\",\n    subtext: statsText,\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 21, fontWeight: 600 },\n    subtextStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  legend: {\n    data: [\"Portfolio NAV\", \"Drawdown\"],\n    top: 108,\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 16 },\n    itemWidth: 22,\n    itemHeight: 12,\n  },\n  grid: { left: 110, right: 110, top: 172, bottom: 90 },\n  xAxis: {\n    type: \"time\",\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n  yAxis: [\n    {\n      type: \"value\",\n      name: \"Drawdown (%)\",\n      nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n      max: 0,\n      position: \"left\",\n      axisLabel: { color: t.inkSoft, fontSize: 14, formatter: \"{value}%\" },\n      axisLine: { show: true, lineStyle: { color: t.inkSoft } },\n      splitLine: { lineStyle: { color: t.grid } },\n    },\n    {\n      type: \"value\",\n      name: \"NAV ($)\",\n      nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n      position: \"right\",\n      scale: true,\n      axisLabel: { color: t.inkSoft, fontSize: 14, formatter: \"${value}\" },\n      axisLine: { show: true, lineStyle: { color: t.inkSoft } },\n      splitLine: { show: false },\n    },\n  ],\n  series: [\n    {\n      name: \"Portfolio NAV\",\n      type: \"line\",\n      yAxisIndex: 1,\n      data: navSeriesData,\n      symbol: \"none\",\n      lineStyle: { color: t.palette[0], width: 2.5 },\n      itemStyle: { color: t.palette[0] },\n    },\n    {\n      name: \"Drawdown\",\n      type: \"line\",\n      yAxisIndex: 0,\n      data: drawdownSeriesData,\n      symbol: \"none\",\n      lineStyle: { color: t.palette[4], width: 2 },\n      itemStyle: { color: t.palette[4] },\n      areaStyle: { color: t.palette[4], opacity: 0.32 },\n      markPoint: { data: markPointData },\n    },\n  ],\n});\n"}