// This script is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// © QuantAlgo
//@version=6
indicator('Advanced Momentum Scanner [QuantAlgo]', overlay = true)
// ╔════════════════════════════════╗ //
// ║ USER-DEFINED SETTINGS ║ //
// ╚════════════════════════════════╝ //
// Input Groups
var string ema_settings = "════════ EMA Settings ════════"
var string momentum_settings = "════════ Momentum Settings ════════"
var string sensitivity_settings = "════════ Sensitivity Settings ════════"
var string visual_settings = "════════ Visual Settings ════════"
// Tooltips
tooltip_ultra_fast = "Ultra-fast EMA period for quick trend changes. Default: 5. Lower values (3-5) increase sensitivity and generate more signals but may cause false triggers. Higher values (7-10) create more stable but delayed signals. Use lower values in volatile markets."
tooltip_fast = "Fast EMA period for short-term trend detection. Default: 10. Lower values (8-12) create more responsive signals for active trading. Higher values (15-20) reduce noise and false signals. Should be at least double the ultra-fast period value."
tooltip_mid = "Mid-term EMA period that helps confirm trend direction. Default: 30. Lower values (20-30) work better for short-term trading. Higher values (40-60) better for swing trading. Should be 2-3x the fast period."
tooltip_slow = "Slow EMA period for long-term trend identification. Default: 100. Lower values (60-90) are more suitable for medium-term trends. Higher values (120-200) better identify major market cycles. Increasing creates smoother but delayed signals."
tooltip_source = "Price data to analyze. Default: Close. Close reflects completed price action, while HL2/HLC3 can better represent the true average price movement. High/Low are more volatile and typically not recommended."
tooltip_roc = "Period for Rate of Change calculation. Default: 5. Lower values (3-5) create more sensitive momentum detection. Higher values (7-10) provide smoother but delayed momentum signals. Should be similar to the ultra-fast EMA value."
tooltip_threshold = "Minimum percentage change required to consider valid momentum. Default: 0.5%. Lower values (0.1-0.3%) detect subtle momentum changes but may increase false signals. Higher values (0.8-1.5%) require stronger momentum for confirmation."
tooltip_sensitivity = "Determines how quickly the indicator responds to trend changes. Default: Balanced. Conservative mode reduces false signals but is slower. Aggressive mode detects changes earlier but with more false signals. Balanced offers a middle ground."
tooltip_early = "Enable/disable detection of early trend signals before full confirmation. Default: On. When enabled, potential trend changes are detected earlier but with lower reliability. Disable for fewer but more reliable signals."
tooltip_up_color = "Color for bullish/uptrend conditions. Brighter colors are more visible but can be distracting. Consider darker shades for less visual impact. Ideally matches your other bullish indicators for consistency."
tooltip_down_color = "Color for bearish/downtrend conditions. Brighter colors are more visible but can be distracting. Consider darker shades for less visual impact. Ideally matches your other bearish indicators for consistency."
tooltip_neutral_color = "Color when no clear trend exists. Lighter grays are less noticeable, darker grays more prominent. Choose based on your chart background - avoid colors that blend with the background."
tooltip_transparency = "Visual transparency of colors and fills (0-100). Default: 80. Lower values (40-60) create more prominent visuals. Higher values (85-95) are subtler. Adjust based on your chart background and preferences."
tooltip_strength_panel = "Show/hide the trend strength information panel. Default: On. The panel displays trend percentage strength and early detection signals. Disable if you prefer a cleaner chart."
tooltip_bg = "Enable/disable gradient background coloring based on trend. Default: Off. When enabled, the chart background color changes with trend direction. Can be visually helpful but potentially distracting."
tooltip_bands = "Enable/disable gradient bands around price action. Default: On. Bands visualize the potential trend channels and help identify reversals. Disable for a cleaner chart appearance."
tooltip_bars = "Enable/disable coloring of price bars based on trend. Default: On. Color-coding bars makes trend direction immediately visible. Disable if you prefer standard bar colors."
tooltip_position = "Position of the trend panel on the chart. Default: Bottom right. Top positions may interfere with price data in recent bars but are closer to current action. Bottom positions leave price action visible but require looking further from current prices."
// EMA Settings
ultra_fast = input.int(5, title = 'Ultra-Fast EMA',
minval = 1,
group = ema_settings,
tooltip = tooltip_ultra_fast)
fast = input.int(10, title = 'Fast EMA',
minval = 1,
group = ema_settings,
tooltip = tooltip_fast)
mid = input.int(30, title = 'Mid EMA',
minval = 1,
group = ema_settings,
tooltip = tooltip_mid)
slow = input.int(100, title = 'Slow EMA',
minval = 1,
group = ema_settings,
tooltip = tooltip_slow)
series float price = input.source(close, 'Price Source',
group = ema_settings,
tooltip = tooltip_source)
// Momentum Settings
roc_period = input.int(5, title = 'RoC Period',
minval = 1,
group = momentum_settings,
tooltip = tooltip_roc)
mom_threshold = input.float(0.5, title = 'Momentum Threshold (%)',
step = 0.1,
group = momentum_settings,
tooltip = tooltip_threshold)
// Sensitivity Settings
mode = input.string('Balanced', 'Sensitivity Mode',
options = ['Conservative', 'Balanced', 'Aggressive'],
group = sensitivity_settings,
tooltip = tooltip_sensitivity)
early_signals = input.bool(true, title = 'Early Signals',
group = sensitivity_settings,
tooltip = tooltip_early)
// Visual Settings
up_color = input.color(#00ffaa, 'Uptrend',
group = visual_settings,
tooltip = tooltip_up_color)
down_color = input.color(#ff0000, 'Downtrend',
group = visual_settings,
tooltip = tooltip_down_color)
neutral_color = input.color(#888888, 'Neutral',
group = visual_settings,
tooltip = tooltip_neutral_color)
transparency = input.int(80, 'Transparency',
group = visual_settings,
tooltip = tooltip_transparency)
show_panel = input.bool(true, title = 'Show Trend Panel',
group = visual_settings,
tooltip = tooltip_strength_panel)
use_bands = input.bool(true, title = 'Show Bands',
group = visual_settings,
tooltip = tooltip_bands)
color_bars = input.bool(true, title = 'Color Bars',
group = visual_settings,
tooltip = tooltip_bars)
use_bg = input.bool(false, title = 'Show Background',
group = visual_settings,
tooltip = tooltip_bg)
panel_pos = input.string('Bottom Right', 'Trend Panel Position',
options = ['Top Left', 'Top Center', 'Top Right', 'Middle Left', 'Middle Center', 'Middle Right', 'Bottom Left', 'Bottom Center', 'Bottom Right'],
group = visual_settings,
tooltip = tooltip_position)
// Constant settings
const int atr_len = 14
const float vol_thresh = 1.5
const int rsi_len = 10
const int rsi_high = 70
const int rsi_low = 30
const bool use_volume = true
const int vol_len = 20
const int bar_transp = 40
const int max_intensity = 20
// ╔════════════════════════════════╗ //
// ║ CALCULATIONS ║ //
// ╚════════════════════════════════╝ //
// EMAs
ema_ultra = ta.ema(price, ultra_fast)
ema_fast = ta.ema(price, fast)
ema_mid = ta.ema(price, mid)
ema_slow = ta.ema(price, slow)
// EMA slopes for trend momentum
slope_ultra = (ema_ultra - ema_ultra[1]) / ema_ultra[1] * 100
slope_fast = (ema_fast - ema_fast[1]) / ema_fast[1] * 100
slope_mid = (ema_mid - ema_mid[1]) / ema_mid[1] * 100
// Rate of Change for early trend detection
roc = (close - close[roc_period]) / close[roc_period] * 100
roc_smooth = ta.sma(roc, 3) // Smoothed RoC
// Volatility (ATR)
atr = ta.atr(atr_len)
norm_atr = atr / close * 100
// Dynamic volatility threshold based on sensitivity mode
var float dyn_thresh = vol_thresh
if mode == 'Conservative'
dyn_thresh := vol_thresh * 1.2
else if mode == 'Aggressive'
dyn_thresh := vol_thresh * 0.8
else
dyn_thresh := vol_thresh
// Adaptive volatility filter
is_volatile = norm_atr > ta.sma(norm_atr, atr_len) * dyn_thresh
// Volume
vol_avg = ta.sma(volume, vol_len)
high_vol = volume > vol_avg * 1.5
// RSI for momentum
rsi = ta.rsi(close, rsi_len)
// Early Trend Direction (more sensitive)
early_up = ema_ultra > ema_fast and slope_fast > 0 and roc_smooth > mom_threshold
early_down = ema_ultra < ema_fast and slope_fast < 0 and roc_smooth < -mom_threshold
// Confirmed Trend Direction (more reliable)
confirm_up = ema_fast > ema_mid and ema_mid > ema_slow and close > ema_fast
confirm_down = ema_fast < ema_mid and ema_mid < ema_slow and close < ema_fast
// Final Trend Direction (based on sensitivity mode)
bool trend_up = false
bool trend_down = false
bool trend_neutral = false
if mode == 'Conservative'
trend_up := confirm_up
trend_down := confirm_down
trend_neutral := not confirm_up and not confirm_down
else if mode == 'Aggressive'
trend_up := early_up or ema_ultra > ema_fast and confirm_up
trend_down := early_down or ema_ultra < ema_fast and confirm_down
trend_neutral := not trend_up and not trend_down
else // Balanced
trend_up := early_up and ema_fast > ema_mid or confirm_up
trend_down := early_down and ema_fast < ema_mid or confirm_down
trend_neutral := not trend_up and not trend_down
// Trend Strength Calculation (0 to 100 scale)
ema_dist = math.abs(ema_fast - ema_slow) / ema_slow * 100
rsi_comp = trend_up ? math.max(rsi - 50, 0) * 2 : trend_down ? math.max(50 - rsi, 0) * 2 : 0
vol_comp = math.min(norm_atr / 2, 25) // Cap at 25% contribution
volume_comp = use_volume and high_vol ? 25 : 0
mom_comp = math.abs(roc_smooth) * 2 // Add momentum component
// Enhanced trend strength calculation
trend_strength = math.min(ema_dist * 1.5 + rsi_comp + vol_comp + volume_comp + mom_comp, 100)
// Early trend strength (used for early signals)
early_strength = math.abs(slope_ultra * 3) + math.abs(roc_smooth * 2)
// Early Entry Signals (more sensitive)
early_long = early_up and not trend_down and roc_smooth > mom_threshold and rsi > 45 and rsi < rsi_high - 5
early_short = early_down and not trend_up and roc_smooth < -mom_threshold and rsi < 55 and rsi > rsi_low + 5
// Confirmed Entry Signals (more reliable)
confirm_long = trend_up and rsi > 50 and rsi < rsi_high and is_volatile and (not use_volume or high_vol)
confirm_short = trend_down and rsi < 50 and rsi > rsi_low and is_volatile and (not use_volume or high_vol)
// Final Entry Signals (based on sensitivity settings)
bool long_signal = false
bool short_signal = false
if early_signals
if mode == 'Conservative'
long_signal := confirm_long or early_long and ema_fast > ema_mid and early_strength > 30
short_signal := confirm_short or early_short and ema_fast < ema_mid and early_strength > 30
else if mode == 'Aggressive'
long_signal := early_long or confirm_long
short_signal := early_short or confirm_short
else // Balanced
long_signal := confirm_long or early_long and early_strength > 20
short_signal := confirm_short or early_short and early_strength > 20
else
long_signal := confirm_long
short_signal := confirm_short
// Exit Signals - made more adaptive
exit_long = confirm_down or ema_ultra < ema_fast and ema_fast < ema_mid or rsi > rsi_high
exit_short = confirm_up or ema_ultra > ema_fast and ema_fast > ema_mid or rsi < rsi_low
// Signal state transitions
var in_long = false
var in_short = false
if long_signal and not in_long
in_long := true
in_short := false
if short_signal and not in_short
in_short := true
in_long := false
if exit_long and in_long
in_long := false
if exit_short and in_short
in_short := false
// Simple trend change detection for better signals
var prev_up = false
var prev_down = false
// Detect true state changes of the trend wave (simpler trend-based signal)
bool trend_up_change = trend_up and (not prev_up)
bool trend_down_change = trend_down and (not prev_down)
bool trend_neutral_change = trend_neutral and (not trend_neutral[1])
// Update previous states for next bar
prev_up := trend_up
prev_down := trend_down
// ╔════════════════════════════════╗ //
// ║ VISUALIZATION ║ //
// ╚════════════════════════════════╝ //
// Gradient background intensity tracking
var float bg_intensity = 0.0
var int prev_trend = 0
// Detect trend state changes
bool trend_changed = trend_up and prev_trend != 1 or trend_down and prev_trend != -1 or trend_neutral and prev_trend != 0
// Reset intensity on trend change
if trend_changed
bg_intensity := 0.0
prev_trend := trend_up ? 1 : trend_down ? -1 : 0
// Increment intensity based on trend
bg_intensity := math.min(bg_intensity + 1, max_intensity)
// Calculate bands that cover the price bars automatically
calcBands() =>
lookback = 10 // Number of bars to look back for determining range
high_price = ta.highest(high, lookback)
low_price = ta.lowest(low, lookback)
// Calculate the total range
total_range = high_price - low_price
// Create bands that divide this range
upper3 = high_price
upper2 = high_price - total_range * 0.2
upper1 = high_price - total_range * 0.4
// Calculate middle point for centered line
middle = (high_price + low_price) / 2
lower1 = low_price + total_range * 0.4
lower2 = low_price + total_range * 0.2
lower3 = low_price
[upper3, upper2, upper1, middle, lower1, lower2, lower3]
[upper3, upper2, upper1, middle_band, lower1, lower2, lower3] = calcBands()
// Colors based on trend direction
color fill_color = trend_up ? up_color : trend_down ? down_color : neutral_color
// Calculate gradient colors based on intensity
color gradient_color = trend_up ? color.from_gradient(bg_intensity, 0, max_intensity, color.new(up_color, transparency), color.new(up_color, 95)) :
trend_down ? color.from_gradient(bg_intensity, 0, max_intensity, color.new(down_color, transparency), color.new(down_color, 95)) :
color.from_gradient(bg_intensity, 0, max_intensity, color.new(neutral_color, transparency), color.new(neutral_color, 95))
// Use gradient or solid color based on user preference
color bg_color = use_bg ? gradient_color : color.new(fill_color, transparency)
color trend_color = use_bands ? gradient_color : color.new(fill_color, transparency)
// Apply background gradient if enabled
bgcolor(use_bg ? gradient_color : na)
// Apply color to price bars based on trend
barcolor(color_bars ? trend_up ? color.new(up_color, bar_transp) :
color_bars and trend_down ? color.new(down_color, bar_transp) :
color.new(neutral_color, bar_transp) : chart.fg_color)
// Plot the main trend line with simple up/down/neutral color
plot(middle_band, 'Trend',
color = trend_up ? up_color : trend_down ? down_color : neutral_color,
linewidth = 4)
// Always create the plots but control visibility with display property
up3 = plot(upper3, 'Upper Band 3', display = display.none, editable = true)
up2 = plot(upper2, 'Upper Band 2', display = display.none, editable = true)
up1 = plot(upper1, 'Upper Band 1', display = display.none, editable = true)
lw1 = plot(lower1, 'Lower Band 1', display = display.none, editable = true)
lw2 = plot(lower2, 'Lower Band 2', display = display.none, editable = true)
lw3 = plot(lower3, 'Lower Band 3', display = display.none, editable = true)
// Create fills with color controlled by the use_bands setting
fill_color2 = use_bands ? trend_color : na
fill(up3, lw3, fill_color2)
fill(up2, lw2, fill_color2)
fill(up1, lw1, fill_color2)
// Trend text and colors
trend_text = trend_up ? 'UPTREND' : trend_down ? 'DOWNTREND' : 'NEUTRAL'
color current_color = trend_up ? up_color : trend_down ? down_color : #555555
// Function to determine if a color is dark or light for contrast
isDarkColor(c) =>
// Extract RGB components
r = color.r(c)
g = color.g(c)
b = color.b(c)
// Formula to determine brightness (weighted RGB)
luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
// Return true if dark, false if light
luminance < 0.5
// Function to convert string position to position.* constant
getTablePosition(posStr) =>
switch posStr
'Top Left' => position.top_left
'Top Center' => position.top_center
'Top Right' => position.top_right
'Middle Left' => position.middle_left
'Middle Center' => position.middle_center
'Middle Right' => position.middle_right
'Bottom Left' => position.bottom_left
'Bottom Center' => position.bottom_center
'Bottom Right' => position.bottom_right
=> position.bottom_right // default
// Trend Strength Panel with customizable position
if barstate.islast and show_panel
color frame_color = color.white
table_pos = getTablePosition(panel_pos)
var trend_panel = table.new(table_pos, 1, 5, bgcolor = color.new(color.black, 0), frame_color = frame_color, frame_width = 4, border_width = 2, border_color = frame_color)
early_text = ''
if early_up and not confirm_up
early_text := 'Early Uptrend Detected'
else if early_down and not confirm_down
early_text := 'Early Downtrend Detected'
else
early_text := 'No Early Signals' // Default text when no early trend detected
trend_text_color = isDarkColor(current_color) ? color.white : color.black
info_text_color = color.white
table.cell(trend_panel, 0, 1, 'Mode: ' + mode, bgcolor = #555555, text_color = info_text_color)
table.cell(trend_panel, 0, 2, trend_text + ': ' + str.tostring(trend_strength, '#.0') + '%', bgcolor = current_color, text_color = trend_text_color)
if early_text == 'Early Uptrend Detected' or early_text == 'Early Downtrend Detected'
table.cell(trend_panel, 0, 3, early_text, bgcolor = current_color, text_color = trend_text_color)
else
table.cell(trend_panel, 0, 3, early_text, bgcolor = #555555, text_color = info_text_color)
// ╔════════════════════════════════╗ //
// ║ ALERTS ║ //
// ╚════════════════════════════════╝ //
alertcondition(trend_up_change,
title="Detect Uptrend",
message="Uptrend Signal {{exchange}}:{{ticker}}")
alertcondition(trend_down_change,
title="Detect Downtrend",
message="Downtrend Signal {{exchange}}:{{ticker}}")
alertcondition(trend_neutral and not trend_neutral[1],
title="Detect Neutral State",
message="Neutral State {{exchange}}:{{ticker}}")
alertcondition(early_up and not confirm_up,
title="Detect Early Uptrend",
message="Early Uptrend Signal {{exchange}}:{{ticker}}")
alertcondition(early_down and not confirm_down,
title="Detect Early Downtrend",
message="Early Downtrend Signal {{exchange}}:{{ticker}}")
// ╔════════════════════════════════╗ //
// ║ CREATED BY ║ //
// ╚════════════════════════════════╝ //
// ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗ █████╗ ██╗ ██████╗ ██████╗
//██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝ ██╔══██╗██║ ██╔════╝ ██╔═══██╗
//██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ███████║██║ ██║ ███╗██║ ██║
//██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ██╔══██║██║ ██║ ██║██║ ██║
//╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ██║ ██║███████╗╚██████╔╝╚██████╔╝
// ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝
Recent Comments