Ichimoku in Python — Coding It from Scratch (pandas)

All five lines, with the forward and backward displacement done right

Ichimoku computed in Python
Ichimoku computed in Python

Ichimoku looks like the hardest indicator in this series to code, but it’s really just five midpoints and two time shifts, as the explainer covered. The only thing to get exactly right is which way each line is displaced.

What you’ll need

  • Python 3.9+, pandas
  • high, low, close series.

All five lines

import pandas as pd


def ichimoku(high, low, close, conv=9, base=26, span_b=52, shift=26):
    def midline(n):
        return (high.rolling(n).max() + low.rolling(n).min()) / 2

    tenkan = midline(conv)          # Conversion line (9)
    kijun  = midline(base)          # Base line (26)

    senkou_a = ((tenkan + kijun) / 2).shift(shift)   # Leading Span A, 26 FORWARD
    senkou_b = midline(span_b).shift(shift)          # Leading Span B, 26 FORWARD
    chikou   = close.shift(-shift)                   # Lagging Span, 26 BACK

    return pd.DataFrame({
        "tenkan": tenkan, "kijun": kijun,
        "senkou_a": senkou_a, "senkou_b": senkou_b, "chikou": chikou,
    })

Every line is (highest high + lowest low) / 2 over some window — even Span B. The only subtlety is the displacement: the two Senkou spans are shifted forward (+shift) so the cloud projects ahead, and the Chikou is shifted backward (−shift). Get the sign wrong and everything looks plausible but is subtly useless.

Reading the cloud in code

ich = ichimoku(df["high"], df["low"], df["close"])
cloud_top = ich[["senkou_a", "senkou_b"]].max(axis=1)
cloud_bot = ich[["senkou_a", "senkou_b"]].min(axis=1)

df["above_cloud"] = df["close"] > cloud_top     # bullish bias
df["below_cloud"] = df["close"] < cloud_bot     # bearish bias
df["tk_bull"] = (ich["tenkan"] > ich["kijun"]) & (ich["tenkan"].shift(1) <= ich["kijun"].shift(1))

Now above_cloud & tk_bull is a concrete “aligned bullish” condition to backtest in AlgoGen.

Gotchas

  • Displacement direction. Spans forward (.shift(+26)), Chikou back (.shift(-26)). This is the #1 Ichimoku bug.
  • The future cloud has no price yet. senkou_a/b extend 26 bars beyond your last close — those rows are the projected cloud and there’s no price to compare them to until time catches up. That’s expected, not missing data.
  • Backtesting the Chikou. Because it’s shifted back, comparing Chikou to price is really comparing today’s close to price 26 bars ago — don’t accidentally introduce look-ahead by testing it against future bars.

Cloud thickness and twists

Two more reads that are trivial once you have the spans. Cloud thickness proxies the strength of support/resistance, and a twist (spans crossing) flags a projected trend change:

thickness = (ich["senkou_a"] - ich["senkou_b"]).abs()
twist_bull = (ich["senkou_a"] > ich["senkou_b"]) & (ich["senkou_a"].shift(1) <= ich["senkou_b"].shift(1))

Because both spans are already shifted forward, these series naturally align with the projected cloud — a thick cloud ahead means a stronger barrier, and a fresh twist_bull marks where the future cloud flips green.

The lazy one-liner

import pandas_ta as ta
df.ta.ichimoku(append=True)   # returns the visible lines plus the forward spans

Same indicator elsewhere: MQL5, Pine Script, EasyLanguage, NinjaScript.


This post is educational, not financial advice. Indicators describe the past; they don’t predict the future. Backtest anything before you risk real money on it.

Sources and further reading

  1. Ichimoku: At a Glance (CMT Association)
  2. Windowing operations (pandas documentation)

Historical research from the Algogen archive. Not investment advice.

Blog

Have a strategy idea?

Describe it in plain English, preview where your rules fire, then decide whether the evidence is worth a backtest.

Keep me postedHow it works