Notebooks/Pattern & Indicator Confluence
Signals·Combined Signals·Intermediate

Pattern & Indicator Confluence

Build a confluence engine that fires only when chart patterns and technical indicators agree — reducing noise and improving precision.

patternsconfluenceindicators

Pattern + Indicator Confluence System

Core Logic

This section describes the fundamental principles behind the Pattern + Indicator Confluence trading system, outlining how candlestick patterns are combined with technical indicators for signal generation.

Pattern + Indicator Confluence combines candlestick pattern signals with technical indicator confirmation, requiring both to agree before emitting a trade signal.

Logic:

  • Detect hammer (bullish) and shooting star (bearish) single-candle patterns.
  • Confirm with RSI: RSI < 40 confirms bullish patterns; RSI > 60 confirms bearish.
  • Signal is only emitted when pattern AND indicator agree.

Limitation: This dual-confirmation approach reduces signal frequency significantly; a minimum dataset of 1000+ bars is recommended for statistical validity.

[1]
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

Data Generation and Application

This section covers the generation of synthetic OHLCV data, which is then fed into the pattern_indicator_confluence_system function to produce trading signals. The output shows the counts of different patterns and signals detected.

[8]
def generate_data(periods: int) -> pd.DataFrame:
    """
    Generate synthetic OHLCV price data using a geometric random walk.

    Parameters
    ----------
    periods : int
        Number of 1-minute bars to generate.

    Returns
    -------
    pd.DataFrame
        DataFrame with columns: open, high, low, close, volume, datetime.
    """
    start_date     = pd.to_datetime("2024-01-01 00:00:00+00:00")
    datetime_index = pd.date_range(start_date, periods=periods, freq="1min", tz="UTC")
    price_data = []
    last_close = 42000
    for i in range(periods):
        open_price  = last_close + np.random.normal(0, last_close * 0.0005)
        close_price = open_price + np.random.normal(0, last_close * 0.005)
        body_high   = max(open_price, close_price)
        body_low    = min(open_price, close_price)
        high_price  = max(body_high + abs(np.random.normal(0, last_close * 0.002)), open_price, close_price)
        low_price   = min(body_low  - abs(np.random.normal(0, last_close * 0.002)), open_price, close_price)
        if high_price < low_price:
            high_price, low_price = low_price, high_price
        price_data.append({
            "open":  max(1, int(open_price)),
            "high":  max(1, int(high_price)),
            "low":   max(1, int(low_price)),
            "close": max(1, int(close_price)),
        })
        last_close = close_price
    df = pd.DataFrame(price_data, index=datetime_index)
    df.index.name = "datetime"
    df["volume"]   = np.random.uniform(100.0, 500.0, periods)
    df["datetime"] = df.index.to_series()
    return df.reset_index(drop=True)

df = generate_data(500)
display(df.head())
open high low close volume datetime
0 42030 42101 41918 41934 286.774763 2024-01-01 00:00:00+00:00
1 41923 42234 41788 42229 328.672052 2024-01-01 00:01:00+00:00
2 42212 42278 41959 42036 112.342922 2024-01-01 00:02:00+00:00
3 42047 42150 41965 42090 220.524604 2024-01-01 00:03:00+00:00
4 42136 42142 41969 41994 406.657245 2024-01-01 00:04:00+00:00

Function

This section defines the pattern_indicator_confluence_system function, which implements the core logic described above. It takes OHLCV data and RSI parameters as input and returns a DataFrame with detected patterns, RSI values, and final trading signals.

[7]
def pattern_indicator_confluence_system(
    df: pd.DataFrame,
    rsi_period: int = 14,
    rsi_bull_level: float = 40.0,
    rsi_bear_level: float = 60.0,
) -> pd.DataFrame:
    """
    Combine candlestick pattern detection with RSI confirmation.

    Core logic
    ----------
    1. Detect Hammer (bullish) and Shooting Star (bearish) candle patterns
       using body/range and wick/body ratios.
    2. Compute RSI for momentum confirmation.
    3. Emit Buy (+1) only when a Hammer AND RSI < rsi_bull_level.
    4. Emit Sell (-1) only when a Shooting Star AND RSI > rsi_bear_level.

    Parameters
    ----------
    df : pd.DataFrame   OHLCV DataFrame.
    rsi_period : int    RSI calculation period.
    rsi_bull_level : float  RSI threshold for bullish confirmation.
    rsi_bear_level : float  RSI threshold for bearish confirmation.

    Returns
    -------
    pd.DataFrame with: pattern, rsi, pattern_signal, rsi_signal, signal.
    """
    df = df.copy().sort_values("datetime", ignore_index=True)

    # ── Candle geometry ───────────────────────────────────────────────────────
    df["body"]       = (df["close"] - df["open"]).abs()
    df["full_range"] = df["high"] - df["low"]
    df["upper_wick"] = df["high"] - df[["open","close"]].max(axis=1)
    df["lower_wick"] = df[["open","close"]].min(axis=1) - df["low"]
    range_safe = df["full_range"].replace(0, np.nan)
    body_safe  = df["body"].replace(0, np.nan)

    # ── Pattern detection ─────────────────────────────────────────────────────
    hammer = (
        (df["lower_wick"] >= 2 * body_safe) &
        (df[["open","close"]].min(axis=1) > df["low"] + 0.7 * df["full_range"])
    )
    shooting_star = (
        (df["upper_wick"] >= 2 * body_safe) &
        (df[["open","close"]].max(axis=1) < df["low"] + 0.3 * df["full_range"])
    )
    df["pattern"]        = "none"
    df["pattern_signal"] = 0
    df.loc[hammer,        "pattern"] = "hammer"
    df.loc[shooting_star, "pattern"] = "shooting_star"
    df.loc[hammer,        "pattern_signal"] =  1
    df.loc[shooting_star, "pattern_signal"] = -1

    # ── RSI ───────────────────────────────────────────────────────────────────
    delta = df["close"].diff()
    gain  = delta.clip(lower=0).rolling(rsi_period).mean()
    loss  = (-delta.clip(upper=0)).rolling(rsi_period).mean()
    df["rsi"] = 100 - 100 / (1 + gain / loss.replace(0, np.nan))
    df["rsi_signal"] = np.where(df["rsi"] < rsi_bull_level, 1,
                       np.where(df["rsi"] > rsi_bear_level, -1, 0))

    # ── Confluence signal ─────────────────────────────────────────────────────
    df["signal"] = 0
    df.loc[(df["pattern_signal"] == 1) & (df["rsi_signal"] == 1),  "signal"] =  1
    df.loc[(df["pattern_signal"] == -1) & (df["rsi_signal"] == -1), "signal"] = -1

    return df
[9]
df_signals = pattern_indicator_confluence_system(df)
print(df_signals["pattern"].value_counts())
print(df_signals["signal"].value_counts())
pattern
none             477
hammer            15
shooting_star      8
Name: count, dtype: int64
signal
 0    493
-1      5
 1      2
Name: count, dtype: int64

Explanation of Parameters

This section clarifies the significance of the parameters used in the pattern_indicator_confluence_system, particularly how the dual-condition gate and adjusted RSI thresholds influence signal generation.

  • Dual-condition gate: Both pattern_signal == X and rsi_signal == X must be true simultaneously; a single condition is insufficient to trigger the final signal.
  • RSI thresholds of 40/60 (rather than the classic 30/70) are intentionally looser, increasing the number of qualifying RSI conditions to compensate for the rarity of confirmed candle patterns.

Visualization

This section provides the code to visualize the generated OHLCV data, overlaid with the detected buy and sell signals, and the corresponding RSI values. This plot helps in understanding the system's output visually.

[10]
buy_signals  = df_signals[df_signals["signal"] ==  1]
sell_signals = df_signals[df_signals["signal"] == -1]
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
    subplot_titles=["Price + Confluence Signals", "RSI"],
    row_heights=[0.65, 0.35])
fig.add_trace(go.Candlestick(x=df_signals["datetime"],
    open=df_signals["open"], high=df_signals["high"],
    low=df_signals["low"], close=df_signals["close"], name="Price"), row=1, col=1)
fig.add_trace(go.Scatter(x=buy_signals["datetime"],  y=buy_signals["low"]  * 0.999,
    mode="markers", marker=dict(symbol="triangle-up",   size=10, color="green"), name="Buy"),  row=1, col=1)
fig.add_trace(go.Scatter(x=sell_signals["datetime"], y=sell_signals["high"] * 1.001,
    mode="markers", marker=dict(symbol="triangle-down", size=10, color="red"),   name="Sell"), row=1, col=1)
fig.add_trace(go.Scatter(x=df_signals["datetime"], y=df_signals["rsi"],
    mode="lines", name="RSI", line=dict(color="purple")), row=2, col=1)
for lvl, clr in [(40, "green"), (60, "red")]:
    fig.add_hline(y=lvl, line_dash="dot", line_color=clr, row=2, col=1)
fig.update_layout(title_text="Pattern + Indicator Confluence",
    xaxis_rangeslider_visible=False, height=700, xaxis2_title="Datetime")
fig.show()