
Ichimoku Trading Series: Part 7 of 10 | ← Previous | View Full Series
Required Libraries
from __future__ import annotations
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
import numpy as np
import pandas as pd
import pandas_ta as ta # Technical analysis
import yfinance as yf # Free market data
from backtesting import Backtest, Strategy
Installation
pip install numpy pandas pandas-ta yfinance backtesting plotly
Fetching Market Data
def fetch_data(symbol: str, start: str, end: str, interval: str) -> pd.DataFrame:
"""
Fetch OHLCV data from Yahoo Finance.
Parameters:
- symbol: "EURUSD=X", "AAPL", "BTC-USD", etc.
- start/end: "2023-01-01" format
- interval: "1h", "4h", "1d"
"""
df = yf.download(symbol, start=start, end=end, interval=interval,
auto_adjust=True, progress=False, threads=False)
if df.empty:
raise ValueError(f"No data returned for {symbol}")
# Handle MultiIndex columns (newer yfinance versions)
if isinstance(df.columns, pd.MultiIndex):
df = df.xs(symbol, axis=1, level=1)
# Standardize column names
df.columns = [c.title() for c in df.columns]
return df.dropna()
Adding Ichimoku Indicators
def add_ichimoku(df: pd.DataFrame,
tenkan: int = 9,
kijun: int = 26,
senkou_b: int = 52) -> pd.DataFrame:
"""
Build bias-safe Ichimoku columns for SIGNAL logic.
- Raw spans (no forward shift) to avoid look-ahead bias
"""
out = df.copy()
h, l, c = out["High"], out["Low"], out["Close"]
# Compute lines manually (bias-free)
tenkan_series = (h.rolling(tenkan).max() + l.rolling(tenkan).min()) / 2.0
kijun_series = (h.rolling(kijun).max() + l.rolling(kijun).min()) / 2.0
# Raw spans (NO forward shift!)
span_a_raw = (tenkan_series + kijun_series) / 2.0
span_b_raw = (h.rolling(senkou_b).max() + l.rolling(senkou_b).min()) / 2.0
out["ich_tenkan"] = tenkan_series
out["ich_kijun"] = kijun_series
out["ich_spanA"] = span_a_raw
out["ich_spanB"] = span_b_raw
# Add ATR for trade management
out["ATR"] = ta.atr(out["High"], out["Low"], out["Close"], length=14)
# Add EMA for trend filter
out["EMA"] = ta.ema(out["Close"], length=100)
# Drop warmup NaNs
return out.dropna()
Putting It Together
# Configuration
SYMBOL = "EURUSD=X"
START = "2023-10-01"
END = "2024-10-01"
INTERVAL = "4h"
CASH = 100_000
COMMISSION = 0.0002 # 0.02%
# Fetch and prepare data
df = fetch_data(SYMBOL, START, END, INTERVAL)
df = add_ichimoku(df)
df = MovingAverageSignal(df, back_candles=7)
df = createSignals(df, lookback_window=10, min_confirm=7)
df = df.dropna()
print(f"Data shape: {df.shape}")
print(df.tail())
Data Verification
Your DataFrame should now have these columns:
Open, High, Low, Close, Volume,
ich_tenkan, ich_kijun, ich_spanA, ich_spanB,
ATR, EMA, EMA_signal, signal
