{"spec_id":"line-stock-comparison","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nline-stock-comparison: Stock Price Comparison Chart\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 83/100 | Updated: 2026-05-23\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_line,\n    geom_text,\n    ggplot,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_color_manual,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#AE3030\", \"#4467A3\"]\n\n# Data\nnp.random.seed(42)\ndates = pd.date_range(\"2024-01-01\", periods=252, freq=\"B\")\nsymbols = [\"AAPL\", \"GOOGL\", \"MSFT\", \"SPY\"]\n\ndata_frames = []\nfor symbol in symbols:\n    if symbol == \"AAPL\":\n        drift, volatility = 0.0008, 0.018\n    elif symbol == \"GOOGL\":\n        drift, volatility = 0.0006, 0.020\n    elif symbol == \"MSFT\":\n        drift, volatility = 0.0009, 0.016\n    else:  # SPY index — lower volatility\n        drift, volatility = 0.0005, 0.010\n\n    returns = np.random.normal(drift, volatility, len(dates))\n    price = 100 * np.exp(np.cumsum(returns))\n    df_symbol = pd.DataFrame({\"date\": dates, \"symbol\": symbol, \"price\": price})\n    data_frames.append(df_symbol)\n\ndf = pd.concat(data_frames, ignore_index=True)\ndf[\"rebased\"] = df.groupby(\"symbol\")[\"price\"].transform(lambda x: x / x.iloc[0] * 100)\n\n# Last data point per series for end-of-line labels\ndf_end = df.loc[df.groupby(\"symbol\")[\"date\"].idxmax()].copy()\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"date\", y=\"rebased\", color=\"symbol\"))\n    + geom_hline(yintercept=100, linetype=\"dashed\", color=INK_SOFT, size=0.6, alpha=0.6)\n    + geom_line(size=1.5, alpha=0.9, tooltips=layer_tooltips().format(\"^y\", \".1f\").line(\"@symbol\").line(\"Rebased|^y\"))\n    + geom_text(\n        data=df_end,\n        mapping=aes(x=\"date\", y=\"rebased\", label=\"symbol\", color=\"symbol\"),\n        hjust=0.5,\n        vjust=2,\n        size=8,\n        fontface=\"bold\",\n    )\n    + scale_color_manual(values=IMPRINT, name=\"Symbol\")\n    + labs(title=\"line-stock-comparison · python · letsplot · anyplot.ai\", x=\"Date\", y=\"Performance (rebased to 100)\")\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=RULE, size=0.3),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=12, color=INK),\n        axis_text=element_text(size=10, color=INK_SOFT),\n        plot_title=element_text(size=16, color=INK),\n        legend_title=element_text(size=10, color=INK),\n        legend_text=element_text(size=10, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n    )\n    + ggsize(800, 450)\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}