102 lines
3.5 KiB
Python
102 lines
3.5 KiB
Python
"""Ichimoku MS/W1/D1 alignment — a faithful port of CheckAlign() in
|
|
ichimoku-ms-w1-d1-ea.mq5.
|
|
|
|
A timeframe's last *completed* bar counts as aligned only when price AND the
|
|
chikou span both sit clearly above (bullish) or clearly below (bearish) the
|
|
tenkan-sen, kijun-sen, and kumo on that timeframe.
|
|
|
|
Bar/offset conventions (mirroring the EA):
|
|
* `df` is oldest -> newest; the LAST row is the last closed bar.
|
|
* The price-side tenkan/kijun are read at the last closed bar; the cloud
|
|
there is the Senkou value computed Kijun rows earlier (the spans are
|
|
plotted Kijun bars into the future).
|
|
* The chikou value equals the close of the last closed bar, plotted Kijun
|
|
bars back; the reference candle and tenkan/kijun are read there, and the
|
|
cloud at that position another Kijun rows further back.
|
|
"""
|
|
|
|
import pandas as pd
|
|
|
|
import config
|
|
|
|
|
|
def compute_levels(df):
|
|
high = df["high"]
|
|
low = df["low"]
|
|
tenkan = (high.rolling(config.TENKAN).max() + low.rolling(config.TENKAN).min()) / 2.0
|
|
kijun = (high.rolling(config.KIJUN).max() + low.rolling(config.KIJUN).min()) / 2.0
|
|
senkou_a = (tenkan + kijun) / 2.0
|
|
senkou_b = (high.rolling(config.SENKOU_B).max() + low.rolling(config.SENKOU_B).min()) / 2.0
|
|
return tenkan, kijun, senkou_a, senkou_b
|
|
|
|
|
|
def align_last_bar(df):
|
|
"""Return 1 (bullish), -1 (bearish), or 0 (not aligned) for the last bar."""
|
|
n = len(df)
|
|
# Enough bars for every level read below: the chikou-side cloud is read
|
|
# 2*KIJUN rows before the last closed bar, and Senkou B there needs its
|
|
# own SENKOU_B-bar window before that.
|
|
if n < config.SENKOU_B + 2 * config.KIJUN + 1:
|
|
return 0
|
|
|
|
tenkan, kijun, senkou_a, senkou_b = compute_levels(df)
|
|
|
|
# last closed bar (oldest->newest: the final row)
|
|
i0 = n - 1
|
|
close_p = float(df["close"].iloc[i0])
|
|
|
|
# The Senkou spans are plotted Kijun bars into the future, so the cloud
|
|
# shown under the last closed bar is the Senkou value computed Kijun bars
|
|
# earlier (mirrors the EA's CopyBuffer(..., sh + Kijun) read).
|
|
iPriceCloud = n - 1 - config.KIJUN
|
|
|
|
above = (
|
|
close_p > tenkan.iloc[i0]
|
|
and close_p > kijun.iloc[i0]
|
|
and close_p > max(senkou_a.iloc[iPriceCloud], senkou_b.iloc[iPriceCloud])
|
|
)
|
|
below = (
|
|
close_p < tenkan.iloc[i0]
|
|
and close_p < kijun.iloc[i0]
|
|
and close_p < min(senkou_a.iloc[iPriceCloud], senkou_b.iloc[iPriceCloud])
|
|
)
|
|
if not above and not below:
|
|
return 0
|
|
|
|
# chikou position: last closed bar, Kijun bars back
|
|
i1 = n - 1 - config.KIJUN
|
|
# Cloud at the chikou's plotted position: another Kijun bars further back.
|
|
i2 = n - 1 - 2 * config.KIJUN
|
|
chik = close_p # the chikou span for the last closed bar is its close
|
|
|
|
if above:
|
|
if (
|
|
chik > df["high"].iloc[i1]
|
|
and chik > tenkan.iloc[i1]
|
|
and chik > kijun.iloc[i1]
|
|
and chik > max(senkou_a.iloc[i2], senkou_b.iloc[i2])
|
|
):
|
|
return 1
|
|
return 0
|
|
|
|
if below:
|
|
if (
|
|
chik < df["low"].iloc[i1]
|
|
and chik < tenkan.iloc[i1]
|
|
and chik < kijun.iloc[i1]
|
|
and chik < min(senkou_a.iloc[i2], senkou_b.iloc[i2])
|
|
):
|
|
return -1
|
|
return 0
|
|
|
|
return 0
|
|
|
|
|
|
def alignment_summary(df):
|
|
"""Per-timeframe direction plus the last close, for alert messages."""
|
|
n = len(df)
|
|
if n < config.KIJUN + 1:
|
|
return {"dir": 0, "close": None, "levels": {}}
|
|
dirn = align_last_bar(df)
|
|
return {"dir": dirn, "close": float(df["close"].iloc[-1]), "levels": {}}
|