{"spec_id":"kagi-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nkagi-basic: Basic Kagi Chart\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.lines import Line2D\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\"\n\n# Data colors (Okabe-Ito palette, theme-independent)\nCOLOR_YANG = \"#009E73\"  # imprint green — bullish\nCOLOR_YIN = \"#AE3030\"  # imprint red — bearish\n\n# Generate synthetic stock price data\nnp.random.seed(42)\nn_days = 250\ninitial_price = 100.0\n\n# Create realistic price movements using geometric brownian motion\ndaily_returns = np.random.normal(0.0003, 0.015, n_days)\nprices = initial_price * np.cumprod(1 + daily_returns)\n\n# Kagi chart construction\nreversal_pct = 0.04  # 4% reversal threshold\n\n# Initialize Kagi data structures\nkagi_x = [0]\nkagi_y = [prices[0]]\nkagi_colors = []\nkagi_widths = []\n\ncurrent_direction = None  # 'up' or 'down'\ncurrent_price = prices[0]\nlast_high = prices[0]\nlast_low = prices[0]\nis_yang = True  # Start as yang (thick line)\nx_pos = 0\n\nfor price in prices[1:]:\n    reversal_amount = current_price * reversal_pct\n\n    if current_direction is None:\n        # Initialize direction based on first significant move\n        if price >= current_price + reversal_amount:\n            current_direction = \"up\"\n            is_yang = True\n            kagi_x.append(x_pos)\n            kagi_y.append(price)\n            kagi_colors.append(COLOR_YANG)\n            kagi_widths.append(4)\n            current_price = price\n            last_high = price\n        elif price <= current_price - reversal_amount:\n            current_direction = \"down\"\n            is_yang = False\n            kagi_x.append(x_pos)\n            kagi_y.append(price)\n            kagi_colors.append(COLOR_YIN)\n            kagi_widths.append(2)\n            current_price = price\n            last_low = price\n\n    elif current_direction == \"up\":\n        if price > current_price:\n            # Continue upward - extend line\n            kagi_y[-1] = price\n            current_price = price\n            # Check if we broke previous high -> become yang\n            if price > last_high:\n                is_yang = True\n                kagi_colors[-1] = COLOR_YANG\n                kagi_widths[-1] = 4\n                last_high = price\n        elif price <= current_price - reversal_amount:\n            # Reversal down - draw horizontal then vertical\n            x_pos += 1\n            # Horizontal shoulder line\n            kagi_x.append(x_pos)\n            kagi_y.append(current_price)\n            kagi_colors.append(COLOR_YANG if is_yang else COLOR_YIN)\n            kagi_widths.append(4 if is_yang else 2)\n            # New vertical down line\n            kagi_x.append(x_pos)\n            kagi_y.append(price)\n            # Check if we broke previous low -> become yin\n            if price < last_low:\n                is_yang = False\n                last_low = price\n            kagi_colors.append(COLOR_YANG if is_yang else COLOR_YIN)\n            kagi_widths.append(4 if is_yang else 2)\n            current_direction = \"down\"\n            current_price = price\n\n    elif current_direction == \"down\":\n        if price < current_price:\n            # Continue downward - extend line\n            kagi_y[-1] = price\n            current_price = price\n            # Check if we broke previous low -> become yin\n            if price < last_low:\n                is_yang = False\n                kagi_colors[-1] = COLOR_YIN\n                kagi_widths[-1] = 2\n                last_low = price\n        elif price >= current_price + reversal_amount:\n            # Reversal up - draw horizontal then vertical\n            x_pos += 1\n            # Horizontal waist line\n            kagi_x.append(x_pos)\n            kagi_y.append(current_price)\n            kagi_colors.append(COLOR_YANG if is_yang else COLOR_YIN)\n            kagi_widths.append(4 if is_yang else 2)\n            # New vertical up line\n            kagi_x.append(x_pos)\n            kagi_y.append(price)\n            # Check if we broke previous high -> become yang\n            if price > last_high:\n                is_yang = True\n                last_high = price\n            kagi_colors.append(COLOR_YANG if is_yang else COLOR_YIN)\n            kagi_widths.append(4 if is_yang else 2)\n            current_direction = \"up\"\n            current_price = price\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw Kagi lines segment by segment\nfor i in range(len(kagi_colors)):\n    ax.plot(\n        [kagi_x[i], kagi_x[i + 1]],\n        [kagi_y[i], kagi_y[i + 1]],\n        color=kagi_colors[i],\n        linewidth=kagi_widths[i],\n        solid_capstyle=\"round\",\n    )\n\n# Style\nax.set_xlabel(\"Kagi Line Index\", fontsize=20, color=INK)\nax.set_ylabel(\"Price ($)\", fontsize=20, color=INK)\nax.set_title(\"kagi-basic · matplotlib · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\n\n# Legend\nlegend_elements = [\n    Line2D([0], [0], color=COLOR_YANG, linewidth=4, label=\"Yang (Bullish)\"),\n    Line2D([0], [0], color=COLOR_YIN, linewidth=2, label=\"Yin (Bearish)\"),\n]\nleg = ax.legend(handles=legend_elements, fontsize=16, loc=\"upper left\")\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\n# Annotation explaining reversal threshold\nax.text(\n    0.98,\n    0.02,\n    f\"Reversal threshold: {reversal_pct * 100:.0f}%\",\n    transform=ax.transAxes,\n    fontsize=14,\n    ha=\"right\",\n    va=\"bottom\",\n    color=INK,\n    bbox={\"boxstyle\": \"round\", \"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"alpha\": 0.9},\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}