Ichimoku Cloud

A multi-component system that packages trend direction, support/ resistance, and momentum into one indicator set — more involved than anything so far, but each component is simple.

def ichimoku(df: pd.DataFrame, tenkan_period=9, kijun_period=26, senkou_b_period=52, displacement=26) -> pd.DataFrame:
    def midpoint(period):
        return (df["high"].rolling(period).max() + df["low"].rolling(period).min()) / 2

    tenkan_sen = midpoint(tenkan_period)          # conversion line — fast
    kijun_sen = midpoint(kijun_period)             # base line — slow
    senkou_span_a = ((tenkan_sen + kijun_sen) / 2).shift(displacement)    # leading span A
    senkou_span_b = midpoint(senkou_b_period).shift(displacement)          # leading span B
    chikou_span = df["close"].shift(-displacement)                          # lagging span

    return pd.DataFrame({
        "tenkan_sen": tenkan_sen, "kijun_sen": kijun_sen,
        "senkou_span_a": senkou_span_a, "senkou_span_b": senkou_span_b,
        "chikou_span": chikou_span,
    }, index=df.index)

The five lines, in plain terms

  • Tenkan-sen (conversion line) — fast average, similar role to a short EMA.
  • Kijun-sen (base line) — slower average, similar role to a medium EMA; also commonly used as a standalone support/resistance/trailing stop level.
  • Senkou Span A/B — plotted displacement bars *forward*, forming the "cloud" (Kumo) — the gap between them is the cloud itself.
  • Chikou Span — current close plotted displacement bars *backward*, used to check for confirmation against price action from that many bars ago.

Standard signals

def ichimoku_signal(df: pd.DataFrame, ichi: pd.DataFrame) -> pd.Series:
    signal = pd.Series(0, index=df.index)
    price_above_cloud = df["close"] > ichi[["senkou_span_a", "senkou_span_b"]].max(axis=1)
    price_below_cloud = df["close"] < ichi[["senkou_span_a", "senkou_span_b"]].min(axis=1)
    tk_bullish = ichi["tenkan_sen"] > ichi["kijun_sen"]

    signal[price_above_cloud & tk_bullish] = 1
    signal[price_below_cloud & ~tk_bullish] = -1
    return signal

This requiring both "price above/below cloud" AND "Tenkan/Kijun cross agreeing" is itself confluence logic (chapter 115) — Ichimoku is really a bundle of pre-designed confluence rules rather than one raw indicator value.

Why the forward/backward shifts matter for backtesting correctness

senkou_span_a/b are plotted displacement bars into the *future* relative to the data that computed them — meaning at the current bar, the cloud you visually see extending ahead of price was computed from data available displacement bars ago, not future information (not a look-ahead violation, chapter 83) — but a careless backtest implementation that misaligns the shift direction can accidentally introduce one. Always verify: does the value at index i in your computed cloud series depend only on data at or before index i - displacement? If yes, it's safe; if the shift direction is flipped, it silently leaks future data.

Practical note

Ichimoku's default periods (9, 26, 52) were designed for the historical Japanese trading week structure — some practitioners adjust these for 5-day-week markets like NSE, others use the defaults unchanged. Validate via backtesting (chapter 82) rather than assuming either choice is automatically correct for Indian markets.

Next: 110 — Volume indicators: OBV, volume profile