The Multi-Pattern Confluence script, is a ThinkOrSwim (thinkScript) study that detects five well-known Japanese candlestick patterns and combines them into a single weighted confluence score.
Instead of acting on any single pattern in isolation, the study requires multiple patterns to align within a configurable look back window before generating a signal.
The core idea is simple: one pattern is a suggestion; multiple patterns are conviction.
Feel free to use and share the script!
Comments and suggestions are always welcome!
Instructions and the script are in the attached pdf file.
How It Works
• Selection: Each of the five patterns can be individually enabled or disabled via toggle inputs.
• Detection: On every bar, all enabled pattern detectors run independently.
• Scoring: Each detected pattern contributes a weighted score (1 or 2 points).
• Accumulation: Scores are summed across the current bar and a configurable lookback window (default: 3bars).
• Filtering: An optional EMA trend filter ensures signals align with the prevailing trend direction.
• Signal: When the accumulated score meets or exceeds the threshold, an entry arrow appears on thechart.
• Reset: A one-click reset toggle restores all parameters to their default values.
note:
This document and the accompanying thinkScript code are provided for educational purposes.
Always backtest and paper trade before using any technical indicator with real capital.
Instead of acting on any single pattern in isolation, the study requires multiple patterns to align within a configurable look back window before generating a signal.
The core idea is simple: one pattern is a suggestion; multiple patterns are conviction.
Feel free to use and share the script!
Comments and suggestions are always welcome!
Instructions and the script are in the attached pdf file.
How It Works
• Selection: Each of the five patterns can be individually enabled or disabled via toggle inputs.
• Detection: On every bar, all enabled pattern detectors run independently.
• Scoring: Each detected pattern contributes a weighted score (1 or 2 points).
• Accumulation: Scores are summed across the current bar and a configurable lookback window (default: 3bars).
• Filtering: An optional EMA trend filter ensures signals align with the prevailing trend direction.
• Signal: When the accumulated score meets or exceeds the threshold, an entry arrow appears on thechart.
• Reset: A one-click reset toggle restores all parameters to their default values.
note:
This document and the accompanying thinkScript code are provided for educational purposes.
Always backtest and paper trade before using any technical indicator with real capital.
Code:
# ============================================
# Multi-Pattern Confluence Study v1.1
# Combines 5 Japanese candlestick patterns
# with weighted scoring, lookback window,
# and trend filter (20 EMA)
#
# Author: Murat Ilgar
# Email: [email protected]
# Comments and suggestions welcome!
# ============================================
# ---- PATTERN TOGGLES ----
input enableThreeInsideUpDown = yes;
input enableThreeSoldiersCrows = yes;
input enableThreeMethods = yes;
input enableMorningEveningStar = yes;
input enablePiercingDarkCloud = yes;
# ---- PARAMETERS ----
input lookbackWindow = 3;
input scoreThreshold = 3;
input emaLength = 20;
input useTrendFilter = yes;
input gapRequired = {default "Auto", "Always", "Never"};
# ---- RESET TO DEFAULTS ----
input resetToDefaults = no;
def lb = if resetToDefaults then 3 else lookbackWindow;
def thresh = if resetToDefaults then 3 else scoreThreshold;
def emaLen = if resetToDefaults then 20 else emaLength;
def trendOn = if resetToDefaults then yes else useTrendFilter;
def aggPeriod = GetAggregationPeriod();
def isIntraday = aggPeriod < AggregationPeriod.DAY;
def requireGap =
if resetToDefaults then !isIntraday
else if gapRequired == gapRequired."Always" then yes
else if gapRequired == gapRequired."Never" then no
else !isIntraday;
def ema = ExpAverage(close, emaLen);
# ============================================
# PATTERN 1: Three Inside Up / Down (weight 2)
# ============================================
def threeInsideUp = enableThreeInsideUpDown and
close[2] < open[2] and
high[1] < high[2] and low[1] > low[2] and
close[1] < open[1] and
close > open and
close > close[2];
def threeInsideDown = enableThreeInsideUpDown and
close[2] > open[2] and
high[1] < high[2] and low[1] > low[2] and
close[1] > open[1] and
close < open and
close < close[2];
# ============================================
# PATTERN 2: Three White Soldiers / Black Crows (weight 2)
# ============================================
def threeWhiteSoldiers = enableThreeSoldiersCrows and
close[2] > open[2] and
close[1] > open[1] and
close > open and
open[1] > open[2] and open[1] < close[2] and
open > open[1] and open < close[1] and
close[1] > close[2] and
close > close[1];
def threeBlackCrows = enableThreeSoldiersCrows and
close[2] < open[2] and
close[1] < open[1] and
close < open and
open[1] < open[2] and open[1] > close[2] and
open < open[1] and open > close[1] and
close[1] < close[2] and
close < close[1];
# ============================================
# PATTERN 3: Rising / Falling Three Methods (weight 2)
# ============================================
def c1Body3 = AbsValue(close[4] - open[4]);
def c1Range3 = high[4] - low[4];
def consolidationContained =
high[3] <= high[4] and low[3] >= low[4] and
high[2] <= high[4] and low[2] >= low[4] and
high[1] <= high[4] and low[1] >= low[4];
def consolidationSmall =
(high[3] - low[3] == 0 or AbsValue(close[3] - open[3])
< (high[3] - low[3]) * 0.5) and
(high[2] - low[2] == 0 or AbsValue(close[2] - open[2])
< (high[2] - low[2]) * 0.5) and
(high[1] - low[1] == 0 or AbsValue(close[1] - open[1])
< (high[1] - low[1]) * 0.5);
def risingThreeMethods = enableThreeMethods and
close[4] > open[4] and
c1Range3 > 0 and c1Body3 > c1Range3 * 0.6 and
consolidationContained and consolidationSmall and
close > open and close > close[4];
def fallingThreeMethods = enableThreeMethods and
close[4] < open[4] and
c1Range3 > 0 and c1Body3 > c1Range3 * 0.6 and
consolidationContained and consolidationSmall and
close < open and close < close[4];
# ============================================
# PATTERN 4: Morning Star / Evening Star (weight 2)
# ============================================
def starRange = high[1] - low[1];
def starBody = AbsValue(close[1] - open[1]);
def morningStar = enableMorningEveningStar and
close[2] < open[2] and
(starRange == 0 or starBody < starRange * 0.5) and
(if requireGap then low[1] > close[2] else yes) and
close > open and
close > (open[2] + close[2]) / 2;
def eveningStar = enableMorningEveningStar and
close[2] > open[2] and
(starRange == 0 or starBody < starRange * 0.5) and
(if requireGap then high[1] < close[2] else yes) and
close < open and
close < (open[2] + close[2]) / 2;
# ============================================
# PATTERN 5: Piercing Line / Dark Cloud Cover (weight 1)
# ============================================
def piercingLine = enablePiercingDarkCloud and
close[1] < open[1] and
close > open and
(if requireGap then open < low[1] else open < close[1]) and
close > (open[1] + close[1]) / 2;
def darkCloudCover = enablePiercingDarkCloud and
close[1] > open[1] and
close < open and
(if requireGap then open > high[1] else open > close[1]) and
close < (open[1] + close[1]) / 2;
# ============================================
# WEIGHTED SCORING WITH LOOKBACK WINDOW
# ============================================
def bullScore0 =
(if threeInsideUp then 2 else 0) +
(if threeWhiteSoldiers then 2 else 0) +
(if risingThreeMethods then 2 else 0) +
(if morningStar then 2 else 0) +
(if piercingLine then 1 else 0);
def bullScore1 =
(if threeInsideUp[1] then 2 else 0) +
(if threeWhiteSoldiers[1] then 2 else 0) +
(if risingThreeMethods[1] then 2 else 0) +
(if morningStar[1] then 2 else 0) +
(if piercingLine[1] then 1 else 0);
def bullScore2 =
(if threeInsideUp[2] then 2 else 0) +
(if threeWhiteSoldiers[2] then 2 else 0) +
(if risingThreeMethods[2] then 2 else 0) +
(if morningStar[2] then 2 else 0) +
(if piercingLine[2] then 1 else 0);
def totalBullScore =
if lb >= 3 then bullScore0 + bullScore1 + bullScore2
else if lb >= 2 then bullScore0 + bullScore1
else bullScore0;
def bearScore0 =
(if threeInsideDown then 2 else 0) +
(if threeBlackCrows then 2 else 0) +
(if fallingThreeMethods then 2 else 0) +
(if eveningStar then 2 else 0) +
(if darkCloudCover then 1 else 0);
def bearScore1 =
(if threeInsideDown[1] then 2 else 0) +
(if threeBlackCrows[1] then 2 else 0) +
(if fallingThreeMethods[1] then 2 else 0) +
(if eveningStar[1] then 2 else 0) +
(if darkCloudCover[1] then 1 else 0);
def bearScore2 =
(if threeInsideDown[2] then 2 else 0) +
(if threeBlackCrows[2] then 2 else 0) +
(if fallingThreeMethods[2] then 2 else 0) +
(if eveningStar[2] then 2 else 0) +
(if darkCloudCover[2] then 1 else 0);
def totalBearScore =
if lb >= 3 then bearScore0 + bearScore1 + bearScore2
else if lb >= 2 then bearScore0 + bearScore1
else bearScore0;
# ============================================
# SIGNAL GENERATION
# ============================================
def bullSignal = totalBullScore >= thresh and
(if trendOn then close < ema else yes);
def bearSignal = totalBearScore >= thresh and
(if trendOn then close > ema else yes);
def bullTrigger = bullSignal and !bullSignal[1];
def bearTrigger = bearSignal and !bearSignal[1];
# ============================================
# PLOTS
# ============================================
plot BullishEntry = if bullTrigger then low - TickSize() * 5
else Double.NaN;
BullishEntry.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
BullishEntry.SetDefaultColor(Color.LIME);
BullishEntry.SetLineWeight(4);
plot BearishEntry = if bearTrigger then high + TickSize() * 5
else Double.NaN;
BearishEntry.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
BearishEntry.SetDefaultColor(Color.MAGENTA);
BearishEntry.SetLineWeight(4);
plot TrendLine = ema;
TrendLine.SetDefaultColor(Color.YELLOW);
TrendLine.SetLineWeight(2);
# ============================================
# SCORE LABELS (live dashboard)
# ============================================
AddLabel(yes, "Bull Score: " + totalBullScore,
if totalBullScore >= thresh then Color.LIME
else Color.DARK_GREEN);
AddLabel(yes, "Bear Score: " + totalBearScore,
if totalBearScore >= thresh then Color.MAGENTA
else Color.DARK_RED);
AddLabel(yes, "Threshold: " + thresh, Color.WHITE);
AddLabel(yes, "Lookback: " + lb, Color.WHITE);
AddLabel(yes, if trendOn then "Trend Filter: ON"
else "Trend Filter: OFF", Color.YELLOW);
AddLabel(resetToDefaults,
"RESET ACTIVE - All params at defaults", Color.YELLOW);
# Pattern toggle status
AddLabel(yes,
"Patterns: " +
(if enableThreeInsideUpDown then "InsideUpDn " else "") +
(if enableThreeSoldiersCrows then "Soldiers/Crows " else "") +
(if enableThreeMethods then "3Methods " else "") +
(if enableMorningEveningStar then "Star " else "") +
(if enablePiercingDarkCloud then "Pierce/Cloud " else ""),
Color.CYAN);
# Individual pattern labels
AddLabel(threeInsideUp, "3 Inside Up +2", Color.GREEN);
AddLabel(threeInsideDown, "3 Inside Down +2", Color.RED);
AddLabel(threeWhiteSoldiers, "3 Soldiers +2", Color.GREEN);
AddLabel(threeBlackCrows, "3 Crows +2", Color.RED);
AddLabel(risingThreeMethods, "Rising 3 Methods +2", Color.GREEN);
AddLabel(fallingThreeMethods, "Falling 3 Methods +2", Color.RED);
AddLabel(morningStar, "Morning Star +2", Color.GREEN);
AddLabel(eveningStar, "Evening Star +2", Color.RED);
AddLabel(piercingLine, "Piercing Line +1", Color.GREEN);
AddLabel(darkCloudCover, "Dark Cloud +1", Color.RED);
# Timeframe label
AddLabel(yes, "TF: " + (if aggPeriod == AggregationPeriod.MIN then "1m"
else if aggPeriod == AggregationPeriod.TWO_MIN then "2m"
else if aggPeriod == AggregationPeriod.THREE_MIN then "3m"
else if aggPeriod == AggregationPeriod.FIVE_MIN then "5m"
else if aggPeriod == AggregationPeriod.TEN_MIN then "10m"
else if aggPeriod == AggregationPeriod.FIFTEEN_MIN then "15m"
else if aggPeriod == AggregationPeriod.THIRTY_MIN then "30m"
else if aggPeriod == AggregationPeriod.HOUR then "1H"
else if aggPeriod == AggregationPeriod.TWO_HOURS then "2H"
else if aggPeriod == AggregationPeriod.FOUR_HOURS then "4H"
else if aggPeriod == AggregationPeriod.DAY then "D"
else if aggPeriod == AggregationPeriod.WEEK then "W"
else if aggPeriod == AggregationPeriod.MONTH then "M"
else "Other") + (if requireGap then " | Gap: On"
else " | Gap: Off"), Color.WHITE);
Attachments
Last edited by a moderator: