{"spec_id":"ohlc-bar","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nohlc-bar: OHLC Bar Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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\n# Okabe-Ito palette\nUP_COLOR = \"#009E73\"  # Okabe-Ito position 1 (brand green)\nDOWN_COLOR = \"#AE3030\"  # imprint red — down days\n\n# Data - Generate 50 trading days of OHLC data\nnp.random.seed(42)\nn_days = 50\ndates = pd.date_range(\"2024-01-02\", periods=n_days, freq=\"B\")  # Business days\n\n# Start price and random walk\nstart_price = 150.0\nreturns = np.random.normal(0.001, 0.02, n_days)\n\n# Generate OHLC data\nprices = [start_price]\nfor r in returns[:-1]:\n    prices.append(prices[-1] * (1 + r))\n\nopens = []\nhighs = []\nlows = []\ncloses = []\n\nfor base_price in prices:\n    daily_volatility = abs(np.random.normal(0, 0.015))\n    intraday_move = np.random.normal(0, 0.01)\n\n    open_price = base_price * (1 + np.random.normal(0, 0.005))\n    close_price = open_price * (1 + intraday_move)\n    high_price = max(open_price, close_price) * (1 + daily_volatility)\n    low_price = min(open_price, close_price) * (1 - daily_volatility)\n\n    opens.append(open_price)\n    highs.append(high_price)\n    lows.append(low_price)\n    closes.append(close_price)\n\ndf = pd.DataFrame({\"date\": dates, \"open\": opens, \"high\": highs, \"low\": lows, \"close\": closes})\n\n# Add direction indicator\ndf[\"direction\"] = [\"Up\" if c >= o else \"Down\" for o, c in zip(df[\"open\"], df[\"close\"], strict=True)]\n\n# High-Low vertical lines\nhl_lines = (\n    alt.Chart(df)\n    .mark_rule(strokeWidth=2)\n    .encode(\n        x=alt.X(\"date:T\", title=\"Date\", axis=alt.Axis(labelFontSize=18, titleFontSize=22, format=\"%b %d\")),\n        y=alt.Y(\n            \"low:Q\", title=\"Price (USD)\", scale=alt.Scale(zero=False), axis=alt.Axis(labelFontSize=18, titleFontSize=22)\n        ),\n        y2=\"high:Q\",\n        color=alt.Color(\n            \"direction:N\",\n            scale=alt.Scale(domain=[\"Up\", \"Down\"], range=[UP_COLOR, DOWN_COLOR]),\n            legend=alt.Legend(title=\"Direction\", titleFontSize=18, labelFontSize=16),\n        ),\n    )\n)\n\n# Open ticks (left side)\nopen_rules = (\n    alt.Chart(df)\n    .transform_calculate(\n        open_start=\"datum.date - 10*60*60*1000\"  # 10 hours offset in milliseconds\n    )\n    .mark_rule(strokeWidth=2)\n    .encode(\n        x=\"open_start:T\",\n        x2=\"date:T\",\n        y=\"open:Q\",\n        color=alt.Color(\n            \"direction:N\", scale=alt.Scale(domain=[\"Up\", \"Down\"], range=[UP_COLOR, DOWN_COLOR]), legend=None\n        ),\n    )\n)\n\n# Close ticks (right side)\nclose_rules = (\n    alt.Chart(df)\n    .transform_calculate(\n        close_end=\"datum.date + 10*60*60*1000\"  # 10 hours offset in milliseconds\n    )\n    .mark_rule(strokeWidth=2)\n    .encode(\n        x=\"date:T\",\n        x2=\"close_end:T\",\n        y=\"close:Q\",\n        color=alt.Color(\n            \"direction:N\", scale=alt.Scale(domain=[\"Up\", \"Down\"], range=[UP_COLOR, DOWN_COLOR]), legend=None\n        ),\n    )\n)\n\n# Layer all components and apply theme-adaptive styling\nchart = (\n    alt.layer(hl_lines, open_rules, close_rules)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\"ohlc-bar · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK_SOFT,\n        gridOpacity=0.10,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_title(color=INK)\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n    .interactive()\n)\n\n# Save as PNG and HTML with theme-suffixed filenames\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}