{"spec_id":"line-stock-comparison","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nline-stock-comparison: Stock Price Comparison Chart\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-23\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\nSELECTOR_ACTIVE = \"rgba(26,26,23,0.12)\" if THEME == \"light\" else \"rgba(240,239,232,0.12)\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#AE3030\", \"#4467A3\"]\n\n# Data - Generate synthetic stock price data for 4 companies\nnp.random.seed(42)\nn_days = 252  # Approximately 1 year of trading days\ndate_range = pd.date_range(start=\"2024-01-01\", periods=n_days, freq=\"B\")\n\n# Define stocks with different volatility and drift characteristics\nstocks = {\n    \"AAPL\": {\"drift\": 0.0008, \"volatility\": 0.018},\n    \"GOOGL\": {\"drift\": 0.0006, \"volatility\": 0.020},\n    \"MSFT\": {\"drift\": 0.0007, \"volatility\": 0.016},\n    \"SPY\": {\"drift\": 0.0004, \"volatility\": 0.012},\n}\n\n# Generate price paths using geometric Brownian motion\nprice_data = {}\nfor symbol, params in stocks.items():\n    returns = np.random.normal(params[\"drift\"], params[\"volatility\"], n_days)\n    prices = 100 * np.exp(np.cumsum(returns))\n    price_data[symbol] = prices\n\n# Create DataFrame and normalize all series to 100 at the starting point (rebasing)\ndf = pd.DataFrame(price_data, index=date_range)\ndf_rebased = (df / df.iloc[0]) * 100\ndates = df_rebased.index.strftime(\"%Y-%m-%d\").tolist()\n\n# Plot\nfig = go.Figure()\n\nfor i, symbol in enumerate(stocks.keys()):\n    fig.add_trace(\n        go.Scatter(\n            x=dates,\n            y=df_rebased[symbol],\n            mode=\"lines\",\n            name=symbol,\n            line={\"width\": 2.5, \"color\": IMPRINT[i]},\n            hovertemplate=f\"{symbol}<br>Date: %{{x|%Y-%m-%d}}<br>Value: %{{y:.1f}}<extra></extra>\",\n        )\n    )\n\n# Add horizontal reference line at 100\nfig.add_hline(\n    y=100,\n    line_dash=\"dash\",\n    line_color=INK_MUTED,\n    line_width=1.5,\n    annotation_text=\"Starting Point (100)\",\n    annotation_position=\"bottom right\",\n    annotation_font_size=10,\n    annotation_font_color=INK_MUTED,\n)\n\n# Add end-point value labels for each series to show final performance\nfor i, symbol in enumerate(stocks.keys()):\n    final_value = df_rebased[symbol].iloc[-1]\n    pct = final_value - 100\n    sign = \"+\" if pct >= 0 else \"\"\n    fig.add_annotation(\n        x=dates[-1],\n        y=final_value,\n        text=f\"  {sign}{pct:.0f}%\",\n        showarrow=False,\n        xanchor=\"left\",\n        yanchor=\"middle\",\n        font={\"size\": 9, \"color\": IMPRINT[i]},\n    )\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\n        \"text\": \"line-stock-comparison · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Date\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": False,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"zerolinecolor\": INK_SOFT,\n        \"rangeselector\": {\n            \"buttons\": [\n                {\"count\": 1, \"label\": \"1M\", \"step\": \"month\", \"stepmode\": \"backward\"},\n                {\"count\": 3, \"label\": \"3M\", \"step\": \"month\", \"stepmode\": \"backward\"},\n                {\"count\": 6, \"label\": \"6M\", \"step\": \"month\", \"stepmode\": \"backward\"},\n                {\"step\": \"all\", \"label\": \"1Y\"},\n            ],\n            \"bgcolor\": ELEVATED_BG,\n            \"activecolor\": SELECTOR_ACTIVE,\n            \"bordercolor\": INK_SOFT,\n            \"borderwidth\": 1,\n            \"font\": {\"color\": INK_SOFT, \"size\": 9},\n        },\n        \"rangeslider\": {\"visible\": True, \"bgcolor\": ELEVATED_BG, \"bordercolor\": INK_SOFT, \"thickness\": 0.05},\n    },\n    yaxis={\n        \"title\": {\"text\": \"Rebased Price (Starting = 100)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"zerolinecolor\": INK_SOFT,\n    },\n    legend={\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"orientation\": \"h\",\n        \"yanchor\": \"bottom\",\n        \"y\": 1.02,\n        \"xanchor\": \"center\",\n        \"x\": 0.5,\n    },\n    margin={\"l\": 80, \"r\": 80, \"t\": 80, \"b\": 80},\n    hovermode=\"x unified\",\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}