{"spec_id":"indicator-ichimoku","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nindicator-ichimoku: Ichimoku Cloud Technical Indicator Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.collections import PatchCollection\nfrom matplotlib.lines import Line2D\nfrom matplotlib.patches import Patch, Rectangle\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\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 palette — semantic exception: green/red for conventional financial up/down\nUP_COLOR = \"#009E73\"  # Imprint position 1, brand green — bullish candles and cloud\nDOWN_COLOR = \"#AE3030\"  # Imprint position 5, matte red — bearish candles and cloud\nTENKAN_COLOR = \"#C475FD\"  # Imprint position 2, lavender — Tenkan-sen\nKIJUN_COLOR = \"#4467A3\"  # Imprint position 3, blue — Kijun-sen\nCHIKOU_COLOR = \"#BD8233\"  # Imprint position 4, ochre — Chikou Span\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data — 220 trading days, seed 777\nnp.random.seed(777)\nn_days = 220\ndates = pd.date_range(\"2024-01-02\", periods=n_days, freq=\"B\")\n\nprice = 145.0\ndrift = np.concatenate(\n    [\n        np.linspace(0.3, 0.8, 60),\n        np.linspace(0.8, -0.2, 40),\n        np.linspace(-0.2, -0.7, 40),\n        np.linspace(-0.7, 0.5, 45),\n        np.linspace(0.5, 0.9, 35),\n    ]\n)\nopens, highs, lows, closes = [], [], [], []\nfor i in range(n_days):\n    change = drift[i] + np.random.randn() * 1.8\n    volatility = abs(np.random.randn()) * 1.2 + 0.5\n    open_price = price\n    close_price = price + change\n    high_price = max(open_price, close_price) + abs(np.random.randn()) * volatility\n    low_price = min(open_price, close_price) - abs(np.random.randn()) * volatility\n    opens.append(open_price)\n    highs.append(high_price)\n    lows.append(low_price)\n    closes.append(close_price)\n    price = close_price\n\ndf = pd.DataFrame({\"date\": dates, \"open\": opens, \"high\": highs, \"low\": lows, \"close\": closes})\ndf[\"bullish\"] = df[\"close\"] >= df[\"open\"]\ndf[\"x\"] = range(n_days)\n\n# Ichimoku calculations — standard parameters (9, 26, 52)\ntenkan_period, kijun_period, senkou_b_period, displacement = 9, 26, 52, 26\n\ndf[\"tenkan_sen\"] = (df[\"high\"].rolling(window=tenkan_period).max() + df[\"low\"].rolling(window=tenkan_period).min()) / 2\n\ndf[\"kijun_sen\"] = (df[\"high\"].rolling(window=kijun_period).max() + df[\"low\"].rolling(window=kijun_period).min()) / 2\n\ndf[\"senkou_span_a\"] = ((df[\"tenkan_sen\"] + df[\"kijun_sen\"]) / 2).shift(displacement)\n\ndf[\"senkou_span_b\"] = (\n    (df[\"high\"].rolling(window=senkou_b_period).max() + df[\"low\"].rolling(window=senkou_b_period).min()) / 2\n).shift(displacement)\n\ndf[\"chikou_span\"] = df[\"close\"].shift(-displacement)\n\n# Trim to visible window — skip early NaN period\nvisible_start = 80\ndf_vis = df.iloc[visible_start:].copy()\ndf_vis[\"x\"] = range(len(df_vis))\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Cloud (Kumo) fill — subtle alpha so candles remain readable\nCLOUD_ALPHA = 0.18\nspan_a = df_vis[\"senkou_span_a\"].values\nspan_b = df_vis[\"senkou_span_b\"].values\nx_vals = df_vis[\"x\"].values\nmask_valid = ~(np.isnan(span_a) | np.isnan(span_b))\nx_cloud = x_vals[mask_valid]\nsa_cloud = span_a[mask_valid]\nsb_cloud = span_b[mask_valid]\n\nax.fill_between(\n    x_cloud, sa_cloud, sb_cloud, where=sa_cloud >= sb_cloud, color=UP_COLOR, alpha=CLOUD_ALPHA, interpolate=True\n)\nax.fill_between(\n    x_cloud, sa_cloud, sb_cloud, where=sa_cloud < sb_cloud, color=DOWN_COLOR, alpha=CLOUD_ALPHA, interpolate=True\n)\n\n# Senkou Span boundary lines via seaborn lineplot\nspan_df = pd.DataFrame(\n    {\n        \"x\": np.tile(x_cloud, 2),\n        \"value\": np.concatenate([sa_cloud, sb_cloud]),\n        \"span\": [\"Senkou A\"] * len(x_cloud) + [\"Senkou B\"] * len(x_cloud),\n    }\n)\nsns.lineplot(\n    data=span_df,\n    x=\"x\",\n    y=\"value\",\n    hue=\"span\",\n    palette={\"Senkou A\": UP_COLOR, \"Senkou B\": DOWN_COLOR},\n    linewidth=0.8,\n    alpha=0.4,\n    ax=ax,\n    legend=False,\n)\n\n# Candlestick wicks\nwick_colors = [UP_COLOR if b else DOWN_COLOR for b in df_vis[\"bullish\"]]\nax.vlines(df_vis[\"x\"], df_vis[\"low\"], df_vis[\"high\"], colors=wick_colors, linewidth=0.7)\n\n# Candle bodies — bullish: filled, bearish: hollow (shape redundancy for colorblind safety)\nbody_width = 0.5\nbull_rects, bear_rects = [], []\nfor _, row in df_vis.iterrows():\n    body_h = abs(row[\"close\"] - row[\"open\"])\n    bottom = min(row[\"open\"], row[\"close\"])\n    height = max(body_h, 0.12)\n    if body_h < 0.12:\n        bottom = (row[\"open\"] + row[\"close\"]) / 2 - 0.06\n    rect = Rectangle((row[\"x\"] - body_width / 2, bottom), body_width, height)\n    if row[\"bullish\"]:\n        bull_rects.append(rect)\n    else:\n        bear_rects.append(rect)\n\nif bull_rects:\n    ax.add_collection(PatchCollection(bull_rects, facecolors=UP_COLOR, edgecolors=UP_COLOR, linewidths=0.4))\nif bear_rects:\n    ax.add_collection(PatchCollection(bear_rects, facecolors=\"none\", edgecolors=DOWN_COLOR, linewidths=1.0))\n\n# Ichimoku indicator lines in long format for seaborn hue-based rendering\ntenkan_df = df_vis[[\"x\", \"tenkan_sen\"]].dropna().rename(columns={\"tenkan_sen\": \"value\"})\ntenkan_df[\"indicator\"] = \"Tenkan-sen (9)\"\nkijun_df = df_vis[[\"x\", \"kijun_sen\"]].dropna().rename(columns={\"kijun_sen\": \"value\"})\nkijun_df[\"indicator\"] = \"Kijun-sen (26)\"\nchikou_df = df_vis[[\"x\", \"chikou_span\"]].dropna().rename(columns={\"chikou_span\": \"value\"})\nchikou_df[\"indicator\"] = \"Chikou Span\"\n\nindicator_df = pd.concat([tenkan_df, kijun_df, chikou_df], ignore_index=True)\nindicator_palette = {\"Tenkan-sen (9)\": TENKAN_COLOR, \"Kijun-sen (26)\": KIJUN_COLOR, \"Chikou Span\": CHIKOU_COLOR}\nindicator_sizes = {\"Tenkan-sen (9)\": 1.4, \"Kijun-sen (26)\": 1.6, \"Chikou Span\": 1.0}\n\nsns.lineplot(\n    data=indicator_df,\n    x=\"x\",\n    y=\"value\",\n    hue=\"indicator\",\n    palette=indicator_palette,\n    size=\"indicator\",\n    sizes=indicator_sizes,\n    alpha=0.85,\n    ax=ax,\n    legend=False,\n)\n\n# TK crossover signals via seaborn scatterplot — triangles for directional clarity\ntenkan_vals = df_vis[\"tenkan_sen\"].values\nkijun_vals = df_vis[\"kijun_sen\"].values\ncross_data = []\nfor i in range(1, len(tenkan_vals)):\n    if any(np.isnan(v) for v in [tenkan_vals[i], kijun_vals[i], tenkan_vals[i - 1], kijun_vals[i - 1]]):\n        continue\n    prev_diff = tenkan_vals[i - 1] - kijun_vals[i - 1]\n    curr_diff = tenkan_vals[i] - kijun_vals[i]\n    if prev_diff <= 0 < curr_diff:\n        cross_data.append({\"x\": df_vis[\"x\"].iloc[i], \"y\": tenkan_vals[i], \"signal\": \"Bullish TK Cross\"})\n    elif prev_diff >= 0 > curr_diff:\n        cross_data.append({\"x\": df_vis[\"x\"].iloc[i], \"y\": tenkan_vals[i], \"signal\": \"Bearish TK Cross\"})\n\nif cross_data:\n    cross_df = pd.DataFrame(cross_data)\n    sns.scatterplot(\n        data=cross_df,\n        x=\"x\",\n        y=\"y\",\n        hue=\"signal\",\n        palette={\"Bullish TK Cross\": UP_COLOR, \"Bearish TK Cross\": DOWN_COLOR},\n        style=\"signal\",\n        markers={\"Bullish TK Cross\": \"^\", \"Bearish TK Cross\": \"v\"},\n        s=55,\n        edgecolor=INK_SOFT,\n        linewidth=0.7,\n        zorder=10,\n        ax=ax,\n        legend=False,\n    )\n\n# X-axis date labels\ntick_step = max(1, len(df_vis) // 8)\ntick_idx = list(range(0, len(df_vis), tick_step))\nax.set_xticks(tick_idx)\nax.set_xticklabels(\n    [df_vis.iloc[i][\"date\"].strftime(\"%b %d\") for i in tick_idx], rotation=30, ha=\"right\", fontsize=8, color=INK_SOFT\n)\n\n# Style\nax.set_xlabel(\"Date\", fontsize=10, color=INK)\nax.set_ylabel(\"Price ($)\", fontsize=10, color=INK)\nax.set_title(\"indicator-ichimoku · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=8)\nax.tick_params(axis=\"y\", labelsize=8, colors=INK_SOFT, length=0)\nax.tick_params(axis=\"x\", length=0)\nsns.despine(ax=ax)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax.xaxis.grid(False)\nax.set_axisbelow(True)\n\n# Axis limits\nax.set_xlim(-1, len(df_vis) + 0.5)\ny_pad = (df_vis[\"high\"].max() - df_vis[\"low\"].min()) * 0.08\nax.set_ylim(df_vis[\"low\"].min() - y_pad, df_vis[\"high\"].max() + y_pad * 2)\n\n# Legend\nlegend_handles = [\n    Patch(facecolor=UP_COLOR, edgecolor=UP_COLOR, label=\"Bullish (filled)\"),\n    Patch(facecolor=\"none\", edgecolor=DOWN_COLOR, linewidth=1.0, label=\"Bearish (hollow)\"),\n    Line2D([0], [0], color=TENKAN_COLOR, linewidth=1.4, label=\"Tenkan-sen (9)\"),\n    Line2D([0], [0], color=KIJUN_COLOR, linewidth=1.6, label=\"Kijun-sen (26)\"),\n    Line2D([0], [0], color=CHIKOU_COLOR, linewidth=1.0, alpha=0.85, label=\"Chikou Span\"),\n    Patch(facecolor=UP_COLOR, alpha=CLOUD_ALPHA, edgecolor=\"none\", label=\"Bullish Cloud\"),\n    Patch(facecolor=DOWN_COLOR, alpha=CLOUD_ALPHA, edgecolor=\"none\", label=\"Bearish Cloud\"),\n]\nax.legend(\n    handles=legend_handles,\n    fontsize=8,\n    loc=\"lower left\",\n    framealpha=0.95,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    ncol=2,\n    fancybox=False,\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}