{"spec_id":"line-stock-comparison","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nline-stock-comparison: Stock Price Comparison Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-23\n\"\"\"\n\nimport os\nimport sys\n\n\ndel sys.path[0]  # prevent altair.py from shadowing the altair package\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\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\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#AE3030\", \"#4467A3\"]\n\n# Data\nnp.random.seed(42)\ndates = pd.date_range(\"2024-01-01\", periods=252, freq=\"B\")\n\nsymbols = [\"AAPL\", \"GOOGL\", \"MSFT\", \"SPY\"]\nreturns_params = {\"AAPL\": (0.0003, 0.012), \"GOOGL\": (0.0002, 0.011), \"MSFT\": (0.0003, 0.008), \"SPY\": (0.0001, 0.006)}\n\ndata_list = []\nfor symbol in symbols:\n    mean_return, volatility = returns_params[symbol]\n    returns = np.random.normal(mean_return, volatility, len(dates))\n    prices = 100 * np.cumprod(1 + returns)\n    for date, price in zip(dates, prices, strict=True):\n        data_list.append({\"date\": date, \"symbol\": symbol, \"rebased_price\": price})\n\ndf = pd.DataFrame(data_list)\n\n# Plot\nbase = alt.Chart(df).encode(\n    x=alt.X(\"date:T\", title=\"Date\"),\n    y=alt.Y(\"rebased_price:Q\", title=\"Rebased Price (Start = 100)\", scale=alt.Scale(zero=False)),\n    color=alt.Color(\n        \"symbol:N\",\n        title=\"Symbol\",\n        scale=alt.Scale(domain=symbols, range=IMPRINT),\n        legend=alt.Legend(titleFontSize=10, labelFontSize=10, symbolSize=120),\n    ),\n    tooltip=[\n        alt.Tooltip(\"date:T\", title=\"Date\"),\n        alt.Tooltip(\"symbol:N\", title=\"Stock\"),\n        alt.Tooltip(\"rebased_price:Q\", title=\"Rebased Price\", format=\".1f\"),\n    ],\n)\n\nlines = base.mark_line().encode(strokeWidth=alt.condition(alt.datum.symbol == \"SPY\", alt.value(3.0), alt.value(2.0)))\n\nreference_line = (\n    alt.Chart(pd.DataFrame({\"y\": [100]}))\n    .mark_rule(color=INK_SOFT, strokeDash=[6, 4], strokeWidth=1.5, opacity=0.6)\n    .encode(y=\"y:Q\")\n)\n\nlayer = alt.layer(reference_line, lines).properties(\n    width=620,\n    height=320,\n    background=PAGE_BG,\n    padding={\"left\": 0, \"right\": 0, \"top\": 0, \"bottom\": 0},\n    title=alt.Title(\"line-stock-comparison · python · altair · anyplot.ai\", fontSize=16, color=INK, anchor=\"middle\"),\n)\n\nchart = (\n    layer.configure_view(fill=PAGE_BG, stroke=None, continuousWidth=620, continuousHeight=320)\n    .configure_axis(\n        grid=True,\n        labelFontSize=10,\n        titleFontSize=12,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.10,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_title(color=INK, fontSize=16)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save PNG\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# Pad to exact target 3200×1800\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}