OPEN-SOURCE SCRIPT
Updated

TSD Quantum | mrmoeinudin

133
This indicator, **TSD Quantum**, has been developed by **Moeinudin Montazerfaraj**, exclusively for the students of the **TSD Intraday Trading Course**.

TSD Quantum is an intelligent and proprietary tool designed to accurately identify **valid supply and demand zones** and distinguish them from weak or linear bases. The algorithm is fully customized to align with the advanced concepts taught in the TSD methodology, empowering traders to make more confident and data-driven decisions.

### Key Features:
- Smart zone detection tailored to TSD methodology
- Floating risk management panel
- Integrated checklist for trade validation
- Real-time display of ATR, spread, and other essential metrics
- Optimized for intraday traders who follow price action and zone-based decision making strategies.

To join our exclusive educational programs, visit our official website:
🌐 [mrmoeinudin.com](mrmoeinudin.com)

Stay connected and follow us for insights, updates, and free educational content:
📌 Telegram: [t.me/mrmoeinudin](https://t.me/mrmoeinudin)
📌 YouTube: [youtube.com/@mrmoeinudin?si=Krd8oL7k__77oKKU]
📌 Instagram: [instagram.com/mrmoeinudin]

Wishing all Persian-speaking traders around the world continued success and growth in their trading journey. 🚀📈
Release Notes
//version=5
indicator("Rejection Candle Detector", shorttitle="RCD", overlay=true)

// ========== General Settings ==========
enable_indicator = input.bool(true, "Enable Indicator", group="General Settings")

// ========== Text Display Settings ==========
text_size = input.string("small", "Text Size", options=["tiny", "small", "normal", "large", "huge"], group="Text Display")
text_color = input.color(color.blue, "Text Color", group="Text Display")
text_position = input.string("above", "Text Position", options=["above", "below"], group="Text Display")

// ========== Minimum Range Settings (Pips) for 5 Minutes ==========
gold_5m_min_range = input.float(30, "Gold - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")
eurusd_5m_min_range = input.float(7, "EURUSD - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")
dax_5m_min_range = input.float(30, "DAX40 - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")

// ========== Minimum Range Settings (Pips) for 15 Minutes ==========
gold_15m_min_range = input.float(53, "Gold - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")
eurusd_15m_min_range = input.float(20, "EURUSD - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")
dax_15m_min_range = input.float(53, "DAX40 - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")

// ========== Body Percentage Settings ==========
body_percentage_5m = input.float(23, "Body Percentage - 5 Minutes (%)", minval=0, maxval=100, group="Body Percentage")
body_percentage_15m = input.float(23, "Body Percentage - 15 Minutes (%)", minval=0, maxval=100, group="Body Percentage")

// ========== Shadow Difference Settings ==========
shadow_difference_5m = input.float(80, "Shadow Difference - 5 Minutes (%)", minval=0, maxval=100, group="Shadow Settings", tooltip="Minimum difference percentage between upper and lower shadows")
shadow_difference_15m = input.float(80, "Shadow Difference - 15 Minutes (%)", minval=0, maxval=100, group="Shadow Settings", tooltip="Minimum difference percentage between upper and lower shadows")

// ========== Display Days Settings ==========
max_display_days = input.int(5, "Maximum Display Days", minval=1, maxval=365, group="Display Settings", tooltip="Maximum number of past days to show rejection candles")

// ========== Symbol Detection Function ==========
get_symbol_type() =>
symbol_name = str.upper(syminfo.ticker)
if str.contains(symbol_name, "XAU") or str.contains(symbol_name, "GOLD")
"GOLD"
else if str.contains(symbol_name, "EUR") and str.contains(symbol_name, "USD")
"EURUSD"
else if str.contains(symbol_name, "DAX") or str.contains(symbol_name, "GER") or str.contains(symbol_name, "DE")
"DAX"
else
"OTHER"

// ========== Pip Calculation Function ==========
calculate_pip_value(range_value, symbol_type) =>
if symbol_type == "GOLD"
range_value * 10
else if symbol_type == "EURUSD"
range_value * 10000
else if symbol_type == "DAX"
range_value
else
0

// ========== Text Size Function ==========
get_text_size() =>
switch text_size
"tiny" => size.tiny
"small" => size.small
"normal" => size.normal
"large" => size.large
"huge" => size.huge
=> size.small

// ========== Shadow Difference Calculation ==========
calculate_shadow_difference() =>
body_top = math.max(open, close)
body_bottom = math.min(open, close)

upper_shadow = high - body_top
lower_shadow = body_bottom - low

total_shadow = upper_shadow + lower_shadow

if total_shadow > 0
shadow_diff = math.abs(upper_shadow - lower_shadow) / total_shadow * 100
shadow_diff
else
0

// ========== Main Calculations ==========
current_symbol = get_symbol_type()
current_timeframe = timeframe.period

candle_range = high - low
candle_body = math.abs(close - open)
body_ratio = candle_body / candle_range * 100

range_in_pips = calculate_pip_value(candle_range, current_symbol)
shadow_diff_percentage = calculate_shadow_difference()

// ========== Determine Minimum Range and Body Percentage ==========
min_required_range = 0.0
required_body_percentage = 0.0
required_shadow_difference = 0.0

if current_symbol != "OTHER"
if current_timeframe == "5"
if current_symbol == "GOLD"
min_required_range := gold_5m_min_range
else if current_symbol == "EURUSD"
min_required_range := eurusd_5m_min_range
else if current_symbol == "DAX"
min_required_range := dax_5m_min_range
required_body_percentage := body_percentage_5m
required_shadow_difference := shadow_difference_5m
else if current_timeframe == "15"
if current_symbol == "GOLD"
min_required_range := gold_15m_min_range
else if current_symbol == "EURUSD"
min_required_range := eurusd_15m_min_range
else if current_symbol == "DAX"
min_required_range := dax_15m_min_range
required_body_percentage := body_percentage_15m
required_shadow_difference := shadow_difference_15m

// ========== Check if candle is within display range ==========
is_within_display_range() =>
// Calculate cutoff time: current time minus max_display_days
days_in_milliseconds = max_display_days * 24 * 60 * 60 * 1000
cutoff_time = timenow - days_in_milliseconds

// Return true if current bar time is after cutoff time
time >= cutoff_time

// ========== Rejection Candle Conditions ==========
symbol_condition = current_symbol != "OTHER"
timeframe_condition = current_timeframe == "5" or current_timeframe == "15"
range_condition = range_in_pips >= min_required_range
body_condition = body_ratio <= required_body_percentage
shadow_condition = shadow_diff_percentage >= required_shadow_difference
time_condition = is_within_display_range()

is_rejection_candle = enable_indicator and symbol_condition and timeframe_condition and range_condition and body_condition and shadow_condition and time_condition

// ========== Text Display ==========
if is_rejection_candle
label_y = text_position == "above" ? high + (high - low) * 0.1 : low - (high - low) * 0.1
label_style = text_position == "above" ? label.style_label_down : label.style_label_up

label.new(x=bar_index, y=label_y, text="RC", color=color.new(color.white, 100), textcolor=text_color, size=get_text_size(), style=label_style)
Release Notes
//version=5
indicator("Rejection Candle Detector", shorttitle="RCD", overlay=true)

// ========== General Settings ==========
enable_indicator = input.bool(true, "Enable Indicator", group="General Settings")

// ========== Text Display Settings ==========
text_size = input.string("small", "Text Size", options=["tiny", "small", "normal", "large", "huge"], group="Text Display")
text_color = input.color(color.blue, "Text Color", group="Text Display")
text_position = input.string("above", "Text Position", options=["above", "below"], group="Text Display")

// ========== Minimum Range Settings (Pips) for 5 Minutes ==========
gold_5m_min_range = input.float(30, "Gold - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")
eurusd_5m_min_range = input.float(7, "EURUSD - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")
dax_5m_min_range = input.float(30, "DAX40 - 5 Minutes (Pips)", minval=0, group="5 Minutes Settings")

// ========== Minimum Range Settings (Pips) for 15 Minutes ==========
gold_15m_min_range = input.float(53, "Gold - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")
eurusd_15m_min_range = input.float(20, "EURUSD - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")
dax_15m_min_range = input.float(53, "DAX40 - 15 Minutes (Pips)", minval=0, group="15 Minutes Settings")

// ========== Body Percentage Settings ==========
body_percentage_5m = input.float(23, "Body Percentage - 5 Minutes (%)", minval=0, maxval=100, group="Body Percentage")
body_percentage_15m = input.float(23, "Body Percentage - 15 Minutes (%)", minval=0, maxval=100, group="Body Percentage")

// ========== Shadow Difference Settings ==========
shadow_difference_5m = input.float(80, "Shadow Difference - 5 Minutes (%)", minval=0, maxval=100, group="Shadow Settings", tooltip="Minimum difference percentage between upper and lower shadows")
shadow_difference_15m = input.float(80, "Shadow Difference - 15 Minutes (%)", minval=0, maxval=100, group="Shadow Settings", tooltip="Minimum difference percentage between upper and lower shadows")

// ========== Display Days Settings ==========
max_display_days = input.int(5, "Maximum Display Days", minval=1, maxval=365, group="Display Settings", tooltip="Maximum number of past days to show rejection candles")

// ========== Symbol Detection Function ==========
get_symbol_type() =>
symbol_name = str.upper(syminfo.ticker)
if str.contains(symbol_name, "XAU") or str.contains(symbol_name, "GOLD")
"GOLD"
else if str.contains(symbol_name, "EUR") and str.contains(symbol_name, "USD")
"EURUSD"
else if str.contains(symbol_name, "DAX") or str.contains(symbol_name, "GER") or str.contains(symbol_name, "DE")
"DAX"
else
"OTHER"

// ========== Pip Calculation Function ==========
calculate_pip_value(range_value, symbol_type) =>
if symbol_type == "GOLD"
range_value * 10
else if symbol_type == "EURUSD"
range_value * 10000
else if symbol_type == "DAX"
range_value
else
0

// ========== Text Size Function ==========
get_text_size() =>
switch text_size
"tiny" => size.tiny
"small" => size.small
"normal" => size.normal
"large" => size.large
"huge" => size.huge
=> size.small

// ========== Shadow Difference Calculation ==========
calculate_shadow_difference() =>
body_top = math.max(open, close)
body_bottom = math.min(open, close)

upper_shadow = high - body_top
lower_shadow = body_bottom - low

total_shadow = upper_shadow + lower_shadow

if total_shadow > 0
shadow_diff = math.abs(upper_shadow - lower_shadow) / total_shadow * 100
shadow_diff
else
0

// ========== Main Calculations ==========
current_symbol = get_symbol_type()
current_timeframe = timeframe.period

candle_range = high - low
candle_body = math.abs(close - open)
body_ratio = candle_body / candle_range * 100

range_in_pips = calculate_pip_value(candle_range, current_symbol)
shadow_diff_percentage = calculate_shadow_difference()

// ========== Determine Minimum Range and Body Percentage ==========
min_required_range = 0.0
required_body_percentage = 0.0
required_shadow_difference = 0.0

if current_symbol != "OTHER"
if current_timeframe == "5"
if current_symbol == "GOLD"
min_required_range := gold_5m_min_range
else if current_symbol == "EURUSD"
min_required_range := eurusd_5m_min_range
else if current_symbol == "DAX"
min_required_range := dax_5m_min_range
required_body_percentage := body_percentage_5m
required_shadow_difference := shadow_difference_5m
else if current_timeframe == "15"
if current_symbol == "GOLD"
min_required_range := gold_15m_min_range
else if current_symbol == "EURUSD"
min_required_range := eurusd_15m_min_range
else if current_symbol == "DAX"
min_required_range := dax_15m_min_range
required_body_percentage := body_percentage_15m
required_shadow_difference := shadow_difference_15m

// ========== Check if candle is within display range ==========
is_within_display_range() =>
// Calculate cutoff time: current time minus max_display_days
days_in_milliseconds = max_display_days * 24 * 60 * 60 * 1000
cutoff_time = timenow - days_in_milliseconds

// Return true if current bar time is after cutoff time
time >= cutoff_time

// ========== Rejection Candle Conditions ==========
symbol_condition = current_symbol != "OTHER"
timeframe_condition = current_timeframe == "5" or current_timeframe == "15"
range_condition = range_in_pips >= min_required_range
body_condition = body_ratio <= required_body_percentage
shadow_condition = shadow_diff_percentage >= required_shadow_difference
time_condition = is_within_display_range()

is_rejection_candle = enable_indicator and symbol_condition and timeframe_condition and range_condition and body_condition and shadow_condition and time_condition

// ========== Text Display ==========
if is_rejection_candle
label_y = text_position == "above" ? high + (high - low) * 0.1 : low - (high - low) * 0.1
label_style = text_position == "above" ? label.style_label_down : label.style_label_up

label.new(x=bar_index, y=label_y, text="RC", color=color.new(color.white, 100), textcolor=text_color, size=get_text_size(), style=label_style)
Release Notes
//version=5
indicator("Hammer & Doji Detector", shorttitle="Hammer & Doji", overlay=true)

// Input settings
doji_threshold = input.float(0.1, "Doji Threshold (%)", minval=0.01, maxval=1.0, step=0.01)
hammer_ratio = input.float(2.0, "Hammer Shadow Ratio", minval=1.5, maxval=5.0, step=0.1)

// Calculate candle dimensions
body_size = math.abs(close - open)
total_range = high - low
upper_shadow = high - math.max(open, close)
lower_shadow = math.min(open, close) - low

// Doji identification conditions
is_doji = body_size <= (total_range * doji_threshold / 100) and total_range > 0

// Hammer identification conditions
is_hammer = (lower_shadow >= body_size * hammer_ratio) and (upper_shadow <= body_size * 0.5) and total_range > 0 and body_size > 0

// Inverted Hammer identification conditions
is_inverted_hammer = (upper_shadow >= body_size * hammer_ratio) and (lower_shadow <= body_size * 0.5) and total_range > 0 and body_size > 0

// Color candles
barcolor(is_doji ? color.yellow : is_hammer ? color.blue : is_inverted_hammer ? color.purple : na, title="Candle Colors")

// Display labels
if is_doji
label.new(bar_index, high + (high - low) * 0.1, "DOJI", color=color.yellow, textcolor=color.black, style=label.style_label_down, size=size.small)

if is_hammer
label.new(bar_index, low - (high - low) * 0.1, "HAMMER", color=color.blue, textcolor=color.white, style=label.style_label_up, size=size.small)

if is_inverted_hammer
label.new(bar_index, high + (high - low) * 0.1, "INV HAMMER", color=color.purple, textcolor=color.white, style=label.style_label_down, size=size.small)

// Display shapes on chart
plotshape(is_doji, title="Doji Marker", location=location.abovebar, color=color.yellow, style=shape.diamond, size=size.small)
plotshape(is_hammer, title="Hammer Marker", location=location.belowbar, color=color.blue, style=shape.triangleup, size=size.small)
plotshape(is_inverted_hammer, title="Inverted Hammer Marker", location=location.abovebar, color=color.purple, style=shape.triangledown, size=size.small)

// Alerts
alertcondition(is_doji, title="Doji Detected", message="Doji candlestick pattern detected")
alertcondition(is_hammer, title="Hammer Detected", message="Hammer candlestick pattern detected")
alertcondition(is_inverted_hammer, title="Inverted Hammer Detected", message="Inverted Hammer candlestick pattern detected")

// Information table
var table info_table = table.new(position.top_right, 2, 4, bgcolor=color.white, border_width=1)
if barstate.islast
table.cell(info_table, 0, 0, "Pattern", text_color=color.black, bgcolor=color.gray)
table.cell(info_table, 1, 0, "Status", text_color=color.black, bgcolor=color.gray)
table.cell(info_table, 0, 1, "Doji", text_color=color.black)
table.cell(info_table, 1, 1, is_doji ? "✓" : "✗", text_color=is_doji ? color.green : color.red)
table.cell(info_table, 0, 2, "Hammer", text_color=color.black)
table.cell(info_table, 1, 2, is_hammer ? "✓" : "✗", text_color=is_hammer ? color.green : color.red)
table.cell(info_table, 0, 3, "Inv Hammer", text_color=color.black)
table.cell(info_table, 1, 3, is_inverted_hammer ? "✓" : "✗", text_color=is_inverted_hammer ? color.green : color.red)

Disclaimer

The information and publications are not meant to be, and do not constitute, financial, investment, trading, or other types of advice or recommendations supplied or endorsed by TradingView. Read more in the Terms of Use.