Signals·Signal Confluence Systems·Intermediate

Pattern Indicator Confluence System

Combine automated chart pattern detection with technical indicator readings to generate high-conviction trade signals that require both a recognized technical price pattern AND confirming indicator alignment before triggering an entry.

pattern-recognitionsignal-generationtechnical-analysistrading-signals

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.

[ ]
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.

[ ]
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.

[ ]
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
[ ]
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.

[ ]
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()

Conclusion

This notebook demonstrates the Pattern + Indicator Confluence System, a trading strategy that combines candlestick pattern detection with RSI-based momentum confirmation.

Key Takeaways:

  • Dual Confirmation Logic: The system explicitly requires both a candlestick pattern (Hammer for bullish, Shooting Star for bearish) and a confirming RSI level (below 40 for bullish, above 60 for bearish) to generate a trade signal.
  • Signal Rarity: As expected with a dual-confirmation approach, the system generates a relatively low number of signals, even with synthetic data. Out of 500 generated bars, only 2 buy signals and 5 sell signals were emitted. This highlights the system's focus on high-conviction trades over frequency.
  • Data Requirements: The low signal frequency reinforces the need for a large dataset (1000+ bars) to achieve statistical validity and adequately test the system's performance.
  • Visualization Insights: The interactive Plotly chart visually confirms how signals are generated only when a pattern coincides with the appropriate RSI condition, providing a clear understanding of the system's mechanics. The RSI thresholds are intentionally set slightly wider (40/60) to increase the likelihood of indicator confirmation, given the strictness of the pattern detection.