{"spec_id":"renko-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nrenko-basic: Basic Renko Chart\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\nimport pandas as pd\n\n\n# Work around naming collision with plotnine.py\nsys.path.pop(0)\n\nfrom plotnine import (\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_rect,\n    ggplot,\n    labs,\n    scale_fill_manual,\n    theme,\n    theme_minimal,\n)\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# imprint semantic anchors for direction\nBULLISH = \"#009E73\"  # green - up bricks\nBEARISH = \"#AE3030\"  # red - down bricks\n\n# Data - Generate stock price data\nnp.random.seed(42)\nn_days = 250\ndates = pd.date_range(\"2024-01-01\", periods=n_days, freq=\"D\")\nreturns = np.random.normal(0.0005, 0.018, n_days)\nprices = 100 * np.cumprod(1 + returns)\nprice_data = pd.DataFrame({\"date\": dates, \"close\": prices})\n\n# Renko brick calculation\nbrick_size = 2.0  # $2 brick size\nbricks = []\ncurrent_price = price_data[\"close\"].iloc[0]\nbrick_start = (current_price // brick_size) * brick_size\ndirection = None  # None initially, then 'up' or 'down'\n\nfor close_price in price_data[\"close\"]:\n    while True:\n        if direction is None:\n            # First brick - determine initial direction\n            if close_price >= brick_start + brick_size:\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start,\n                        \"top\": brick_start + brick_size,\n                        \"direction\": \"up\",\n                    }\n                )\n                brick_start += brick_size\n                direction = \"up\"\n            elif close_price <= brick_start - brick_size:\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start - brick_size,\n                        \"top\": brick_start,\n                        \"direction\": \"down\",\n                    }\n                )\n                brick_start -= brick_size\n                direction = \"down\"\n            else:\n                break\n        elif direction == \"up\":\n            if close_price >= brick_start + brick_size:\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start,\n                        \"top\": brick_start + brick_size,\n                        \"direction\": \"up\",\n                    }\n                )\n                brick_start += brick_size\n            elif close_price <= brick_start - 2 * brick_size:\n                # Reversal requires 2x brick size\n                brick_start -= brick_size\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start - brick_size,\n                        \"top\": brick_start,\n                        \"direction\": \"down\",\n                    }\n                )\n                brick_start -= brick_size\n                direction = \"down\"\n            else:\n                break\n        else:  # direction == 'down'\n            if close_price <= brick_start - brick_size:\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start - brick_size,\n                        \"top\": brick_start,\n                        \"direction\": \"down\",\n                    }\n                )\n                brick_start -= brick_size\n            elif close_price >= brick_start + 2 * brick_size:\n                # Reversal requires 2x brick size\n                brick_start += brick_size\n                bricks.append(\n                    {\n                        \"brick_num\": len(bricks),\n                        \"bottom\": brick_start,\n                        \"top\": brick_start + brick_size,\n                        \"direction\": \"up\",\n                    }\n                )\n                brick_start += brick_size\n                direction = \"up\"\n            else:\n                break\n\nbrick_df = pd.DataFrame(bricks)\n\n# Create rectangle coordinates for geom_rect\nbrick_df[\"xmin\"] = brick_df[\"brick_num\"] + 0.1\nbrick_df[\"xmax\"] = brick_df[\"brick_num\"] + 0.9\nbrick_df[\"ymin\"] = brick_df[\"bottom\"]\nbrick_df[\"ymax\"] = brick_df[\"top\"]\n\n# Plot\nplot = (\n    ggplot(brick_df, aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\", fill=\"direction\"))\n    + geom_rect(color=INK_SOFT, size=0.3)\n    + scale_fill_manual(values={\"up\": BULLISH, \"down\": BEARISH}, labels={\"up\": \"Bullish\", \"down\": \"Bearish\"})\n    + labs(x=\"Brick Index\", y=\"Price ($)\", title=\"renko-basic · plotnine · anyplot.ai\", fill=\"Direction\")\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\n        text=element_text(size=14, color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        plot_title=element_text(size=24, color=INK),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=INK, size=0.5, alpha=0.15),\n        panel_grid_minor=element_line(color=INK, size=0.3, alpha=0.08),\n        panel_border=element_rect(color=INK_SOFT, fill=None, size=0.3),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT, size=0.3),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=300, width=16, height=9)\n"}