{"spec_id":"line-yield-curve","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nline-yield-curve: Yield Curve (Interest Rate Term Structure)\nLibrary: plotly 6.8.0 | Python 3.13.13\nQuality: 95/100 | Updated: 2026-06-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\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.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint palette positions 1-3\nC1 = \"#009E73\"  # brand green — normal/upward-sloping curve\nC2 = \"#C475FD\"  # lavender — flat curve\nC3 = \"#4467A3\"  # blue — inverted curve\n\n# Data - U.S. Treasury yield curves on three dates\nmaturity_labels = [\"1M\", \"3M\", \"6M\", \"1Y\", \"2Y\", \"3Y\", \"5Y\", \"7Y\", \"10Y\", \"20Y\", \"30Y\"]\nmaturity_years = np.array([1 / 12, 0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30])\n\n# Normal upward-sloping curve (Jan 2022)\nyields_normal = np.array([0.08, 0.21, 0.44, 0.78, 1.18, 1.42, 1.72, 1.90, 1.98, 2.32, 2.27])\n# Flat curve (Jun 2023)\nyields_flat = np.array([5.27, 5.40, 5.47, 5.40, 4.87, 4.49, 4.13, 4.03, 3.84, 4.09, 3.91])\n# Inverted curve (Oct 2023)\nyields_inverted = np.array([5.54, 5.55, 5.56, 5.46, 5.05, 4.80, 4.62, 4.65, 4.62, 4.98, 4.81])\n\nfig = go.Figure()\n\nfor ydata, name, color, marker in [\n    (yields_normal, \"Jan 2022 (Normal)\", C1, \"circle\"),\n    (yields_flat, \"Jun 2023 (Flat)\", C2, \"diamond\"),\n    (yields_inverted, \"Oct 2023 (Inverted)\", C3, \"square\"),\n]:\n    fig.add_trace(\n        go.Scatter(\n            x=maturity_years,\n            y=ydata,\n            name=name,\n            mode=\"lines+markers\",\n            line={\"color\": color, \"width\": 2.5, \"shape\": \"spline\"},\n            marker={\"size\": 10, \"symbol\": marker, \"line\": {\"width\": 1.5, \"color\": ELEVATED_BG}},\n            hovertemplate=\"%{text}<br>Yield: %{y:.2f}%<extra>\" + name + \"</extra>\",\n            text=maturity_labels,\n        )\n    )\n\n# Inversion shading — band where short-term yields exceed long-term yields\nshort_term_max = max(yields_inverted[:4])\nlong_term_min = min(yields_inverted[6:])\nfig.add_hrect(y0=long_term_min, y1=short_term_max, fillcolor=\"rgba(68,103,163,0.08)\", line_width=0)\n\n# Annotation: inversion zone callout (on-chart at 6M maturity)\n# Note: with xaxis.type='log', annotation x values use log10(data_value)\nfig.add_annotation(\n    x=np.log10(0.5),\n    y=short_term_max,\n    xref=\"x\",\n    yref=\"y\",\n    text=\"<b>Inversion Zone</b><br><i>Short-term yields exceed<br>long-term yields</i>\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1.3,\n    arrowwidth=1.5,\n    arrowcolor=C3,\n    ax=85,\n    ay=-55,\n    font={\"size\": 11, \"color\": INK_SOFT},\n    align=\"left\",\n    bordercolor=C3,\n    borderwidth=1,\n    borderpad=5,\n    bgcolor=ELEVATED_BG,\n)\n\n# Annotation: basis-point spread between inverted and normal at 10Y\nspread_10y_bps = int(round((yields_inverted[8] - yields_normal[8]) * 100))\nfig.add_annotation(\n    x=np.log10(10),\n    y=(yields_inverted[8] + yields_normal[8]) / 2,\n    xref=\"x\",\n    yref=\"y\",\n    text=f\"<b>+{spread_10y_bps} bps</b><br>at 10Y\",\n    showarrow=False,\n    font={\"size\": 10, \"color\": C1},\n    bgcolor=ELEVATED_BG,\n    borderpad=4,\n)\n\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\n        \"text\": (\n            \"<b>U.S. Treasury Yield Curves</b>\"\n            f\"<br><span style='font-size:10px;color:{INK_MUTED}'>\"\n            \"line-yield-curve · python · plotly · anyplot.ai</span>\"\n        ),\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n        \"y\": 0.97,\n        \"yanchor\": \"top\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Maturity\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickvals\": maturity_years,\n        \"ticktext\": maturity_labels,\n        \"type\": \"log\",\n        \"showgrid\": False,\n        \"showline\": True,\n        \"linewidth\": 1,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n        \"spikemode\": \"across\",\n        \"spikethickness\": 1,\n        \"spikecolor\": INK_MUTED,\n        \"spikedash\": \"dot\",\n    },\n    yaxis={\n        \"title\": {\"text\": \"Yield (%)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"ticksuffix\": \"%\",\n        \"showgrid\": True,\n        \"gridwidth\": 1,\n        \"gridcolor\": GRID,\n        \"showline\": True,\n        \"linewidth\": 1,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n        \"spikemode\": \"across\",\n        \"spikethickness\": 1,\n        \"spikecolor\": INK_MUTED,\n        \"spikedash\": \"dot\",\n    },\n    legend={\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"x\": 0.99,\n        \"y\": 0.02,\n        \"xanchor\": \"right\",\n        \"yanchor\": \"bottom\",\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"itemsizing\": \"constant\",\n    },\n    margin={\"l\": 80, \"r\": 40, \"t\": 90, \"b\": 60},\n    hovermode=\"x unified\",\n    hoverlabel={\"font_size\": 10, \"namelength\": -1},\n    spikedistance=-1,\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}