Understanding the concepts of Premium and Discount zones, as well as Break of Structure (BoS) and Change of Character (CHoCH), is crucial for traders using the Smart Money Oscillator. These concepts are rooted in market structure analysis, which involves studying price levels and movements.
Premium Zone is where the price is considered to be relatively high or 'overbought'. In this zone, prices have risen significantly and may indicate that the asset is becoming overvalued, potentially leading to a reversal or slowdown in the upward trend.
The Discount Zone represents a 'discount' or 'oversold' area. Here, prices have fallen substantially, suggesting that the asset might be undervalued. This could be an indicator of a potential upward reversal or a pause in the downward trend.
Break of Structure (BoS) is about the continuation of a trend. In a bullish trend, a BoS is identified by the break of a recent higher high. In a bearish trend, it's the break of a recent Lower Low. BoS indicates that the trend is strong and likely to continue in its current direction. It's a sign of strength in the prevailing trend, whether up or down.
Change of Character (CHoCH) is an indication of a potential end to a trend. It occurs when there's a significant change in the market's behavior, contradicting the current trend. For example, in an uptrend characterized by higher highs and higher lows, a CHoCH may occur if a new high is formed but then is followed by an impulsive move downwards. This suggests that the bullish trend may be weakening and a bearish reversal could be imminent. CHoCH is essentially a sign of trend exhaustion and potential reversal.
With each consecutive BoS, the signal line of the oscillator will deepen in color. This allows you to visually see the strength of the current trend. The maximum strength of the trend is found by keeping track of the maximum number of consecutive BoS's within a window of 10. This calculation excludes periods without any BoS's to allow for a more stable max.
Quick Update is a feature that implements a more aggressive algorithm to update the highs and lows. Instead of updating the pivot points exclusively to update the range levels, it will attempt to use the current historical highs/lows to update the bounds. This results in a more responsive range at the cost of stability. There are pros and cons for both settings. With Quick Update disabled, the indicator will allow for strong reversals to register without the indicator maxing out. With Quick Update enabled, the indicator will show shorter term extremes with the risk of the signal being pinned to the extremities during strong trends or large movements. With Quick Update disabled, the oscillator prioritizes stability, using a more historical perspective to set its bounds. When Quick Update is enabled, the oscillator becomes more responsive, adjusting its bounds rapidly to reflect the latest market movements.
The Scale Offset feature allows the indicator to break the boundaries of the oscillator. This can be useful when the market is breaking highs or lows allowing the user to identify extremities in price. With Scale Offset disabled the oscillator will always remain inside of the boundaries because the extremities will be updated instantly. When this feature is enabled it will update the boundaries one step behind instead of updating it instantly. This allows the user to more easily see overbought and oversold conditions at the cost of incurring a single bar lag to the boundaries. Generally this is a good idea as this behavior makes the oscillator more sensitive to recent price spikes or drops, reflecting sudden market movements more accurately. It accentuates the extremities of the market conditions, potentially offering a more aggressive analysis. The main trade-off with the Scale Offset feature is between sensitivity and potential overreaction. It offers a more immediate and exaggerated reflection of market conditions but might also lead to misinterpretations in certain scenarios, especially in highly volatile markets.
Divergence is used to predict potential trend reversals. It occurs when the price of an asset and the reading of an oscillator move in opposite directions. This discrepancy can signal a weakening of the current trend and possibly indicate a potential reversal.
Divergence doesn't always lead to a trend reversal, but it's a warning sign that the current trend might be weakening. Divergence can sometimes give false signals, particularly in strongly trending markets where the oscillator may remain in overbought or oversold conditions for extended periods. The lagging nature of using pivot points to calculate divergences means that all divergences are limited by the pivot look forward input. The upside of using a longer look forward is that the divergences will be more accurate. The obvious con here is that it will be more delayed and might be useless by the time it appears. Its recommended to use the built in divergences as a way to learn how these are formed so you can make your own in real time.
By default, the oscillator uses a smoothing of 3 to allow for a more price like behavior while still being rather smooth compared to raw price data. Conversely, you can increase this value to make this indicator behave smoother. Something to keep in mind is that the amount of delay from real time is equal to half of the smoothing period.
We have included a verity of alerts in this indicator. Here is a list of all of the available alerts: Bullish BOS, Bearish BOS, Bullish CHoCH, Bearish CHoCH, Bullish Divergence, Hidden Bullish Divergence, Bearish Divergence, Hidden Bearish Divergence, Cross Over Average, Cross Under Average.
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ChartPrime
//@version=5
indicator("Smart Money Oscillator [ChartPrime]", overlay = false, max_labels_count = 500, max_lines_count = 500)
smoothing = input.int(
10
, "Smoothing"
, minval = 1
, group = "Settings"
, tooltip = "Specifies the degree of smoothing applied to the oscillator. Higher values result in smoother but
potentially less responsive signals."
)
average = input.int(
20
, "Average Length"
, minval = 1
, group = "Settings"
, tooltip = "Sets the length of the moving average applied to the oscillator, affecting its sensitivity and smoothness."
)
pivot_length = input.int(
30
, "Pivot Length"
, minval = 1
, group = "Settings"
, tooltip = "Determines the length of the pivot points used in calculating the oscillator. Longer lengths can provide
a more stable but less responsive signal."
)
pivot_length_forward = input.int(
20
, "Pivot Forward Length"
, minval = 1
, group = "Settings"
, tooltip = "Specifies the forward-looking length for pivot points, affecting how the oscillator anticipates
future price movements. This directly impacts the delay in finding a pivot."
)
max_high_length = input.int(
20
, "Max Length"
, minval = 1
, group = "Settings"
, tooltip = "Sets the maximum length to consider for calculating the highest values in the oscillator."
)
min_low_length = input.int(
20
, "Min Length"
, minval = 1
, group = "Settings"
, tooltip = "Defines the minimum length for calculating the lowest values in the oscillator."
)
quick_update = input.bool(
false
, "Quick Update"
, group = "Settings"
, tooltip = "Activates a faster update mode for the oscillator's extremities, which may result in less stable
range boundaries."
)
accentuate = input.bool(
true
, "Scale Offset"
, group = "Settings"
, tooltip = "When enabled, delays updating minimum and maximum values to enhance signal directionality, allowing the
signal to occasionally exceed normal bounds."
)
candle_color = input.bool(
false
, "Candle Color"
, group = "Settings"
, tooltip = "Enables coloring of candles based on the current directional signal of the oscillator."
)
enable_labels = input.bool(
true
, "Enable BOS/CHoCH Labels"
, group = "Labels"
, tooltip = "Activates the display of BOS (Break of Structure) and CHoCH (Change of Character) labels on the chart."
)
enable_visual_padding = input.bool(
true
, "Visual Padding"
, group = "Labels"
, inline = "Padding"
)
padding_level = input.int(
30
, ""
, minval = 0
, group = "Labels"
, inline = "Padding"
, tooltip = "Turns on additional visual padding at the top and bottom of the chart to accommodate labels.\n
Determines the amount of visual padding added to the chart for label display."
)
right = input.int(
20
, "Divergence Pivot"
, minval = 1
, group = "Divergence"
, tooltip = "Defines the number of bars to the right of the pivot in divergence calculations, influencing the
oscillator's responsiveness."
)
left = input.int(
20
, "Divergence Pivot Forward"
, minval = 1
, group = "Divergence"
, tooltip = "Directly impacts latency. Longer periods results in more accurate results at the sacrifice of delay."
)
upper_range = input.int(
500
, "Upper Range"
, minval = 0
, group = "Divergence"
, tooltip = "Sets the upper range limit for divergence calculations, influencing the oscillator's sensitivity to
larger trends."
)
lower_range = input.int(
25
, "Lower Range"
, minval = 0
, group = "Divergence"
, tooltip = "Determines the lower range limit for divergence calculations, affecting the oscillator's sensitivity to
shorter trends."
)
labels = input.string(
"Symbol"
, "Divergence Label Style"
, ["Text", "Symbol"]
, group = "Divergence"
, tooltip = "Allows selection of the label style for divergence indicators, with options for text or symbolic
representation."
)
enable_regular_bullish = input.bool(
true
, "Regular Bullish"
, group = "Divergence"
, tooltip = "Activates the detection and marking of regular bullish divergences in the oscillator."
)
enable_hidden_bullish = input.bool(
false
, "Hidden Bullish"
, group = "Divergence"
, tooltip = "Enables the identification and display of hidden bullish divergences."
)
enable_regular_bearish = input.bool(
true
, "Regular Bearish"
, group = "Divergence"
, tooltip = "Turns on the feature to detect and highlight regular bearish divergences."
)
enable_hidden_bearish = input.bool(
false
, "Hidden Bearish"
, group = "Divergence"
, tooltip = "Activates the functionality for detecting and displaying hidden bearish divergences."
)
bullish_color_min = input.color(
#52FF52
, "Bullish"
, group = "Color"
, inline = "Bullish"
)
bullish_color_max = input.color(
#00AA00
, ""
, group = "Color"
, inline = "Bullish"
, tooltip = "Determines the minimum/maximum color gradient for bullish signals, impacting the chart's visual appearance."
)
bearish_color_min = input.color(
#FF5252
, "Bearish"
, group = "Color"
, inline = "Bearish"
)
bearish_color_max = input.color(
#AA0000
, ""
, group = "Color"
, inline = "Bearish"
, tooltip = "Defines the minimum/maximum color gradient for bearish signals, affecting their visual representation."
)
average_color = input.color(
#ff9a16
, "Average"
, group = "Color"
, tooltip = "Specifies the color for the average line of the oscillator, enhancing chart readability."
)
choch_bullish_color = input.color(
#22ff22
, "CHoCH"
, group = "Color"
, inline = "CHoCH"
)
choch_bearish_color = input.color(
#FF2222
, ""
, group = "Color"
, inline = "CHoCH"
, tooltip = "Sets the color for bullish/bearish CHoCH (Change of Character) signals."
)
premium_color = input.color(
#FF2222
, "Premium/Discount"
, group = "Color"
, inline = "Boundry"
)
discount_color = input.color(
#22ff22
, ""
, group = "Color"
, inline = "Boundry"
, tooltip = "Determines the color for the premium/discount zone in the oscillator's visual representation."
)
text_color = input.color(
#EEEEEE
, "Text Color"
, group = "Color"
, tooltip = "Sets the color for the text in BoS/CHoCH labels"
)
regular_bullish_color = input.color(
#55FF77
, "Regular Bullish"
, group = "Color"
, tooltip = "Defines the color used to represent regular bullish divergences."
)
hidden_bullish_color = input.color(
#5577FF
, "Hidden Bullish"
, group = "Color"
, tooltip = "Specifies the color for hidden bullish divergences."
)
regular_bearish_color = input.color(
#FF3333
, "Regular Bearish"
, group = "Color"
, tooltip = "Sets the color for regular bearish divergence signals."
)
hidden_bearish_color = input.color(
#FF8833
, "Hidden Bearish"
, group = "Color"
, tooltip = "Determines the color for hidden bearish divergences."
)
text_color_div = input.color(
#000000
, "Divergence Text Color"
, group = "Color"
, tooltip = "Specifies the color for the text in divergence labels."
)
type nibble
bool a = na
bool b = na
bool c = na
bool d = na
in_range(bool cond, int lower_range, int upper_range) =>
bars = ta.barssince(cond == true)
lower_range <= bars and bars <= upper_range
divergence(
float source,
int right,
int left,
int upper_range,
int lower_range,
bool enable_regular_bullish,
color regular_bullish_color,
bool enable_hidden_bullish,
color hidden_bullish_color,
bool enable_regular_bearish,
color regular_bearish_color,
bool enable_hidden_bearish,
color hidden_bearish_color,
string labels,
color text_color) =>
repaint = barstate.ishistory or barstate.isconfirmed
pl = na(ta.pivotlow(source, left, right)) ? false : true
ph = na(ta.pivothigh(source, left, right)) ? false : true
source_hl = source[right] > ta.valuewhen(pl, source[right], 1) and in_range(pl[1], lower_range, upper_range)
price_ll = low[right] < ta.valuewhen(pl, low[right], 1)
bullish_condition = enable_regular_bullish and price_ll and source_hl and pl and repaint
oscLL = source[right] < ta.valuewhen(pl, source[right], 1) and in_range(pl[1], lower_range, upper_range)
priceHL = low[right] > ta.valuewhen(pl, low[right], 1)
hidden_bullish_condition = enable_hidden_bullish and priceHL and oscLL and pl and repaint
oscLH = source[right] < ta.valuewhen(ph, source[right], 1) and in_range(ph[1], lower_range, upper_range)
priceHH = high[right] > ta.valuewhen(ph, high[right], 1)
bearish_condition = enable_regular_bearish and priceHH and oscLH and ph and repaint
oscHH = source[right] > ta.valuewhen(ph, source[right], 1) and in_range(ph[1], lower_range, upper_range)
priceLH = high[right] < ta.valuewhen(ph, high[right], 1)
hidden_bearish_condition = enable_hidden_bearish and priceLH and oscHH and ph and repaint
offset = -right
var float current_bullish_value = 0
var int current_bull_idx = 0
var float last_bullish_value = 0
var int last_bullish_idx = 0
var float current_hidden_bullish_value = 0
var int current_hidden_bullish_idx = 0
var float last_hidden_bullish_value = 0
var int last_hidden_bullish_idx = 0
var float current_bearish_value = 0
var int current_bearish_idx = 0
var float last_bearish_value = 0
var int last_bearish_idx = 0
var float current_hidden_bearish_value = 0
var int current_hidden_bearish_idx = 0
var float last_hidden_bearish_idx = 0
var int last_hidden_bearish_value = 0
if pl
last_bullish_value := current_bullish_value
last_bullish_idx := current_bull_idx
current_bullish_value := source[right]
current_bull_idx := bar_index - right
last_hidden_bullish_value := current_hidden_bullish_value
last_hidden_bullish_idx := current_hidden_bullish_idx
current_hidden_bullish_value := source[right]
current_hidden_bullish_idx := bar_index - right
if ph
last_bearish_value := current_bearish_value
last_bearish_idx := current_bearish_idx
current_bearish_value := source[right]
current_bearish_idx := bar_index - right
last_hidden_bearish_idx := current_hidden_bearish_value
last_hidden_bearish_value := current_hidden_bearish_idx
current_hidden_bearish_value := source[right]
current_hidden_bearish_idx := bar_index - right
label_style_bullish = switch labels
"Text" => label.style_label_up
"Symbol" => label.style_triangleup
=> label.style_none
label_style_bearish = switch labels
"Text" => label.style_label_down
"Symbol" => label.style_triangledown
=> label.style_none
size = switch labels
"Text" => size.small
"Symbol" => size.tiny
=> size.auto
distance = 20
if bullish_condition
line.new(last_bullish_idx, last_bullish_value, current_bull_idx, current_bullish_value
, width = 2, color = regular_bullish_color)
if labels != "Disabled"
label.new(bar_index - right, source[right] - distance, labels == "Text" ? "Bullish" : ""
, color = regular_bullish_color, textcolor = text_color, style = label_style_bullish, size = size
, tooltip = "Bullish")
if hidden_bullish_condition
line.new(last_hidden_bullish_idx, last_hidden_bullish_value, current_hidden_bullish_idx
, current_hidden_bullish_value, width = 2, color = hidden_bullish_color)
if labels != "Disabled"
label.new(bar_index - right, source[right] - distance, labels == "Text" ? "Hidden Bullish" : ""
, color = hidden_bullish_color, textcolor = text_color, style = label_style_bullish, size = size
, tooltip = "Hidden Bullish")
if bearish_condition
line.new(last_bearish_idx, last_bearish_value, current_bearish_idx, current_bearish_value
, width = 2, color = regular_bearish_color)
if labels != "Disabled"
label.new(bar_index - right, source[right] + distance, labels == "Text" ? "Bearish" : ""
, color = regular_bearish_color, textcolor = text_color, style = label_style_bearish, size = size
, tooltip = "Bearish")
if hidden_bearish_condition
line.new(last_hidden_bearish_value, last_hidden_bearish_idx, current_hidden_bearish_idx
, current_hidden_bearish_value, width = 2, color = hidden_bearish_color)
if labels != "Disabled"
label.new(bar_index - right, source[right] + distance, labels == "Text" ? "Hidden Bearish" : ""
, color = hidden_bearish_color, textcolor = text_color, style = label_style_bearish, size = size
, tooltip = "Hidden Bearish")
nibble.new(bullish_condition
, hidden_bullish_condition
, bearish_condition
, hidden_bearish_condition)
method max_size(int[] self, int length)=>
if self.size() > length
self.shift()
var active_up = false
var active_down = false
ph = ta.pivothigh(high, pivot_length, pivot_length_forward)
current_ph = fixnan(ph)
pl = ta.pivotlow(low, pivot_length, pivot_length_forward)
current_pl = fixnan(pl)
if not na(ph)
active_up := true
if not na(pl)
active_down := true
max = ta.highest(high, max_high_length)
min = ta.lowest(low, min_low_length)
var float top = na
var float bottom = na
var bool polarity = false
bos = false
choch = false
hh = high > current_ph
ll = low < current_pl
hh_neg = hh and not hh[1]
ll_neg = ll and not ll[1]
var int bos_up_count = 0
var int bos_down_count = 0
var bos_up_array = array.new()
var bos_down_array = array.new()
if hh_neg
if active_up
if polarity
bos := true
bos_up_count += 1
if not polarity
choch := true
if bos_down_count != 0
bos_down_array.push(bos_down_count)
bos_up_count := 0
bos_down_count := 0
polarity := true
active_up := false
bottom := current_pl <= bottom ? (quick_update ? (bottom > min ? bottom : min) : bottom) : current_pl
if ll_neg
if active_down
if not polarity
bos := true
bos_down_count += 1
if polarity
choch := true
if bos_up_count != 0
bos_up_array.push(bos_up_count)
bos_up_count := 0
bos_down_count := 0
polarity := false
active_down := false
top := current_ph >= top ? (quick_update ? (top < max ? max : top) : top) : current_ph
bos_up_array.max_size(20)
bos_down_array.max_size(20)
bullish_bos_max = math.max(1, bos_up_array.max())
bearish_bos_max = math.max(1, bos_down_array.max())
bullish_color = color.from_gradient(bos_up_count, 0, bullish_bos_max, bullish_color_min, bullish_color_max)
bearish_color = color.from_gradient(bos_down_count, 0, bearish_bos_max, bearish_color_min, bearish_color_max)
if high > max[1] and high > top
top := max
if low < min[1] and low < bottom
bottom := min
alpha = color.new(color.black, 100)
polarity_color = polarity ? bullish_color : bearish_color
center = math.avg(top, bottom)
offset = accentuate ? 1 : 0
scaled_price = -100 + (close - bottom[offset]) * 200 / (top[offset] - bottom[offset])
price = ta.sma(scaled_price, smoothing)
avg = ta.wma(price, average)
barcolor(candle_color ? polarity_color : na)
plot(avg, "Average", average_color)
mx = plot(100, "Top", premium_color)
c = plot(0, "Center", color.gray)
mn = plot(-100, "Bottom", discount_color)
fill(c, mx, 100, 0, color.new(premium_color, 87), alpha, "Premium Zone")
fill(c, mn, 0, -100, alpha, color.new(discount_color, 87), "Discount Zone")
plot(price, "Price", polarity_color)
plotshape(enable_labels and polarity and bos ? math.min(-120, price - 20) : na, "BOS Bullish"
, shape.triangleup, location.absolute, bullish_color, 0, "BOS", text_color, true, size.tiny)
plotshape(enable_labels and not polarity and bos ? math.max(110, price + 10) : na, "BOS Bullish"
, shape.triangledown, location.absolute, bearish_color, 0, "BOS", text_color, true, size.tiny)
plotshape(enable_labels and polarity and choch ? math.min(-120, price - 20) : na, "CHoCH Bullish"
, shape.circle, location.absolute, choch_bullish_color, 0, "ChoCH", text_color, true, size.tiny)
plotshape(enable_labels and not polarity and choch ? math.max(110, price + 10) : na, "CHoCH Bullish"
, shape.circle, location.absolute, choch_bearish_color, 0, "ChoCH", text_color, true, size.tiny)
plot(enable_visual_padding ? 110 + padding_level : na, "buffer", alpha, editable = false, display = display.pane)
plot(enable_visual_padding ? -100 + -padding_level : na, "buffer", alpha, editable = false, display = display.pane)
div_alerts = divergence(price
, right
, left
, upper_range
, lower_range
, enable_regular_bullish
, regular_bullish_color
, enable_hidden_bullish
, hidden_bullish_color
, enable_regular_bearish
, regular_bearish_color
, enable_hidden_bearish
, hidden_bearish_color
, labels
, text_color)
alertcondition(polarity and bos, "Bullish BOS")
alertcondition(not polarity and bos, "Bearish BOS")
alertcondition(polarity and choch, "Bullish CHoCH")
alertcondition(not polarity and choch, "Bearish CHoCH")
alertcondition(div_alerts.a, "Bullish Divergence")
alertcondition(div_alerts.b, "Hidden Bullish Divergence")
alertcondition(div_alerts.c, "Bearish Divergence")
alertcondition(div_alerts.d, "Hidden Bearish Divergence")
alertcondition(ta.crossover(price, avg), "Cross Over Average")
alertcondition(ta.crossunder(price, avg), "Cross Under Average")