L’indicateur RSI v2 est une version améliorée du célèbre Relative Strength Index, conçu pour offrir une lecture plus complète et visuelle de la dynamique du marché. En plus du calcul classique du RSI basé sur la méthode de Wilder, il intègre la possibilité de lisser l’indicateur avec différents types de moyennes mobiles (SMA, EMA, WMA, SMMA, VWMA) ou de l’entourer de bandes de Bollinger pour mieux appréhender la volatilité. L’affichage inclut des zones de surachat et de survente (notamment les seuils 80/20 et 70/30) avec des dégradés colorés pour une interprétation rapide. L’outil va plus loin en détectant automatiquement les divergences haussières et baissières régulières entre prix et RSI, en les signalant directement sur le graphique et via alertes, ce qui en fait un indicateur puissant pour anticiper les retournements de tendance.
//@version=5
indicator(title="Relative Strength Index v2", shorttitle="RSI v2", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
ma(source, length, type) =>
switch type
"SMA" => ta.sma(source, length)
"Bollinger Bands" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"SMMA (RMA)" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"VWMA" => ta.vwma(source, length)
rsiLengthInput = input.int(2, minval=1, title="RSI Length", group="RSI Settings")
rsiSourceInput = input.source(close, "Source", group="RSI Settings")
maTypeInput = input.string("SMA", title="MA Type", options=["SMA", "Bollinger Bands", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group="MA Settings", display = display.data_window)
maLengthInput = input.int(14, title="MA Length", group="MA Settings", display = display.data_window)
bbMultInput = input.float(2.0, minval=0.001, maxval=50, title="BB StdDev", group="MA Settings", display = display.data_window)
showDivergence = input.bool(false, title="Show Divergence", group="RSI Settings", display = display.data_window)
// RRE ajout de 2 bandes 80 et 20
band1 = hline(80, title="Higher Line", linewidth=1, color=color.red , linestyle = hline.style_solid)
band2 = hline(20, title="Lower Line", linewidth=1, color=color.lime , linestyle = hline.style_solid)
up = ta.rma(math.max(ta.change(rsiSourceInput), 0), rsiLengthInput)
down = ta.rma(-math.min(ta.change(rsiSourceInput), 0), rsiLengthInput)
rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))
rsiMA = ma(rsi, maLengthInput, maTypeInput)
isBB = maTypeInput == "Bollinger Bands"
rsiPlot = plot(rsi, "RSI", color=#7E57C2)
plot(rsiMA, "RSI-based MA", color=color.yellow)
rsiUpperBand = hline(95, "RSI Upper Band", color=#787B86)
midline = hline(50, "RSI Middle Band", color=color.new(#787B86, 50))
rsiLowerBand = hline(5, "RSI Lower Band", color=#787B86)
fill(rsiUpperBand, rsiLowerBand, color=color.rgb(126, 87, 194, 90), title="RSI Background Fill")
bbUpperBand = plot(isBB ? rsiMA + ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Upper Bollinger Band", color=color.green)
bbLowerBand = plot(isBB ? rsiMA - ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Lower Bollinger Band", color=color.green)
fill(bbUpperBand, bbLowerBand, color= isBB ? color.new(color.green, 90) : na, title="Bollinger Bands Background Fill")
midLinePlot = plot(50, color = na, editable = false, display = display.none)
fill(rsiPlot, midLinePlot, 100, 70, top_color = color.new(color.green, 0), bottom_color = color.new(color.green, 100), title = "Overbought Gradient Fill")
fill(rsiPlot, midLinePlot, 30, 0, top_color = color.new(color.red, 100), bottom_color = color.new(color.red, 0), title = "Oversold Gradient Fill")
// Divergence
lookbackRight = 5
lookbackLeft = 5
rangeUpper = 60
rangeLower = 5
bearColor = color.red
bullColor = color.green
textColor = color.white
noneColor = color.new(color.white, 100)
plFound = na(ta.pivotlow(rsi, lookbackLeft, lookbackRight)) ? false : true
phFound = na(ta.pivothigh(rsi, lookbackLeft, lookbackRight)) ? false : true
_inRange(cond) =>
bars = ta.barssince(cond == true)
rangeLower <= bars and bars <= rangeUpper
//------------------------------------------------------------------------------
// Regular Bullish
// rsi: Higher Low
rsiHL = rsi[lookbackRight] > ta.valuewhen(plFound, rsi[lookbackRight], 1) and _inRange(plFound[1])
// Price: Lower Low
priceLL = low[lookbackRight] < ta.valuewhen(plFound, low[lookbackRight], 1)
bullCondAlert = priceLL and rsiHL and plFound
bullCond = showDivergence and bullCondAlert
plot(
plFound ? rsi[lookbackRight] : na,
offset=-lookbackRight,
title="Regular Bullish",
linewidth=2,
color=(bullCond ? bullColor : noneColor)
)
plotshape(
bullCond ? rsi[lookbackRight] : na,
offset=-lookbackRight,
title="Regular Bullish Label",
text=" Bull ",
style=shape.labelup,
location=location.absolute,
color=bullColor,
textcolor=textColor
)
//------------------------------------------------------------------------------
// Regular Bearish
// rsi: Lower High
rsiLH = rsi[lookbackRight] < ta.valuewhen(phFound, rsi[lookbackRight], 1) and _inRange(phFound[1])
// Price: Higher High
priceHH = high[lookbackRight] > ta.valuewhen(phFound, high[lookbackRight], 1)
bearCondAlert = priceHH and rsiLH and phFound
bearCond = showDivergence and bearCondAlert
plot(
phFound ? rsi[lookbackRight] : na,
offset=-lookbackRight,
title="Regular Bearish",
linewidth=2,
color=(bearCond ? bearColor : noneColor)
)
plotshape(
bearCond ? rsi[lookbackRight] : na,
offset=-lookbackRight,
title="Regular Bearish Label",
text=" Bear ",
style=shape.labeldown,
location=location.absolute,
color=bearColor,
textcolor=textColor
)
alertcondition(bullCondAlert, title='Regular Bullish Divergence', message="Found a new Regular Bullish Divergence, `Pivot Lookback Right` number of bars to the left of the current bar.")
alertcondition(bearCondAlert, title='Regular Bearish Divergence', message='Found a new Regular Bearish Divergence, `Pivot Lookback Right` number of bars to the left of the current bar.')
NSDT HAMA Candles + FVG + Smoothed Heiken Ashi + MA Sabres :
Cet outil combine des bougies HAMA (Heiken Ashi lissées et recalculées) avec un dégradé de couleurs pour visualiser la force du mouvement autour d’une moyenne mobile configurable. Il détecte et trace les Fair Value Gaps (FVG) sur l’unité de temps courante et/ou une unité supérieure (MTF), avec options d’effacement automatique quand le gap est comblé ou au test du milieu de gap. Une ligne de MA (“MA Sabres”) sert de boussole de tendance, avec alertes quand elle monte/descend ou quand le prix croise la MA. Les bougies, leurs mèches et la ligne de MA héritent du code couleur (vert/rouge ou dégradé) pour lire rapidement biais, momentum et zones d’inefficience potentielles à viser (ou à attendre en retest). En pratique, on l’utilise pour : définir le biais de tendance, repérer les zones FVG susceptibles d’être comblées, attendre un signal de reprise (changement de couleur / croisement MA) et synchroniser l’exécution entre TF courant et supérieur.
//@version=5
indicator('NSDT HAMA Candles + FVG + Smoothed Heiken Ashi + MA Sabres', overlay=true)
//The follow gradient code is taken from the Pinecoders Gradient Framework example.
//https://www.tradingview.com/script/hqH4YIFa-Color-Gradient-Framework-PineCoders/
//Pro Advance/Decline Gradient
//North Star Day Trading
////////////////////
//GRADIENT AREA
////////////////////
f_c_gradientAdvDecPro(_source, _center, _steps, _c_bearWeak, _c_bearStrong, _c_bullWeak, _c_bullStrong) =>
var float _qtyAdvDec = 0.
var float _maxSteps = math.max(1, _steps)
bool _xUp = ta.crossover(_source, _center)
bool _xDn = ta.crossunder(_source, _center)
float _chg = ta.change(_source)
bool _up = _chg > 0
bool _dn = _chg < 0
bool _srcBull = _source > _center
bool _srcBear = _source < _center
_qtyAdvDec := _srcBull ? _xUp ? 1 : _up ? math.min(_maxSteps, _qtyAdvDec + 1) : _dn ? math.max(1, _qtyAdvDec - 1) : _qtyAdvDec : _srcBear ? _xDn ? 1 : _dn ? math.min(_maxSteps, _qtyAdvDec + 1) : _up ? math.max(1, _qtyAdvDec - 1) : _qtyAdvDec : _qtyAdvDec
var color _return = na
_return := _srcBull ? color.from_gradient(_qtyAdvDec, 1, _maxSteps, _c_bullWeak, _c_bullStrong) : _srcBear ? color.from_gradient(_qtyAdvDec, 1, _maxSteps, _c_bearWeak, _c_bearStrong) : _return
_return
//MA TYPES
mat(source, length, type) =>
type == 'SMA' ? ta.sma(source, length) : type == 'EMA' ? ta.ema(source, length) : type == 'RMA' ? ta.rma(source, length) : type == 'WMA' ? ta.wma(source, length) : type == 'VWMA' ? ta.vwma(source, length) : type == 'HMA' ? ta.hma(source, length) : type == 'TMA' ? ta.sma(ta.sma(source, length), length) : na
//INPUTS
bull = input(color.rgb(0, 255, 0), title='Bull Color')
bear = input(color.rgb(255, 0, 0), title='Bear Color')
neutral = input(color.rgb(255, 255, 0, 0), title='Neutral Color')
show_ma = true
ma_type = 'WMA'
ma_source = close
ma_length = 55
UseGradient = input(true, title='Use Gradient Colors')
stepn = input(5, title='Max Gradient Steps')
ma = mat(ma_source, ma_length, ma_type)
col = f_c_gradientAdvDecPro(ma, ta.ema(ma, 3), stepn, neutral, bear, neutral, bull)
////////////////////
//END GRADIENT AREA
////////////////////
//MA INFO
WickColor = input.color(color.rgb(80, 80, 80, 100), title='Wick Color', tooltip='Suggest Full Transparency.')
OpenLength = input.int(25, minval=1, title='Length Open', inline='Open')
OpenType = input.string(defval='EMA', title='Type', options=['EMA', 'SMA', 'WMA'], inline='Open')
HighLength = 20
HighType = 'EMA'
LowLength = 20
LowType = 'EMA'
CloseLength = input.int(20, minval=1, title='Length Close', inline='Close')
CloseType = input.string(defval='EMA', title='Type', options=['EMA', 'SMA', 'WMA'], inline='Close')
LengthMA = input.int(55, minval=1, title='MA Line Length', inline='MA Info')
MAType = input.string(defval='EMA', title='MA Line Type', options=['EMA', 'SMA', 'WMA'], inline='MA Info')
MASource = input(hl2, title='MA Source')
TypeOpen = OpenType
SourceOpen = (open[1] + close[1]) / 2
LengthOpen = OpenLength
TypeHigh = HighType
SourceHigh = math.max(high, close)
LengthHigh = HighLength
TypeLow = LowType
SourceLow = math.min(low, close)
LengthLow = LowLength
TypeClose = CloseType
SourceClose = (open + high + low + close) / 4
LengthClose = CloseLength
funcCalcMA1(type1, src1, len1) =>
return_1 = type1 == 'EMA' ? ta.ema(src1, len1) : type1 == 'SMA' ? ta.sma(src1, len1) : type1 == 'WMA' ? ta.wma(src1, len1) : na
return_1
funcCalcOpen(TypeOpen, SourceOpen, LengthOpen) =>
return_2 = TypeOpen == 'EMA' ? ta.ema(SourceOpen, LengthOpen) : TypeOpen == 'SMA' ? ta.sma(SourceOpen, LengthOpen) : TypeOpen == 'WMA' ? ta.wma(SourceOpen, LengthOpen) : na
return_2
funcCalcHigh(TypeHigh, SourceHigh, LengthHigh) =>
return_3 = TypeHigh == 'EMA' ? ta.ema(SourceHigh, LengthHigh) : TypeHigh == 'SMA' ? ta.sma(SourceHigh, LengthHigh) : TypeHigh == 'WMA' ? ta.wma(SourceHigh, LengthHigh) : na
return_3
funcCalcLow(TypeLow, SourceLow, LengthLow) =>
return_4 = TypeLow == 'EMA' ? ta.ema(SourceLow, LengthLow) : TypeLow == 'SMA' ? ta.sma(SourceLow, LengthLow) : TypeLow == 'WMA' ? ta.wma(SourceLow, LengthLow) : na
return_4
funcCalcClose(TypeClose, SourceClose, LengthClose) =>
return_5 = TypeClose == 'EMA' ? ta.ema(SourceClose, LengthClose) : TypeClose == 'SMA' ? ta.sma(SourceClose, LengthClose) : TypeClose == 'WMA' ? ta.wma(SourceClose, LengthClose) : na
return_5
MA1 = funcCalcMA1(MAType, MASource, LengthMA)
CandleOpen = funcCalcOpen(TypeOpen, SourceOpen, LengthOpen)
CandleHigh = funcCalcHigh(TypeHigh, SourceHigh, LengthHigh)
CandleLow = funcCalcLow(TypeLow, SourceLow, LengthLow)
CandleClose = funcCalcClose(TypeClose, SourceClose, LengthClose)
//PLOT CANDLES
BodyColor = CandleOpen > CandleOpen[1] ? color.green : color.red
barcolor(UseGradient ? col : BodyColor)
plotcandle(CandleOpen, CandleHigh, CandleLow, CandleClose, color=UseGradient ? col : BodyColor, title='HAMA Candles', wickcolor=WickColor, bordercolor=na)
plot(MA1, title='MA Line', color=UseGradient ? col : BodyColor, style=plot.style_line, linewidth=2)
//ALERTS
alertcondition(ta.rising(MA1, 2), title='MA Rising', message='MA Rising')
alertcondition(ta.falling(MA1, 2), title='MA Falling', message='MA Falling')
alertcondition(ta.crossover(high, MA1), title='High Crossing MA', message='High Crossing MA')
alertcondition(ta.crossunder(low, MA1), title='Low Crossing MA', message='Low Crossing MA')
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © spacemanbtc
//Fair Value Gap
//V1, 2022.4.12
//This indicator displays the fair value gap of the current timeframe AND an optional higher time frame.
//What the script does is take into account the values of the current bar high/low and compare with 2 bars previous
//The "gap" is generated from the lack of overlap between these bars
//Bearish or Bullish gaps determined via whether the gap is above or below price, as they tend to be filled gaps can be used as targets.
// ———————————————————— Inputs {
// Standard practice declared input variables with i_ easier to identify
i_tf = input.timeframe("D", "MTF Timeframe", group = "MTF Settings")
i_mtf = input.string(defval = "Current TF",group = "MTF Settings", title = "MTF Options", options = ["Current TF", "Current + HTF", "HTF"])
i_tfos = input.int(defval = 10,title = "Offset", minval = 0, maxval = 500 ,group = "MTF Settings", inline = "OS")
i_mtfos = input.int(defval = 20,title = "MTF Offset", minval = 0, maxval = 500 ,group = "MTF Settings", inline = "OS")
i_fillByMid = input.bool(false, "MidPoint Fill",group = "General Settings", tooltip = "When enabled FVG is filled when midpoint is tested")
i_deleteonfill = input.bool(true, "Delete Old On Fill",group = "General Settings")
i_labeltf = input.bool(true,"Label FVG Timeframe",group = "General Settings")
i_bullishfvgcolor = input.color(color.new(color.green,90), "Bullish FVG", group = "Coloring", inline = "BLFVG")
i_mtfbullishfvgcolor = input.color(color.new(color.lime,80), "MTF Bullish FVG", group = "Coloring", inline = "BLFVG")
i_bearishfvgcolor = input.color(color.new(color.red,90), "Bearish FVG", group = "Coloring", inline = "BRFVG")
i_mtfbearishfvgcolor = input.color(color.new(color.maroon,80), "MTF Bearish FVG", group = "Coloring", inline = "BRFVG")
i_midPointColor = input.color(color.new(color.white,85), "MidPoint Color", group = "Coloring")
i_textColor = input.color(color.white, "Text Color", group = "Coloring")
// }
// ———————————————————— Global data {
//Using current bar data for HTF highs and lows instead of security to prevent future leaking
var htfH = open
var htfL = open
if close > htfH
htfH:= close
if close < htfL
htfL := close
//Security Data, used for HTF Bar Data reference
sClose = request.security(syminfo.tickerid, i_tf, close[1], barmerge.gaps_off, barmerge.lookahead_on)
sHighP2 = request.security(syminfo.tickerid, i_tf, high[2], barmerge.gaps_off, barmerge.lookahead_on)
sLowP2 = request.security(syminfo.tickerid, i_tf, low[2], barmerge.gaps_off, barmerge.lookahead_on)
sOpen = request.security(syminfo.tickerid, i_tf, open[1], barmerge.gaps_off, barmerge.lookahead_on)
sBar = request.security(syminfo.tickerid, i_tf, bar_index, barmerge.gaps_off, barmerge.lookahead_on)
// }
//var keyword can be used to hold data in memory, with pinescript all data is lost including variables unless the var keyword is used to preserve this data
var bullishgapholder = array.new_box(0)
var bearishgapholder = array.new_box(0)
var bullishmidholder = array.new_line(0)
var bearishmidholder = array.new_line(0)
var bullishlabelholder = array.new_label(0)
var bearishlabelholder = array.new_label(0)
var transparentcolor = color.new(color.white,100)
// ———————————————————— Functions {
//function paramaters best declared with '_' this helps defer from variables in the function scope declaration and elsewhere e.g. close => _close
f_gapCreation(_upperlimit,_lowerlimit,_midlimit,_bar,_boxholder,_midholder,_labelholder,_boxcolor,_mtfboxcolor, _htf)=>
timeholder = str.tostring(i_tf)
offset = i_mtfos
boxbgcolor = _mtfboxcolor
if _htf == false
timeholder := str.tostring(timeframe.period)
offset := i_tfos
boxbgcolor := _boxcolor
array.push(_boxholder,box.new(_bar,_upperlimit,_bar+1,_lowerlimit,border_color=transparentcolor,bgcolor = boxbgcolor, extend = extend.right))
if i_fillByMid
array.push(_midholder,line.new(_bar,_midlimit,_bar+1,_midlimit,color = i_midPointColor, extend = extend.right))
if i_labeltf
array.push(_labelholder,label.new(_bar+ offset,_midlimit * 0.999, text = timeholder + " FVG", style =label.style_none, size = size.normal, textcolor = i_textColor))
//checks for gap between current candle and 2 previous candle e.g. low of current candle and high of the candle before last, this is the fair value gap.
f_gapLogic(_close,_high,_highp2,_low,_lowp2,_open,_bar,_htf)=>
if _open > _close
if _high - _lowp2 < 0
upperlimit = _close - (_close - _lowp2 )
lowerlimit = _close - (_close-_high)
midlimit = (upperlimit + lowerlimit) / 2
f_gapCreation(upperlimit,lowerlimit,midlimit,_bar,bullishgapholder,bullishmidholder,bullishlabelholder,i_bullishfvgcolor,i_mtfbullishfvgcolor,_htf)
else
if _low - _highp2 > 0
upperlimit = _close - (_close-_low)
lowerlimit = _close- (_close - _highp2),
midlimit = (upperlimit + lowerlimit) / 2
f_gapCreation(upperlimit,lowerlimit,midlimit,_bar,bearishgapholder,bearishmidholder,bearishlabelholder,i_bearishfvgcolor,i_mtfbearishfvgcolor,_htf)
//Used to remove the gap from its relevant array as a result of it being filled.
f_gapDeletion(_currentgap,_i,_boxholder,_midholder,_labelholder)=>
array.remove(_boxholder,_i)
if i_fillByMid
currentmid=array.get(_midholder,_i)
array.remove(_midholder,_i)
if i_deleteonfill
line.delete(currentmid)
else
line.set_extend(currentmid, extend.none)
line.set_x2(currentmid,bar_index)
if i_deleteonfill
box.delete(_currentgap)
else
box.set_extend(_currentgap,extend.none)
box.set_right(_currentgap,bar_index)
if i_labeltf
currentlabel=array.get(_labelholder,_i)
array.remove(_labelholder,_i)
if i_deleteonfill
label.delete(currentlabel)
//checks if gap has been filled either by 0.5 fill (i_fillByMid) or SHRINKS the gap to reflect the true value gap left.
f_gapCheck(_high,_low)=>
if array.size(bullishgapholder) > 0
for i = array.size(bullishgapholder)-1 to 0
currentgap = array.get(bullishgapholder,i)
currenttop = box.get_top(currentgap)
if i_fillByMid
currentmid = array.get(bullishmidholder,i)
currenttop := line.get_y1(currentmid)
if _high >= currenttop
f_gapDeletion(currentgap,i,bullishgapholder,bullishmidholder,bullishlabelholder)
if _high > box.get_bottom(currentgap) and _high < box.get_top(currentgap)
box.set_bottom(currentgap,_high)
if array.size(bearishgapholder) > 0
for i = array.size(bearishgapholder)-1 to 0
currentgap = array.get(bearishgapholder,i)
currentbottom = box.get_bottom(currentgap)
if i_fillByMid
currentmid = array.get(bearishmidholder,i)
currentbottom := line.get_y1(currentmid)
if _low <= currentbottom
f_gapDeletion(currentgap,i,bearishgapholder,bearishmidholder,bearishlabelholder)
if _low < box.get_top(currentgap) and _low > box.get_bottom(currentgap)
box.set_top(currentgap,_low)
// pine provided function to determine a new bar
is_newbar(res) =>
t = time(res)
not na(t) and (na(t[1]) or t > t[1])
if is_newbar(i_tf)
htfH := open
htfL := open
// }
// User Input, allow MTF data calculations
if is_newbar(i_tf) and (i_mtf == "Current + HTF" or i_mtf == "HTF")
f_gapLogic(sClose,htfH,sHighP2,htfL,sLowP2,sOpen,bar_index,true)
// Use current Timeframe data to provide gap logic
if (i_mtf == "Current + HTF" or i_mtf == "Current TF")
f_gapLogic(close[1],high,high[2],low,low[2],open[1],bar_index,false)
f_gapCheck(high,low)
// This source code provided free and open-source as defined by the terms of the Mozilla Public License 2.0 (https://mozilla.org/MPL/2.0/)
// with the following additional requirements:
//
// 1. Citations for sources and references must be maintained and updated when appropriate
// 2. Links to strategy references included in indicator tooptips and/or alerts should be retained to give credit to the original source
// while also providing a freely available source of information on proper use and interpretation
//
// Author: SamAccountX
//
// The intent of this indicator is to provide a more customizable and refined version of the Smoothed Heiken Ashi indicator. In addition
// to converting to Pinescript v5, numerous additional enhancements have been made to improve user customization options, settings
// clarity, and provide more meaningful context for user configuration options.
//
// Inputs
// Inputs group 1 - Display & Timeframe Settings
g_TimeframeSettings = 'Display & Timeframe Settings'
time_frame = input.timeframe(title='Timeframe for HA candle calculation', defval='', group=g_TimeframeSettings, tooltip='Select the timeframe to use for calculating the smoothed ' +
'HA candles. The default value is to use the current chart timeframe, but altering this will allow you to have the indicator reflect a higher or lower timeframe. \n\n' +
'Note: Selecting a lower timeframe than the current chart timeframe may not result in more precise candles as the display will still be limited to the current timeframe resolution.')
// I decided to add just a couple display-related settings here
colorBullish = input.color(title='Color for bullish candle (Close > Open)', defval=color.rgb(255, 255, 255, 0), tooltip='Select the color to use to denote a bullish candle (where price closed above the open). \n\n' +
'Note: Any changes to the "Style" tab will override this setting. Actual doji candles (Open == Close) inherit the color of the previous candle.')
colorBearish = input.color(title='Color for bearish candle (Close < Open)', defval=color.rgb(255, 0, 255, 0), tooltip='Select the color to use to denote a bearish candle (where price closed below the open). \n\n' +
'Note: Any changes to the "Style" tab will override this setting. Actual doji candles (Open == Close) inherit the color of the previous candle.')
showWicks = input.bool(title="Show Wicks", defval=true, group=g_TimeframeSettings, tooltip='If checked (default), this indicator will paint wicks for the smoothed HA candles. \n\n' +
'This can be helpful with shorter smooting periods, but many people like to hide wicks for longer periods to de-clutter the chart a bit. \n\n' +
'Note: By default, wick color will match the candle body color. This can be overridden in the "Styles" tab.')
// Inputs group 2 - Smoothed HA settings
g_SmoothedHASettings = 'Smoothed HA Settings'
smoothedHALength = input.int(title='HA Price Input Smoothing Length', minval=1, maxval=500, step=1, defval=10, group=g_SmoothedHASettings, tooltip='This input determines the number of time intervals (i.e. candles) ' +
'to use when calculating a single smoothed HA candle. Lower values will be more responsive to momentum shifts, while higher values will be better able to show sustained trends even ' +
'during a moderate pull-back. \n\n' +
'Note: A value of 1 (no matter the moving average type selected) will result in a standard HA candle, which may be desirable if you wish to see both regular and HA candles on the same ' +
'chart simultaneously (in which case you should un-check "Enable double-smoothing" below).')
smoothedMAType = input.string(title='Moving Average Calculation', group=g_SmoothedHASettings, options=['Exponential', 'Simple', 'Smoothed', 'Weighted', 'Linear', 'Hull', 'Arnaud Legoux'], defval='Exponential', tooltip='Type of moving average calculation to use ' +
'for calculating the smoothed HA candles (default is Exponential (EMA)).')
smoothedHAalmaSigma = input.float(title="ALMA Sigma", defval=6, minval=0, maxval=100, step=0.1, group=g_SmoothedHASettings, tooltip='Standard deviation applied to the ALMA MA. Higher values tend to make the line smoother. \n\n' +
'Only relevant when "Arnaud Legoux" is selected as the MA type above. Default: 6')
smoothedHAalmaOffset = input.float(title="ALMA Offset", defval=0.85, minval=0, maxval=1, step=0.01, group=g_SmoothedHASettings, tooltip='Gaussian offset applied to the ALMA MA. Higher values tend to make the line smoother, while lower values make it more responsive. \n\n' +
'Only relevant when "Arnaud Legoux" is selected as the MA type above. Default: 0.85')
// Inputs group 3 - Double-smooth settings
g_DoubleSmoothingSettings = 'Double-smoothed HA Settings'
doDoubleSmoothing = input.bool(title='Enable double-smoothing', defval=true, group=g_DoubleSmoothingSettings, tooltip='Check this box to apply a secondary moving average to further smooth ' +
'the smoothed HA candles. \n\n' +
'While this may seem counter-intuitive, most versions of this indicator do use double-smoothing, hence the default value is true/checked.')
doubleSmoothedHALength = input.int(title='HA Second Smoothing Length', minval=1, maxval=500, step=1, defval=10, group=g_DoubleSmoothingSettings, tooltip='This input defines how many of the smoothed HA candle ' +
'price points to include for calculating a double-smoothed HA candle. \n\n' +
'Similar to how the comparable "Smoothed HA Settings" setting above use pure price data to calculate and construct a smoothed HA candle, this will use the output open, high, low, and close ' +
'of the above pre-smoothed candles and apply a second level of smoothing on top of them in a similar method (calculate a new average open, high, low, and close, then apply the HA formula ' +
'to those new values to determing what to print as the output). \n\n' +
'Also, a value of 1 for this setting will be the same as un-checking the "Enable double-smoothing" box.')
doubleSmoothedMAType = input.string(title='Double-Smoothing Moving Average Calculation', group=g_DoubleSmoothingSettings, options=['Exponential', 'Simple', 'Smoothed', 'Weighted', 'Linear', 'Hull', 'Arnaud Legoux'], defval='Exponential', tooltip='Type of moving average calculation to use ' +
'for calculating the second smoothing applied to the smoothed HA candles (default is Exponential (EMA)).')
doubleSmoothedHAalmaSigma = input.float(title="ALMA Sigma", defval=6, minval=0, maxval=100, step=0.1, group=g_DoubleSmoothingSettings, tooltip='Standard deviation applied to the ALMA MA above. Higher values tend to make the line smoother. \n\n' +
'Only relevant when "Arnaud Legoux" is selected as the MA type above. Default: 6')
doubleSmoothedHAalmaOffset = input.float(title="ALMA Offset", defval=0.85, minval=0, maxval=1, step=0.01, group=g_DoubleSmoothingSettings, tooltip='Gaussian offset applied to the ALMA MA above. Higher values tend to make the line smoother, while lower values make it more responsive. \n\n' +
'Only relevant when "Arnaud Legoux" is selected as the MA type above. Default: 0.85')
// Define a function for calculating the smoothed moving average, as there is no built-in function for this...
smoothedMovingAvg(src, len) =>
smma = 0.0
// TV will complain about the use of the ta.sma function use inside a function saying that it should be called on each calculation,
// but since we're only using it once to set the initial value for the smoothed MA (when the previous smma value is NaN - Not a Number)
// and using the previous smma value for each subsequent iteration, this can be safely ignored
smma := na(smma[1]) ? ta.sma(src, len) : (smma[1] * (len - 1) + src) / len
smma
// Define utility functions for calculating HA candle values
// HA Open
getHAOpen(prevOpen, prevClose) =>
haOpen = 0.0
haOpen := ((prevOpen + prevClose)/2)
haOpen
// HA High
getHAHigh(o, h, c) =>
haHigh = 0.0
haHigh := math.max(h, o, c)
haHigh
// HA Low
getHALow(o, l, c) =>
haLow = 0.0
haLow := math.min(o, l, c)
haLow
// HA Close
getHAClose(o, h, l, c) =>
haClose = 0.0
haClose := ((o + h + l + c)/4)
haClose
// Some utility functions for working with the various price manipulations
// Get the MA value for an input source and length...
getMAValue(src, len, type, isDoubleSmooth) =>
maValue = 0.0
if (type == 'Exponential')
maValue := ta.ema(source=src, length=len)
else if (type == 'Simple')
maValue := ta.sma(source=src, length=len)
else if (type == 'Smoothed')
maValue := smoothedMovingAvg(src=src, len=len)
else if (type == 'Weighted')
maValue := ta.wma(source=src, length=len)
else if (type == 'Linear')
maValue := ta.linreg(source=src, length=len, offset=0)
else if (type == 'Hull')
maValue := ta.hma(source=src, length=len)
else if (type == 'Arnaud Legoux')
maValue := ta.alma(series=src, length=len, offset=(isDoubleSmooth ? doubleSmoothedHAalmaOffset : smoothedHAalmaOffset), sigma=(isDoubleSmooth ? doubleSmoothedHAalmaSigma : smoothedHAalmaSigma))
else
maValue := na
maValue
// Begin active processing code...
// Explicitly define our ticker to help ensure that we're always getting ACTUAL price instead of relying on the input
// ticker info and input vars (as they tend to inherit the type from what's displayed on the current chart)
realPriceTicker = ticker.new(prefix=syminfo.prefix, ticker=syminfo.ticker)
// This MAY be unnecessary, but in testing I've found some oddities when trying to use this on varying chart types
// like on the HA chart, where the source referece for the MA calculations skews to the values for the current chart type
// instead of the expected pure-price values. For example, the 'source=close' reference - 'close' would be actual price
// close on a normal candlestick chart, but would be the HA close on the HA chart.
actualOpen = request.security(symbol=realPriceTicker, timeframe=time_frame, expression=open, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
actualHigh = request.security(symbol=realPriceTicker, timeframe=time_frame, expression=high, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
actualLow = request.security(symbol=realPriceTicker, timeframe=time_frame, expression=low, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
actualClose = request.security(symbol=realPriceTicker, timeframe=time_frame, expression=close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
// Get the MA values from actual price
smoothedMA1open = getMAValue(actualOpen, smoothedHALength, smoothedMAType, false)
smoothedMA1high = getMAValue(actualHigh, smoothedHALength, smoothedMAType, false)
smoothedMA1low = getMAValue(actualLow, smoothedHALength, smoothedMAType, false)
smoothedMA1close = getMAValue(actualClose, smoothedHALength, smoothedMAType, false)
// Next up is to apply the Heiken Ashi transformation calculations using the above smoothed OHLC values.
// This will result in the official "Smoothed Heiken Ashi Candle"
// The formulas for the HA open, high, low, and close are noted below in comments,
// along with the subsequent code to compute their current values...
//
// Close = Average of all 4 values for the current candle - open, high, low, and close
// (open1MA + high1MA + low1MA + close1MA) / 4
smoothedHAClose = getHAClose(smoothedMA1open, smoothedMA1high, smoothedMA1low, smoothedMA1close)
// Open = If the previous open or close resolves to 'NaN' (Not a Number), add the current
// values of open1MA and close1MA and divide by 2 to get the average. Otherwise, add the open1MA and close1MA of the previous candle
// and take the average value (by dividing by 2) as the HA open
//
// Since we need to self-reference previous values of this variable, we need to define it with an initial starting value,
// then use the mutable operator to update the value if it can
smoothedHAOpen = smoothedMA1open
smoothedHAOpen := na(smoothedHAOpen[1]) ? smoothedMA1open : getHAOpen(smoothedHAOpen[1], smoothedHAClose[1])
// High = Highest value of the current candle's HA open, high, and HA close
// smoothedHAHigh = getHAHigh(smoothedHAOpen, smoothedMA1high, smoothedMA1close)
smoothedHAHigh = getHAHigh(smoothedHAOpen, smoothedMA1high, smoothedHAClose)
// Low = Lowest value of the current candle's open, low, and close
smoothedHALow = getHALow(smoothedHAOpen, smoothedMA1low, smoothedHAClose)
// Now to have our fun with double-smoothing... Since we're making this optional, we'll first
// start by pre-setting our plot variables to the single-smoothed values. Then check if the
// doDoubleSmoothing option is selected. If it is, we'll execute the double-smoothing calculations
// and update our plot variables. Otherwise, it'll skip that processing and go right to plotting...
openToPlot = smoothedHAOpen
closeToPlot = smoothedHAClose
highToPlot = smoothedHAHigh
lowToPlot = smoothedHALow
// Now for the double-smoothing fun...
if (doDoubleSmoothing)
// So here we have to do the double-smoothing... Fortunately, this is going to be relatively easy
// compared to the earlier fun we had with the initial smoothed HA candle calculations. The hard
// work calculating the initial smoothed HA candles is done, the second smoothing is then applied
// to the values of those smoothed HA candles.
//
// Double-smoothed Open
openToPlot := getMAValue(smoothedHAOpen, doubleSmoothedHALength, doubleSmoothedMAType, true)
closeToPlot := getMAValue(smoothedHAClose, doubleSmoothedHALength, doubleSmoothedMAType, true)
highToPlot := getMAValue(smoothedHAHigh, doubleSmoothedHALength, doubleSmoothedMAType, true)
lowToPlot := getMAValue(smoothedHALow, doubleSmoothedHALength, doubleSmoothedMAType, true)
//na
// Double-smoothing was disabled by the user, so do nothing
else
na
// Since we will want to color the candle to distinguish between open > close (red) and open < close (green),
// we will pre-define our candle color before plotting the actual candle to simplify the 'plotcandle' function's color input.
// To help simplify this a bit, we're going to default the candle wick colors to match the candle body's color. Users
// should be able to override these defaults in the "Style" configuration tab to their own liking.
//
// We'll even get fancy and if the current candle is an EXACT doji (open == close), use the previous candle's color
// The logical ordering of this conditional goes like this...
// First check if the close is greater than the open. If so, return green.
// If not, check if the close is less than the open. If so, return red.
// If not (which will only occur if the open and close are exactly equal), return the
// color used on the previous candle.
//
// Note: Since we need to take into account the possibility that there is no previous candle, we'll defensively-code
// this so that we pre-assign a color (Black and transparent) to our color variable to use as a fail-safe.
//
// While this is an extreme edge-case, we also want to try and account for the possibility that a pure doji is the
// first candle to print. Since it's not easy to check a previous candle's actual color value, we'll work around
// this by adding another conditional check to see if the previous candle had a value for 'smoothedHAOpen'
//
// Like Arty, I prefer colors that really pop, so that's what we're going to use...
// Depending on how this looks in the config "Style" tab, I may define these as input variables.
candleColor = color.rgb(0, 0, 0, 100)
candleColor := (closeToPlot > openToPlot) ? colorBullish :
(closeToPlot < openToPlot) ? colorBearish : candleColor[1]
// Now we can do a plot of our smoothed HA candles...
plotcandle(open=openToPlot, high=highToPlot, low=lowToPlot, close=closeToPlot, title="Smoothed HA", color=candleColor, wickcolor=(showWicks ? candleColor : na), bordercolor=candleColor)
// Now for what everyone always loves... Alerts...
// The only reasonable alert that comes to mind at present is a a color change alert
// (e.g. previous candle printed as bearish color, next candle printed as bullish color, and vice versa).
//
// While I don't see any reason I'd personally intend to use this as an alert (this indicator is better used as a
// trend identification/bias indicator to supplement more responsive signaling indicators), we'll make a couple anyways...
// First, we'll define variables as our alert triggers...
// For the bullish change, check if the previous candle closed lower than the open. If so, check if the current candle
// closed above the open. If both checks are true, signal the alert.
isBullishColorChange = ((closeToPlot[1] < openToPlot[1]) ? (closeToPlot > openToPlot ? true : false) : false)
// And the inverse for bearish...
isBearishColorChange = ((closeToPlot[1] > openToPlot[1]) ? (closeToPlot < openToPlot ? true : false) : false)
// Important to note here is the addition of the 'barstate.isconfirmed' to the conditions... This built-in variable only evaluates to 'true' when
// the current candle is having it's final calculation performed (it's in the act of closing, and the values calculated will be the final values for that candle)
//
// As Arty would say, "Wait for the damn candle to close!" - so that's exactly what we're going to do... We will call this a "confirmed" signal...
isConfirmedBullishColorChange = isBullishColorChange and barstate.isconfirmed
isConfirmedBearishColorChange = isBearishColorChange and barstate.isconfirmed
// Now we have our trigger for alerts. Let's start with the older-style alertconditions...
alertcondition(condition=isConfirmedBullishColorChange, title="Smoothed HA Bear -> Bull", message="Smoothed Heiken Ashi detected a change in candle direction from bearish to bullish.")
alertcondition(condition=isConfirmedBearishColorChange, title="Smoothed HA Bull -> Bear", message="Smoothed Heiken Ashi detected a change in candle direction from bullish to bearish.")
// And the newer style alert functions...
if (isConfirmedBullishColorChange)
alert(freq=alert.freq_once_per_bar_close, message="Smoothed Heiken Ashi detected a change in candle direction from bearish to bullish.")
if (isConfirmedBearishColorChange)
alert(freq=alert.freq_once_per_bar_close, message="Smoothed Heiken Ashi detected a change in candle direction from bullish to bearish.")
// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © LuxAlgo
//@version=5
//------------------------------------------------------------------------------
//Settings
//-----------------------------------------------------------------------------{
type = input.string( "TEMA" , 'MA Type' , group= 'MA'
, options = ["SMA", "EMA", "SMMA (RMA)", "HullMA", "WMA", "VWMA", "DEMA", "TEMA", "NONE"])
len4 = input.int ( 50 , 'Length' , group= 'MA' )
count = input.int ( 20 , 'Previous Trend Duration' , group= 'MA'
, tooltip = 'Reversal after x bars in the same direction' )
colUp = input.color (#2962ff, 'Bullish' , group='Colours')
colDn = input.color (#f23645, 'Bearish' , group='Colours')
colMa = input.color (#787b86, 'MA' , group='Colours')
//-----------------------------------------------------------------------------}
//Method MA
//-----------------------------------------------------------------------------{
method ma(string type, int length) =>
//
ema1 = ta.ema(close, length)
ema2 = ta.ema(ema1 , length)
ema3 = ta.ema(ema2 , length)
//
switch type
"SMA" => ta.sma (close, length)
"EMA" => ema1
"SMMA (RMA)" => ta.rma (close, length)
"HullMA" => ta.hma (close, length)
"WMA" => ta.wma (close, length)
"VWMA" => ta.vwma(close, length)
"DEMA" => 2 * ema1 - ema2
"TEMA" => (3 * ema1) - (3 * ema2) + ema3
=> na
//-----------------------------------------------------------------------------}
//Calculations
//-----------------------------------------------------------------------------{
ma5 = type.ma(len4)
fl = ta.falling(ma5 , count)
rs = ta.rising (ma5 , count)
up = fl[1] and ma5 > ma5[1]
dn = rs[1] and ma5 < ma5[1]
atr = ta.atr(14)
n4 = bar_index
//-----------------------------------------------------------------------------}
//Execution
//-----------------------------------------------------------------------------{
if up
p = array.new<chart.point>()
p.push(chart.point.from_index(n4 - 1 , low [1] - atr / 15 ))
p.push(chart.point.from_index(n4 + (len4 / 2 -1) , low [1] + atr / 2.5))
p.push(chart.point.from_index(n4 + len4 , low [1] + atr * 2 ))
p.push(chart.point.from_index(n4 + (len4 / 2 -1) , low [1] + atr / 2.5))
p.push(chart.point.from_index(n4 - 1 , low [1] + atr / 15 ))
polyline.new(p
, curved = true
, closed = false
, line_color = colUp
, fill_color = color.new(colUp, 50))
if dn
p = array.new<chart.point>()
p.push(chart.point.from_index(n4 - 1 , high[1] + atr / 15 ))
p.push(chart.point.from_index(n4 + (len4 / 2 -1) , high[1] - atr / 2.5))
p.push(chart.point.from_index(n4 + len4 , high[1] - atr * 2 ))
p.push(chart.point.from_index(n4 + (len4 / 2 -1) , high[1] - atr / 2.5))
p.push(chart.point.from_index(n4 - 1 , high[1] - atr / 15 ))
polyline.new(p
, curved = true
, closed = false
, line_color = colDn
, fill_color = color.new(colDn, 50))
//-----------------------------------------------------------------------------}
//Plots
//-----------------------------------------------------------------------------{
plot (ma5 , 'MA' , color= colMa )
plotshape(up ? low [1] : na, '', color= colUp , location=location.absolute, style=shape.circle, size=size.tiny , offset=-1)
plotshape(up ? low [1] : na, '', color=color.new(colUp, 50), location=location.absolute, style=shape.circle, size=size.small , offset=-1)
plotshape(up ? low [1] : na, '', color=color.new(colUp, 65), location=location.absolute, style=shape.circle, size=size.normal, offset=-1)
plotshape(dn ? high[1] : na, '', color= colDn , location=location.absolute, style=shape.circle, size=size.tiny , offset=-1)
plotshape(dn ? high[1] : na, '', color=color.new(colDn, 50), location=location.absolute, style=shape.circle, size=size.small , offset=-1)
plotshape(dn ? high[1] : na, '', color=color.new(colDn, 65), location=location.absolute, style=shape.circle, size=size.normal, offset=-1)
//-----------------------------------------------------------------------------}