
Ichimoku Trading Series: Part 5 of 10 | ← Previous | View Full Series
The Perfect Entry Setup
We now combine the EMA trend filter with Ichimoku cloud conditions to find high-probability entries.
Entry Rules for LONG Position
Step 1: Confirm Uptrend (EMA Filter)
EMA_signal == +1 (at least 7 candles fully above EMA 100)
Step 2: Confirm Momentum (Ichimoku Cloud)
Within the last 10 candles, at least 7 must be FULLY ABOVE the cloud
“Fully above” means: Open > cloud_top AND Close > cloud_top
Step 3: The Entry Trigger (Cloud Pierce)
Current candle opens INSIDE the cloud AND closes ABOVE it
This represents a retracement that is bouncing back into the trend direction.
Entry Rules for SHORT Position
Mirror image:
EMA_signal == -1- At least 7 of 10 candles fully BELOW cloud
- Current candle opens inside cloud, closes BELOW
Why These Rules Work
“We are trading with a trend and we are trying to capture patterns where candles are also above the Ichimoku cloud confirming a strong momentum. But then we look for a candle dipping or bouncing in and out of the cloud because we are looking for some kind of a retracement.”
The key insight:
“When the candle closes above the cloud, we assume that the retracement is over and the price will probably continue in the direction of the main trend.”
Code Implementation
def createSignals(df: pd.DataFrame,
lookback_window: int = 10,
min_confirm: int = 5,
ema_signal_col: str = "EMA_signal") -> pd.DataFrame:
"""
Produce a single signal column aligned with EMA trend:
+1 (long): Ichimoku pierce-up + enough prior bars entirely ABOVE cloud
AND EMA_signal == +1
-1 (short): Ichimoku pierce-down + enough prior bars entirely BELOW cloud
AND EMA_signal == -1
0 (none): otherwise
"""
out = df.copy()
# Cloud boundaries
cloud_top = out[["ich_spanA", "ich_spanB"]].max(axis=1)
cloud_bot = out[["ich_spanA", "ich_spanB"]].min(axis=1)
# Candles entirely above/below cloud
above_cloud = (out["Open"] > cloud_top) & (out["Close"] > cloud_top)
below_cloud = (out["Open"] < cloud_bot) & (out["Close"] < cloud_bot)
above_count = above_cloud.rolling(lookback_window, min_periods=lookback_window).sum()
below_count = below_cloud.rolling(lookback_window, min_periods=lookback_window).sum()
# Current-bar pierce conditions
pierce_up = (out["Open"] < cloud_top) & (out["Close"] > cloud_top)
pierce_down = (out["Open"] > cloud_bot) & (out["Close"] < cloud_bot)
# Trend confirmations
up_trend_ok = above_count >= min_confirm
down_trend_ok = below_count >= min_confirm
# EMA alignment
ema_up = (out[ema_signal_col] == 1)
ema_down = (out[ema_signal_col] == -1)
# Final conditions
long_cond = up_trend_ok & pierce_up & ema_up
short_cond = down_trend_ok & pierce_down & ema_down
signal = np.where(long_cond & ~short_cond, 1,
np.where(short_cond & ~long_cond, -1, 0)).astype(int)
out["signal"] = signal
return out
# Usage
df = createSignals(df, lookback_window=10, min_confirm=7)
Ideal vs Non-Ideal Entries
Ideal Entry
- Small candle that dips into cloud
- Closes just above cloud top
- Three long wicks showing rejection of cloud (support)
- Tight entry close to cloud = better risk/reward
Less Ideal Entry
- Long candle that dips into cloud
- Closes far above cloud top
- Entry is “late” = wider stop-loss needed
