{"spec_id":"frontier-efficient","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nfrontier-efficient: Efficient Frontier for Portfolio Optimization\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\n# Fix for running script named bokeh.py (avoid shadowing the bokeh package)\nif Path(__file__).name == \"bokeh.py\":\n    sys.path = [p for p in sys.path if Path(__file__).parent != Path(p)]\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColorBar, ColumnDataSource, LinearColorMapper\nfrom bokeh.palettes import Viridis256\nfrom bokeh.plotting import figure\nfrom scipy.optimize import minimize\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"  # Okabe-Ito position 1\nACCENT_1 = \"#C475FD\"  # Okabe-Ito position 2\nACCENT_2 = \"#4467A3\"  # Okabe-Ito position 3\n\n# Asset data (5 assets)\nnp.random.seed(42)\nn_assets = 5\n\nexpected_returns = np.array([0.04, 0.10, 0.12, 0.09, 0.07])\n\nvolatilities = np.array([0.05, 0.18, 0.25, 0.20, 0.22])\ncorrelations = np.array(\n    [\n        [1.00, 0.20, 0.15, 0.10, 0.05],\n        [0.20, 1.00, 0.85, 0.70, 0.30],\n        [0.15, 0.85, 1.00, 0.65, 0.35],\n        [0.10, 0.70, 0.65, 1.00, 0.40],\n        [0.05, 0.30, 0.35, 0.40, 1.00],\n    ]\n)\ncov_matrix = np.outer(volatilities, volatilities) * correlations\nrisk_free_rate = 0.02\n\n# Generate random portfolios\nn_portfolios = 300\nportfolio_returns = []\nportfolio_risks = []\nportfolio_sharpes = []\n\nfor _ in range(n_portfolios):\n    weights = np.random.random(n_assets)\n    weights /= weights.sum()\n\n    ret = np.dot(weights, expected_returns)\n    risk = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))\n    sharpe = (ret - risk_free_rate) / risk\n\n    portfolio_returns.append(ret)\n    portfolio_risks.append(risk)\n    portfolio_sharpes.append(sharpe)\n\nportfolio_returns = np.array(portfolio_returns)\nportfolio_risks = np.array(portfolio_risks)\nportfolio_sharpes = np.array(portfolio_sharpes)\n\n# Calculate efficient frontier using scipy optimization\nconstraints = {\"type\": \"eq\", \"fun\": lambda x: np.sum(x) - 1}\nbounds = tuple((0, 1) for _ in range(n_assets))\ninit_weights = np.array([1 / n_assets] * n_assets)\n\n# Find minimum variance portfolio\nmin_var_result = minimize(\n    lambda w: np.sqrt(np.dot(w.T, np.dot(cov_matrix, w))),\n    init_weights,\n    method=\"SLSQP\",\n    bounds=bounds,\n    constraints=constraints,\n)\nmin_var_weights = min_var_result.x\nmin_var_return = np.dot(min_var_weights, expected_returns)\nmin_var_risk = np.sqrt(np.dot(min_var_weights.T, np.dot(cov_matrix, min_var_weights)))\n\n# Find maximum Sharpe ratio portfolio\nmax_sharpe_result = minimize(\n    lambda w: -(np.dot(w, expected_returns) - risk_free_rate) / np.sqrt(np.dot(w.T, np.dot(cov_matrix, w))),\n    init_weights,\n    method=\"SLSQP\",\n    bounds=bounds,\n    constraints=constraints,\n)\nmax_sharpe_weights = max_sharpe_result.x\nmax_sharpe_return = np.dot(max_sharpe_weights, expected_returns)\nmax_sharpe_risk = np.sqrt(np.dot(max_sharpe_weights.T, np.dot(cov_matrix, max_sharpe_weights)))\nmax_sharpe = (max_sharpe_return - risk_free_rate) / max_sharpe_risk\n\n# Generate efficient frontier\nfrontier_returns = []\nfrontier_risks = []\ntarget_returns = np.linspace(min_var_return, expected_returns.max(), 50)\n\nfor target in target_returns:\n    constraints_ef = (\n        {\"type\": \"eq\", \"fun\": lambda x: np.sum(x) - 1},\n        {\"type\": \"eq\", \"fun\": lambda x, t=target: np.dot(x, expected_returns) - t},\n    )\n    result = minimize(\n        lambda w: np.sqrt(np.dot(w.T, np.dot(cov_matrix, w))),\n        init_weights,\n        method=\"SLSQP\",\n        bounds=bounds,\n        constraints=constraints_ef,\n    )\n    if result.success:\n        frontier_returns.append(np.dot(result.x, expected_returns))\n        frontier_risks.append(np.sqrt(np.dot(result.x.T, np.dot(cov_matrix, result.x))))\n\n# Map Sharpe ratios to colors\nsharpe_min = min(portfolio_sharpes)\nsharpe_max = max(portfolio_sharpes)\nsharpe_normalized = [(s - sharpe_min) / (sharpe_max - sharpe_min) for s in portfolio_sharpes]\ncolor_indices = [int(s * 255) for s in sharpe_normalized]\ncolors = [Viridis256[min(i, 255)] for i in color_indices]\n\n# Create ColumnDataSource\nsource = ColumnDataSource(\n    data={\"risk\": portfolio_risks, \"return\": portfolio_returns, \"sharpe\": portfolio_sharpes, \"color\": colors}\n)\n\n# Create figure\np = figure(\n    width=4800,\n    height=2700,\n    title=\"frontier-efficient · bokeh · anyplot.ai\",\n    x_axis_label=\"Risk (Standard Deviation)\",\n    y_axis_label=\"Expected Return\",\n    tools=\"pan,box_zoom,reset,save\",\n)\n\n# Plot random portfolios with Sharpe ratio color coding\np.scatter(\"risk\", \"return\", source=source, size=18, color=\"color\", alpha=0.7)\n\n# Plot efficient frontier curve\nfrontier_source = ColumnDataSource(data={\"risk\": frontier_risks, \"return\": frontier_returns})\np.line(\"risk\", \"return\", source=frontier_source, line_width=6, color=BRAND, legend_label=\"Efficient Frontier\")\n\n# Mark minimum variance portfolio\nmin_var_source = ColumnDataSource(data={\"risk\": [min_var_risk], \"return\": [min_var_return]})\np.scatter(\n    \"risk\",\n    \"return\",\n    source=min_var_source,\n    size=45,\n    color=ACCENT_1,\n    marker=\"star\",\n    line_color=INK_SOFT,\n    line_width=3,\n    legend_label=\"Min Variance Portfolio\",\n)\n\n# Mark maximum Sharpe ratio portfolio\nmax_sharpe_source = ColumnDataSource(data={\"risk\": [max_sharpe_risk], \"return\": [max_sharpe_return]})\np.scatter(\n    \"risk\",\n    \"return\",\n    source=max_sharpe_source,\n    size=45,\n    color=ACCENT_2,\n    marker=\"diamond\",\n    line_color=INK_SOFT,\n    line_width=3,\n    legend_label=\"Max Sharpe Portfolio\",\n)\n\n# Capital Market Line (from risk-free rate tangent to max Sharpe portfolio)\ncml_x_end = max(portfolio_risks) * 1.1\ncml_y_end = risk_free_rate + max_sharpe * cml_x_end\ncml_source = ColumnDataSource(data={\"x\": [0, cml_x_end], \"y\": [risk_free_rate, cml_y_end]})\np.line(\n    \"x\", \"y\", source=cml_source, line_width=4, line_dash=\"dashed\", color=INK_SOFT, legend_label=\"Capital Market Line\"\n)\n\n# Style title and labels\np.title.text_font_size = \"28pt\"\np.title.text_color = INK\np.xaxis.axis_label_text_font_size = \"22pt\"\np.yaxis.axis_label_text_font_size = \"22pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_font_size = \"18pt\"\np.yaxis.major_label_text_font_size = \"18pt\"\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\n\n# Style grid\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.10\np.ygrid.grid_line_alpha = 0.10\n\n# Style legend (position bottom-right to avoid overlap)\np.legend.location = \"bottom_right\"\np.legend.label_text_font_size = \"18pt\"\np.legend.label_text_color = INK_SOFT\np.legend.background_fill_color = PAGE_BG\np.legend.background_fill_alpha = 0.9\np.legend.border_line_color = INK_SOFT\np.legend.glyph_height = 30\np.legend.glyph_width = 30\np.legend.spacing = 12\np.legend.padding = 15\n\n# Add color bar for Sharpe ratio\ncolor_mapper = LinearColorMapper(palette=Viridis256, low=sharpe_min, high=sharpe_max)\ncolor_bar = ColorBar(\n    color_mapper=color_mapper,\n    title=\"Sharpe Ratio\",\n    title_text_font_size=\"22pt\",\n    major_label_text_font_size=\"18pt\",\n    label_standoff=15,\n    width=40,\n    location=(0, 0),\n)\ncolor_bar.title_text_color = INK\ncolor_bar.major_label_text_color = INK_SOFT\np.add_layout(color_bar, \"right\")\n\n# Set background and borders\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\np.min_border_right = 120\n\n# Save HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome\nW, H = 4800, 2700\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}