Bilateral Stochastic Oscillator - For The Sake Of EfficiencyIntroduction
The stochastic oscillator is a feature scaling method commonly used in technical analysis, this method is the same as the running min-max normalization method except that the stochastic oscillator is in a range of (0,100) while min-max normalization is in a range of (0,1). The stochastic oscillator in itself is efficient since it tell's us when the price reached its highest/lowest or crossed this average, however there could be ways to further develop the stochastic oscillator, this is why i propose this new indicator that aim to show all the information a classical stochastic oscillator would give with some additional features.
Min-Max Derivation
The min-max normalization of the price is calculated as follow : (price - min)/(max - min) , this calculation is efficient but there is alternates forms such as :
price - (max - min) - min/(max - min)
This alternate form is the one i chosen to make the indicator except that both range (max - min) are smoothed with a simple moving average, there are also additional modifications that you can see on the code.
The Indicator
The indicator return two main lines, in blue the bull line who show the buying force and in red the bear line who show the selling force.
An orange line show the signal line who represent the moving average of the max(bull,bear), this line aim to show possible exit/reversals points for the current trend.
Length control the highest/lowest period as well as the smoothing amount, signal length control the moving average period of the signal line, the pre-filtering setting indicate which smoothing method will be used to smooth the input source before applying normalization.
The default pre-filtering method is the sma.
The ema method is slightly faster as you can see above.
The triangular moving average is the moving average of another moving average, the impulse response of this filter is a triangular function hence its name. This moving average is really smooth.
The lsma or least squares moving average is the fastest moving average used in this indicator, this filter try to best fit a linear function to the data in a certain window by using the least squares method.
No filtering will use the source price without prior smoothing for the indicator calculation.
Relationship With The Stochastic Oscillator
The crosses between the bull and bear line mean that the stochastic oscillator crossed the 50 level. When the Bull line is equal to 0 this mean that the stochastic oscillator is equal to 0 while a bear line equal to 0 mean a stochastic oscillator equal to 100.
The indicator and below a stochastic oscillator of both period 100
Using Levels
Unlike a stochastic oscillator who would clip at the 0 and 100 level the proposed indicator is not heavily constrained in a range like the stochastic oscillator, this mean that you can apply levels to trigger signals
Possible levels could be 1,2,3... even if the indicator rarely go over 3.
Its then possible to create strategies using such levels as support or resistance one.
Conclusion
I've showed a modified stochastic oscillator who aim to show additional information to the user while keeping all the information a classical stochastic oscillator would give. The proposed indicator is no longer constrained in an hard range and posses more liberty to exploit its scale which in return allow to create strategies based on levels.
For pinescript users what you can learn from this is that alternates forms of specific formulas can be extremely interesting to modify, changes can be really surprising so if you are feeling stuck, modifying alternates forms of know indicators can give great results, use tools such as sympy gamma to get alternates forms of formulas.
Thanks for reading !
If you are looking for something or just want to say thanks try to pm me :)
스크립트에서 "马斯克+100万"에 대해 찾기
High/Low bandsGives good idea about trend.
In last 100 days the lowest price was this.
In last 100 days the highest price was this.
Price makes new 100 days high! (uptrend)
Chaikin MF% (CMFP) w. Alerts, Bells & Whistles [LucF]This is Chaikin’s Money Flow indicator on a 0-100 scale with buy/sell signals, alerts and other bells & whistles.
It includes:
- a fast EMA (16 periods by default),
- a slow MA (64 periods by default),
- histograms,
- 3 different sorts of crosses,
- big swings identification,
- buy/sell signals on CMFP crossing back from outside user-defined levels,
- buy/sell signals on the slow MA pivots above/below user-defined levels,
- alerts on big swings and buy/sells.
This indicator started with @LazyBear code (VAPI) at:
@cI8DH then changed the scale to 0-100, which I find very useful:
I then added the rest.
The chart above shows both clean and busy versions of the indicator.
Note that the default length is 10 rather than the commonly used 20. I use CMFP in conjunction with VFI and like the fact that it is faster than VFI. The default inputs show the way I normally use this indicator, with the slow MA shown in histogram mode. I find it gives good context to the signal line. Crosses between the two are often useful.
The buy/sell signals aren’t the main attraction of this indicator, and nothing to write home about. Like the big swing markers, I think it’s more realistic to view them as pointers to potentially interesting areas on charts. Their nature makes them more suited to identifying reversals. They certainly aren’t reliable enough to turn this study into a strategy and I normally don’t use them. The levels pre-defined for the buy/sell signals on CMFP are most useful on short intervals. The buy/sell signals on the slow MA pivots work on a more complete range of intervals. Optimization for your specific instruments and intervals will improve their reliability.
As usual when defining alerts, be sure you already have defined proper inputs and that you are on the intended interval, as they will be used when triggering alerts.
3 of SlowStochastics
스토캐스틱 3개를 한번에 볼수 있습니다. 천장과 바닥은 각 100의 위치마다 존재합니다
You can see three slow stochastics at once. The ceiling and floor are located at each 100 (0 - 100 - 200- 300)
Percentage Price Oscillator (PPO)The Percentage Price Oscillator (PPO) is a momentum oscillator that measures the difference between two moving averages as a percentage of the larger moving average. As with its cousin, MACD, the Percentage Price Oscillator is shown with a signal line, a histogram and a centerline. Signals are generated with signal line crossovers, centerline crossovers, and divergences. First, PPO readings are not subject to the price level of the security. Second, PPO readings for different securities can be compared, even when there are large differences in the price.
Calculations
PPO: {(12-day EMA - 26-day EMA)/26-day EMA} x 100
Signal Line: 9-day EMA of PPO
PPO Histogram: PPO - Signal Line
While MACD measures the absolute difference between two moving averages, PPO makes this a relative value by dividing the difference by the slower moving average (26-day EMA). PPO is simply the MACD value divided by the longer moving average. The result is multiplied by 100 to move the decimal place two spots.
Interpretation
As with MACD, the PPO reflects the convergence and divergence of two moving averages. PPO is positive when the shorter moving average is above the longer moving average. The indicator moves further into positive territory as the shorter moving average distances itself from the longer moving average. This reflects strong upside momentum. The PPO is negative when the shorter moving average is below the longer moving average. Negative readings grow when the shorter moving average distances itself from the longer moving average (goes further negative). This reflects strong downside momentum. The histogram represents the difference between PPO and its 9-day EMA, the signal line. The histogram is positive when PPO is above its 9-day EMA and negative when PPO is below its 9-day EMA. The PPO-Histogram can be used to anticipate signal line crossovers in the PPO.
MACD, PPO and Price
MACD levels are affected by the price of a security. A high-priced security will have higher or lower MACD values than a low-priced security, even if volatility is basically equal. This is because MACD is based on the absolute difference in the two moving averages. Because MACD is based on absolute levels, large price changes can affect MACD levels over an extended period of time. If a stock advances from 20 to 100, its MACD levels will be considerably smaller around 20 than around 100. The PPO solves this problem by showing MACD values in percentage terms.
Conclusions
The Percentage Price Oscillator (PPO) generates the same signals as the MACD, but provides an added dimension as a percentage version of MACD. The PPO levels of the Dow Industrials (price > 20K) can be compared against the PPO levels of IBM (price < 200) because the PPO “levels” the playing field. In addition, PPO levels in one security can be compared over extended periods of time, even if the price has doubled or tripled. This is not the case for the MACD.
Limitations
Despite its advantages, the PPO is still not the best oscillator to identify overbought or oversold conditions because movements are unlimited (in theory). Levels for RSI and the Stochastic Oscillator are limited and this makes them better suited to identify overbought and oversold levels.
Source: Stockcharts
Multiple Moving AveragesThis is really simple. But useful for me as I don't have a paid account. No-pro users can only use 3 indicators at once and because I rely heavily on simple moving averages it can be a real pain.
This one indicator features:
20 MA
50 MA
100 MA
200 MA
which I find are the most useful overall. The 20 and 50 over all time frame but in particular < 1 day, the 100 and 200 at > 4 hr time frames. In general I don't use the 100 MA that much. The daily 200 MA is a critical support for many assets like stocks and cryptos. I'm by no means a pro and if you are learning I recommend becoming familiar with moving averages right at the beginning.
If you want to deactivate some of the lines, you can do it via the indicator's settings icon.
Exponential Moving Average (Set of 3) [Krypt] + 13/34 EMAsI took Krypt's script and essentially added on to it.
the 20/50/100/200 EMAs should be used together as support and resistance as normal.
Wait for price to break 200 EMA
Wait for 50 EMA to cross 200 EMA
Wait for pullback to 50 EMA to open position
20 and 100 EMAs are for extra information about moving support and resistance
and 13/34 EMAs should be used in conjunction
When 13 EMA crosses 34 EMA, open position
When price gets far from 13/34, close position (because price will attempt to revert back to mean)
This is better for scalping and swing trades than the 20/50/100/200 setup.
Twitter: @AzorAhai06
Ichimoku Cloud Score v1.0This script calculates a simple Ichimoku Score based on the signals documented here , with a few additions. Each of the score components can be individually weighted via the script inputs . The output is a plot of the normalized Ichimoku score, in the range of -100 to 100.
This script has been heavily modified from 'Ichimoku Cloud Signal Score v2.0.0 '. Credit to user 'dashed' for the initial implementation.
This has been modified with several refinements:
Clean/Organized Code
Simplified Inputs
Improved Style
Scores normalized to a range (-100, 100)
Bugfixes and Improvements
Script Inputs: i.imgur.com
Volume RatioDefinition:
Volume ratio can be obtained in a similar way to RSI.
Volume Ratio (%) = 100 - 100/(1+vr)
The parameter "vr" is defined as
vr=(A+U/2)/(D+U/2)
A=Total volume of the periods when the price advanced
D=Total volume of the periods when the price declined
U=Total volume of the periods when the price unchanged
After substitution, following expression can be derived and the denominator represents total volume of all periods.
Volume Ratio (%) = 100 x (A+U/2)/(A+D+U)
Notes:
A similar method to interpret RSI can be employed.
1) Overbought level over 70% and oversold level under 30%. These levels need to be adjusted according to the periods, time frames and issues.
2) Bullish picture over 50% line and bearish picture under 50% line.
3) Crossing oversold level to the upside can be taken as a confirmation of bullish reversal. - and vice versa for a bearish reversal.
4) After a long-term bearish market, the increase of volume can happen in the early stage of a bullish market.
5) Buying opportunity can be suggested when the volume ratio is declining and the price is either advancing or leveling off.
CCI with Volume Weighted EMA Here is an attempt to improve on the CCI using a volume weighted ema which is then plugged into the CCI formula.
Use:
The CCI with VW EMA is an oscillator that gives readings between -100 and +100. The usual use is to 'go long' with values over +100 and short on values less than -100.
Another use of this oscillator is a countertrend indicator where one sells at crosses under +100 and buys on crosses over -100.
Multi-Functional Fisher Transform MTF with MACDL TRIGGERWhat this indicator gives you is a true signal when price is exhausted and ready for a fast turnaround. Fisher Transform is set for multi-time frame and also allows the user to change the length. This way a user can compare two or more time spans and lengths to look for these MACDL divergent triggers after a Fisher exhaustion. With so many indicators, it's probably best to merge these indicators and change the Fisher and Trigger colors so you can still have a look at price action (remember to scale right after merger). I've noticed from time to time when you have Fisher 34 100 and 300 up and running on two different time frames such as 5 and 15 min charts, with MACDL triggers on the 100/300 or 34/100 you get a high probability trade trigger. However, there are rare exceptions such as when price moves in a parabolic state up or down for a long period where this indication does not work. Ideally this indicator works best in a sideways market or slow rising/descending moving market.
This indicator was worked on by Glaz, nmike and myself
LazyBear also introduced the MACDL indicator
CCI Crossover AlertThis very simple indicator will give you a blue background where the CCI crossed from below -100 to above -100, and a red background where it crossed from above 100 to below 100.
SPX + DJI Overlay Template1 second chart to tell direct of DJ vs SPX
//@version=5
indicator("SPX + DJI Overlay Template", overlay=true)
sym_spx = input.symbol("TVC:SPX", "SPX (derived / CFD)")
sym_dji = input.symbol("TVC:DJI", "DJI")
spx_close = request.security(sym_spx, timeframe.period, close)
dji_close = request.security(sym_dji, timeframe.period, close)
var float spx_base = na
var float dji_base = na
if barstate.isfirst
spx_base := spx_close
dji_base := dji_close
norm_spx = (spx_close / spx_base) * 100
norm_dji = (dji_close / dji_base) * 100
plot(norm_spx, color=color.blue, title="SPX (normalized)")
plot(norm_dji, color=color.orange, title="DJI (normalized)")
plot(norm_spx - norm_dji, color=color.red, title="Difference", style=plot.style_line)
Grouped Peaks Screener (5/5) V4//@version=6
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — screener ready", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0, format=format.volume)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int grp_ticks = array.new_int()
var int grp_counts = array.new_int()
var int grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int grp_start = array.new_int() // bar_index of earliest peak in group
var line grp_lines = array.new_line()
var bool grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// ---------- Screener-ready plot (use this plot as the Screener column/filter)
// Screener reads plots. Plot = 1 when there is at least one active grouped Equal H/L (count >= 2)
bool hasEqualHL = false
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
hasEqualHL := true
break
// Additional check: ensure we have enough data for the pivot detection
bool hasEnoughData = bar_index >= LEFT + RIGHT + 10 // Add some buffer
// Only consider EqualHL valid if we have enough data and it's within the current trading day
bool isValidEqualHL = hasEqualHL and hasEnoughData and allow_detection
float equalHLPlot = isValidEqualHL ? 1.0 : 0.0
// Hidden on chart but visible to Screener. If you prefer a visible mark remove `display=display.none`.
plot(equalHLPlot, title="EqualHL", style=plot.style_columns, color=color.new(color.yellow, 0), linewidth=2, display=display.none)
// invisible plot to keep indicator active
plot(na)
Grouped Peaks Screener (5/5) V3- Z//@version=6
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — screener ready", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0, format=format.volume)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int grp_ticks = array.new_int()
var int grp_counts = array.new_int()
var int grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int grp_start = array.new_int() // bar_index of earliest peak in group
var line grp_lines = array.new_line()
var bool grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// ---------- Screener-ready plot (use this plot as the Screener column/filter)
// Screener reads plots. Plot = 1 when there is at least one active grouped Equal H/L (count >= 2)
bool hasEqualHL = false
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
hasEqualHL := true
break
float equalHLPlot = hasEqualHL ? 1.0 : 0.0
// Hidden on chart but visible to Screener. If you prefer a visible mark remove `display=display.none`.
plot(equalHLPlot, title="EqualHL", style=plot.style_columns, color=color.new(color.yellow, 0), linewidth=2, display=display.none)
// invisible plot to keep indicator active
plot(na)
Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — V2//@version=6
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — screener ready", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int grp_ticks = array.new_int()
var int grp_counts = array.new_int()
var int grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int grp_start = array.new_int() // bar_index of earliest peak in group
var line grp_lines = array.new_line()
var bool grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// ---------- Screener-ready plot (use this plot as the Screener column/filter)
// Screener reads plots. Plot = 1 when there is at least one active grouped Equal H/L (count >= 2)
bool hasEqualHL = false
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
hasEqualHL := true
break
float equalHLPlot = hasEqualHL ? 1.0 : 0.0
// Hidden on chart but visible to Screener. If you prefer a visible mark remove `display=display.none`.
plot(equalHLPlot, title="EqualHL", style=plot.style_columns, color=color.new(color.yellow, 0), linewidth=2, display=display.none)
// invisible plot to keep indicator active
plot(na)
Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — V1//@version=6
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — v2", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int grp_ticks = array.new_int()
var int grp_counts = array.new_int()
var int grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int grp_start = array.new_int() // bar_index of earliest peak in group
var line grp_lines = array.new_line()
var bool grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// invisible plot to keep indicator active
plot(na)
Previous candle - D-T.R.A.D.I.N.G//@version=6
indicator("Previous candle - byDen (Body % line & Wick size) ", overlay=true, max_boxes_count=200, max_lines_count=200, max_labels_count=200)
// ---------------- Inputs ----------------
showHighLow = input.bool(true, "Show High/Low lines")
showHLBox = input.bool(true, "Show High-Low Box (full range)")
showMidLine = input.bool(true, "Show Middle Line")
showCloseLine = input.bool(true, "Show Close Line")
showBodyPct = input.bool(true, "Show Body % Line")
enableWickBox = input.bool(true, "Show Wick-Body Box (previous candle)")
keepLastWick = input.bool(false, "Keep last Wick-Box when disabled")
bodyPct = input.float(25.0, "Body % Level", step=0.1, minval=0.0, maxval=100.0)
// colors / styles
cHighLow = input.color(color.white, "H/L Line Color")
hlStyleStr = input.string("solid", "H/L Style", options= )
hlWidth = input.int(1, "H/L Width", minval=1, maxval=5)
cMid = input.color(color.yellow, "Mid Line Color")
midStyleStr = input.string("dashed", "Mid Style", options= )
midWidth = input.int(1, "Mid Width", minval=1, maxval=5)
cClose = input.color(color.yellow, "Close Line Color")
closeStyleStr = input.string("dotted", "Close Style", options= )
closeWidth = input.int(1, "Close Width", minval=1, maxval=5)
cBoxBorder = input.color(color.red, "HL Box Border")
cBoxFill = color.new(input.color(color.gray, "HL Box Fill"), 85)
cBullBox = color.new(input.color(color.green, "Bull WickBox"), 80)
cBearBox = color.new(input.color(color.red, "Bear WickBox"), 80)
cWboxBorder = input.color(color.white, "WickBox Border")
cBodyPct = input.color(color.blue, "Body % Line Color")
bodyPctStyle = input.string("dashed", "Body % Line Style", options= )
bodyPctWidth = input.int(1, "Body % Line Width", minval=1, maxval=5)
// table colors / pip size
cPipBox = input.color(color.new(color.blue, 80), "Table Background Color")
cPipText = input.color(color.white, "Table Text Color")
pipSize = input.float(0.1, "Pip size (XAUUSD=0.1)", step=0.01)
wickLabelText = input.string("Wick", "Opis za wick")
bodyLabelText = input.string("H/L-Body%", "Opis za H/L-Body%")
descFontSizeStr = input.string("small", "Table Description Font Size", options= )
valFontSizeStr = input.string("normal", "Table Value Font Size", options= )
// custom texts for line labels
txtHigh = input.string("High", "Label Text - High")
txtLow = input.string("Low", "Label Text - Low")
txtMid = input.string("Mid", "Label Text - Mid")
txtClose = input.string("Close", "Label Text - Close")
txtBody = input.string("Body%", "Label Text - Body %")
lineFontSizeStr = input.string("small", "Line Label Font Size", options= )
lineLabelPos = input.string("above", "Line Label Position", options= )
lineLabelAlign = input.string("center", "Line Label Align", options= )
// ---------------- Candle Display Options ----------------
highDisplay = input.string("Both", "High Line Display", options= )
lowDisplay = input.string("Both", "Low Line Display", options= )
midDisplay = input.string("Both", "Mid Line Display", options= )
closeDisplay = input.string("Both", "Close Line Display", options= )
bodyDisplay = input.string("Both", "Body % Line Display", options= )
hlBoxDisplay = input.string("Both", "HL Box Display", options= )
wickBoxDisplay = input.string("Both", "Wick Box Display", options= )
// timeframe filter
enabledTFs = input.string("1,5,15,30,60,240,D", "Enable on timeframes")
// ---------------- Helpers ----------------
is_tf_enabled(tfStr) =>
str.contains("," + enabledTFs + ",", "," + tfStr + ",")
line_style_from_str(s) =>
s == "dotted" ? line.style_dotted : s == "dashed" ? line.style_dashed : line.style_solid
get_font_size(fs) =>
fs == "tiny" ? size.tiny : fs == "small" ? size.small : fs == "large" ? size.large : fs == "huge" ? size.huge : size.normal
get_label_style(a) =>
a == "left" ? label.style_label_left : a == "right" ? label.style_label_right : label.style_label_center
get_y_pos(basePrice, pos) =>
pos == "above" ? basePrice + (syminfo.mintick * 10) : basePrice - (syminfo.mintick * 10)
// ---------------- Data ----------------
tf = timeframe.period
= request.security(syminfo.tickerid, tf, , lookahead=barmerge.lookahead_off)
midLevel = (prevH + prevL) / 2
isBull = prevC > prevO
// ---------------- Persistent ----------------
var line lHigh = na
var line lLow = na
var line lMid = na
var line lClose = na
var line lBodyPct = na
var box bHL = na
var box wickBox = na
var table wickTable = na
var label labHigh = na
var label labLow = na
var label labMid = na
var label labClose = na
var label labBodyPct = na
offsetBars = input.int(20, "Bars right", minval=1, maxval=500)
leftX_base = bar_index
rightX_base = bar_index + offsetBars
if na(wickTable)
wickTable := table.new(position.bottom_right, 2, 2, border_width=1, frame_color=color.white)
// ---------------- DRAW ----------------
if barstate.isconfirmed and is_tf_enabled(tf)
// delete previous labels
if not na(labHigh)
label.delete(labHigh)
labHigh := na
if not na(labLow)
label.delete(labLow)
labLow := na
if not na(labMid)
label.delete(labMid)
labMid := na
if not na(labClose)
label.delete(labClose)
labClose := na
if not na(labBodyPct)
label.delete(labBodyPct)
labBodyPct := na
// --- DRAW HIGH ---
drawHigh = (highDisplay=="Both") or (highDisplay=="Bullish" and isBull) or (highDisplay=="Bearish" and not isBull)
if showHighLow and drawHigh
if not na(lHigh)
line.delete(lHigh)
lHigh := line.new(leftX_base, prevH, rightX_base, prevH, xloc=xloc.bar_index, color=cHighLow, width=hlWidth, style=line_style_from_str(hlStyleStr))
labHigh := label.new(rightX_base, get_y_pos(prevH,lineLabelPos), txtHigh, xloc=xloc.bar_index, yloc=yloc.price, style=get_label_style(lineLabelAlign), textcolor=cHighLow, size=get_font_size(lineFontSizeStr))
// --- DRAW LOW ---
drawLow = (lowDisplay=="Both") or (lowDisplay=="Bullish" and isBull) or (lowDisplay=="Bearish" and not isBull)
if showHighLow and drawLow
if not na(lLow)
line.delete(lLow)
lLow := line.new(leftX_base, prevL, rightX_base, prevL, xloc=xloc.bar_index, color=cHighLow, width=hlWidth, style=line_style_from_str(hlStyleStr))
labLow := label.new(rightX_base, get_y_pos(prevL,lineLabelPos), txtLow, xloc=xloc.bar_index, yloc=yloc.price, style=get_label_style(lineLabelAlign), textcolor=cHighLow, size=get_font_size(lineFontSizeStr))
// --- DRAW MID ---
drawMid = (midDisplay=="Both") or (midDisplay=="Bullish" and isBull) or (midDisplay=="Bearish" and not isBull)
if showMidLine and drawMid
if not na(lMid)
line.delete(lMid)
lMid := line.new(leftX_base, midLevel, rightX_base, midLevel, xloc=xloc.bar_index, color=cMid, width=midWidth, style=line_style_from_str(midStyleStr))
labMid := label.new(rightX_base, get_y_pos(midLevel,lineLabelPos), txtMid, xloc=xloc.bar_index, yloc=yloc.price, style=get_label_style(lineLabelAlign), textcolor=cMid, size=get_font_size(lineFontSizeStr))
// --- DRAW CLOSE ---
drawClose = (closeDisplay=="Both") or (closeDisplay=="Bullish" and isBull) or (closeDisplay=="Bearish" and not isBull)
if showCloseLine and drawClose
if not na(lClose)
line.delete(lClose)
lClose := line.new(leftX_base, prevC, rightX_base, prevC, xloc=xloc.bar_index, color=cClose, width=closeWidth, style=line_style_from_str(closeStyleStr))
labClose := label.new(rightX_base, get_y_pos(prevC,lineLabelPos), txtClose, xloc=xloc.bar_index, yloc=yloc.price, style=get_label_style(lineLabelAlign), textcolor=cClose, size=get_font_size(lineFontSizeStr))
// --- DRAW BODY% ---
float bodySize = math.abs(prevC-prevO)
float levelPct = na
drawBody = (bodyDisplay=="Both") or (bodyDisplay=="Bullish" and isBull) or (bodyDisplay=="Bearish" and not isBull)
if showBodyPct and bodySize>0 and drawBody
if not na(lBodyPct)
line.delete(lBodyPct)
levelPct := isBull ? (prevC - (bodyPct/100)*bodySize) : (prevC + (bodyPct/100)*bodySize)
lBodyPct := line.new(leftX_base, levelPct, rightX_base, levelPct, xloc=xloc.bar_index, color=cBodyPct, width=bodyPctWidth, style=line_style_from_str(bodyPctStyle))
labBodyPct := label.new(rightX_base, get_y_pos(levelPct,lineLabelPos), txtBody, xloc=xloc.bar_index, yloc=yloc.price, style=get_label_style(lineLabelAlign), textcolor=cBodyPct, size=get_font_size(lineFontSizeStr))
// --- BOXES ---
drawHLBox = (hlBoxDisplay=="Both") or (hlBoxDisplay=="Bullish" and isBull) or (hlBoxDisplay=="Bearish" and not isBull)
if showHLBox and drawHLBox
if not na(bHL)
box.delete(bHL)
bHL := box.new(leftX_base, prevH, rightX_base, prevL, xloc=xloc.bar_index, border_color=cBoxBorder, bgcolor=cBoxFill)
drawWickBox = (wickBoxDisplay=="Both") or (wickBoxDisplay=="Bullish" and isBull) or (wickBoxDisplay=="Bearish" and not isBull)
if enableWickBox and drawWickBox
float topP = isBull ? prevH : prevC
float botP = isBull ? prevC : prevL
if not na(topP) and not na(botP)
if not na(wickBox)
box.delete(wickBox)
wickBox := box.new(leftX_base, topP, rightX_base, botP, xloc=xloc.bar_index, border_color=cWboxBorder, bgcolor=(isBull?cBullBox:cBearBox))
else if not enableWickBox and not keepLastWick and not na(wickBox)
box.delete(wickBox)
wickBox := na
// --- TABLE (vedno na obeh) ---
float wickPips = isBull ? prevH-prevC : prevC-prevL
float bodyPips = na
if not na(levelPct)
bodyPips := isBull ? math.round((prevH-levelPct)/pipSize) : math.round((levelPct-prevL)/pipSize)
table.cell(wickTable, 0, 0, wickLabelText, text_color=cPipText, bgcolor=cPipBox, text_size=get_font_size(descFontSizeStr))
table.cell(wickTable, 1, 0, str.tostring(wickPips/pipSize), text_color=cPipText, bgcolor=cPipBox, text_size=get_font_size(valFontSizeStr))
if not na(bodyPips)
table.cell(wickTable, 0, 1, bodyLabelText, text_color=cPipText, bgcolor=cPipBox, text_size=get_font_size(descFontSizeStr))
table.cell(wickTable, 1, 1, str.tostring(bodyPips), text_color=cPipText, bgcolor=cPipBox, text_size=get_font_size(valFontSizeStr))
Relative Performance Indicator - TrendSpider StyleRelative Performance Indicator - TrendSpider Style
📈 Overview
This Relative Performance (RP) indicator measures how your stock is performing compared to a benchmark index, displayed as a percentile ranking from 0-100. Based on TrendSpider's methodology, it answers the critical question: "Is this stock a leader or a laggard?"
Unlike simple ratio charts, this indicator uses percentile ranking to normalize relative performance, making it easy to identify when a stock is showing exceptional strength (>80) or concerning weakness (<20) compared to its historical relationship with the benchmark.
✨ Key Features
Three Calculation Modes:
Quarterly: 3-month relative performance for swing trading
Yearly: Weighted 4-quarter performance for position trading
TechRank: Composite of 6 technical indicators for multi-factor analysis
Clean Visual Design:
Green fills above 80 (strong outperformance)
Red fills below 20 (significant underperformance)
Dotted median line at 50 for quick reference
Current value label for instant reading
Flexible Benchmarks:
Compare against major indices (SPY, QQQ, IWM)
Sector ETFs for within-sector analysis
Custom symbols for specialized comparisons
Built-in Alerts:
Strong performance zone entry (>80)
Weak performance zone entry (<20)
Median crossovers (50 level)
📊 How To Use
Buy Signals:
RP crosses above 80: Stock entering leadership status
RP holding above 60: Maintaining relative strength
RP rising while price consolidating: Accumulation phase
Sell/Avoid Signals:
RP drops below 50: Losing relative strength
RP below 20: Significant underperformance
RP falling while price rising: Bearish divergence
Sector Rotation:
Compare multiple assets to find strongest sectors
Rotate into high RP assets (>70)
Exit low RP positions (<30)
🎯 Reading The Values
80-100: Exceptional outperformance - Strong buy/hold
60-80: Moderate outperformance - Hold positions
40-60: Market perform - No edge
20-40: Underperformance - Caution/reduce
0-20: Severe underperformance - Avoid/exit
⚙️ Calculation Method
Calculates percentage performance of both your stock and the benchmark
Finds the performance differential
Ranks this differential against historical values using percentile analysis
Normalizes to 0-100 scale for easy interpretation
This percentile approach adapts to different market conditions and volatility regimes, providing consistent signals whether in trending or choppy markets.
💡 Pro Tips
For Growth Stocks: Use quarterly mode with QQQ as benchmark
For Value Stocks: Use yearly mode with SPY as benchmark
For Small Caps: Compare against IWM, not SPY
For Sector Analysis: Use sector ETFs (XLK, XLF, XLE, etc.)
Combine with Price Action: High RP + price breakout = powerful signal
⚠️ Important Notes
RP is relative, not absolute - stocks can fall with high RP if the market falls harder
Choose appropriate benchmarks for meaningful comparisons
Best used in conjunction with price action and volume analysis
Historical lookback period affects sensitivity (adjustable in settings)
🔧 Customization
Fully customizable visual settings, thresholds, calculation periods, and smoothing options. Adjust the normalization lookback period (default 252 days) to fine-tune sensitivity to your trading timeframe.
📌 Credit
Inspired by TrendSpider's Relative Performance implementation, adapted for TradingView with enhanced customization options and Pine Script v6 optimization.
Tags to include: relativeperformance, relativestrength, percentile, ranking, sectorrotation, benchmark, outperformance, trendspider, marketbreadth, strengthindicator
Category: Momentum Indicators / Trend Analysis
Feel free to modify this description to match your style or add any specific points you want to emphasize!
Hurst‑Millard FLD Normalized 2.0 – Signals "Hurst-Millard FLD Normalized 2.0 – Signals" indicator. It analyzes price data using a combination of moving averages (MAs) and the Hurst exponent to decompose price movements into trend, swing, and noise components, generating buy and sell signals. Here's a brief overview of its functionality:Inputs and Modes:Offers Auto Mode (cycle-based) and Manual Mode for configuring three moving averages: Long-Term (LT), Mid-Term (MT), and Short-Term (ST).
Auto Mode calculates MA lengths and offsets based on user-defined target cycle lengths (e.g., LT: 400 bars, MT: 100 bars, ST: 25 bars) with predefined offset ratios (0.2, 0.333, 0.5 respectively).
Manual Mode allows direct input of MA lengths and offsets.
Moving Averages:Computes Simple Moving Averages (SMAs) for LT, MT, and ST based on the closing price.
Applies forward-shifting to simulate future price behavior (e.g., maLongFwd shifts the LT MA by the specified offset).
Decomposition:Trend: Derived from the forward-shifted LT MA (maLongFwd).
Swing: Calculated as the difference between MT and LT MAs, scaled as a percentage of the closing price and amplified (using ATR or a manual factor).
Noise: Calculated as the difference between ST and MT MAs, similarly scaled and amplified.
Hurst Exponent:Estimates the Hurst exponent to measure the persistence or mean-reversion of the noise component.
Uses a 50-bar lookback period, smoothed with a 5-period SMA.
Signal Generation:Generates buy signals when the noise component is less than the swing component and their difference is within a user-defined proximity threshold (default: 25% of swing).
Generates sell signals when noise exceeds swing within the same threshold.
Signals are plotted as diamond shapes at the calculated proximity price level.
Visualization:Plots the trend, swing, and noise components as lines with customizable colors and gradient intensity based on their relative strength.
Optional debugging plots for raw forward-shifted MAs and proximity thresholds.
Displays a periodic debug table (every 100 bars) showing key metrics like close price, MAs, trend, swing, noise, Hurst exponent, and more.
Additional Features:Supports ATR-based amplification for scaling swing and noise.
Allows customization of signal colors, diamond offsets, and proximity thresholds.
Includes debugging options to visualize raw MAs and proximity bands.
In summary, this indicator uses cycle-based or manually configured MAs to break down price action into trend, swing, and noise, calculates the Hurst exponent for noise analysis, and generates buy/sell signals based on the relationship between swing and noise within a proximity threshold. It’s designed for traders to identify potential trend reversals or continuations.
VWAP angleVWAP Angle Indicator
The VWAP Angle indicator is a technical analysis tool designed to measure the directional momentum of the Volume Weighted Average Price (VWAP). Rather than simply plotting the VWAP line itself, this indicator calculates the angle of the VWAP's trajectory over a specified lookback period. It converts the slope into degrees using arctangent mathematics, normalized by the Average True Range (ATR) to account for price volatility. The result is a momentum oscillator that ranges from -100 to +100, providing traders with a clear visual representation of whether the VWAP is trending sharply upward, downward, or moving sideways.
The indicator features customizable overbought and oversold zones (defaulting to +65 and -65 respectively) with color-coded bands to help identify potential reversal points or extreme market conditions. A dynamic color gradient transitions from cyan (bullish angles) to orange (bearish angles), making it easy to spot trend changes at a glance. The visualization includes multiple threshold bands at ±65 and ±80 levels, with shaded regions that intensify as the angle reaches extreme values, helping traders identify when momentum may be overextended.
Built-in alert functionality notifies traders when the VWAP angle crosses into overbought or oversold territories, with optional visual labels appearing directly on the indicator panel. This makes it particularly useful for automated trading strategies or for traders who want real-time notifications of potential trend exhaustion or reversal setups. The indicator works best when combined with other technical tools to confirm signals and filter out false alerts during choppy or ranging market conditions.
Adaptive HMA SignalsAdaptive HMA Signals
This indicator pairs nicely with the Contrarian 100 MA and can be located here:
Overview
The "Adaptive HMA Signals" indicator is a sophisticated technical analysis tool designed for traders aiming to capture trend changes with precision. By leveraging Hull Moving Averages (HMAs) that adapt dynamically to market conditions (volatility or volume), this indicator generates actionable buy and sell signals based on price interactions with adaptive HMAs and slope analysis. Optimized for daily charts, it is highly customizable and suitable for trading forex, stocks, cryptocurrencies, or other assets. The indicator is ideal for swing traders and trend followers seeking to time entries and exits effectively.
How It Works
The indicator uses two adaptive HMAs—a primary HMA and a minor HMA—whose periods adjust dynamically based on user-selected market conditions (volatility via ATR or volume via RSI). It calculates the slope of the primary HMA to identify trend strength and generates exit signals when the price crosses the minor HMA under specific slope conditions. Signals are plotted as circles above or below the price, with inverted colors (white for buy, blue for sell) to enhance visibility on any chart background.
Key Components
Adaptive HMAs: Two HMAs (primary and minor) with dynamic periods that adjust based on volatility (ATR-based) or volume (RSI-based) conditions. Periods range between user-defined minimum and maximum values, adapting by a fixed percentage (3.141%).
Slope Analysis: Calculates the slope of the primary HMA over a 34-bar period to gauge trend direction and strength, normalized using market range data.
Signal Logic: Generates buy signals (white circles) when the price falls below the minor HMA with a flat or declining slope (indicating a potential trend reversal) and sell signals (blue circles) when the price rises above the minor HMA with a flat or rising slope.
Signal Visualization: Plots signals at an offset based on ATR for clarity, using semi-transparent colors to avoid chart clutter.
Mathematical Concepts
Dynamic Period Adjustment:
Primary HMA period adjusts between minLength (default: 144) and maxLength (default: 200).
Minor HMA period adjusts between minorMin (default: 55) and minorMax (default: 89).
Periods decrease by 3.141% under high volatility/volume and increase otherwise.
HMA Calculation:
Uses the Hull Moving Average formula: WMA(2 * WMA(src, length/2) - WMA(src, length), sqrt(length)).
Provides a smoother, faster-responding moving average compared to traditional MAs.
Slope Calculation:
Computes the slope of the primary HMA using a 34-bar period, normalized by the market range (highest high - lowest low over 34 bars).
Slope angle is converted to degrees using arccosine for intuitive trend strength interpretation.
Signal Conditions:
Buy: Slope ≥ 17° (flat or rising), price < minor HMA, low volatility/volume.
Sell: Slope ≤ -17° (flat or declining), price > minor HMA, low volatility/volume.
Signals are triggered only on confirmed bars to avoid repainting.
Entry and Exit Rules
Buy Signal (White Circle): Triggered when the price crosses below the minor HMA, the slope of the primary HMA is flat or rising (≥17°), and volatility/volume is low. The signal appears as a white circle above the price bar, offset by 0.72 * ATR(5).
Sell Signal (Blue Circle): Triggered when the price crosses above the minor HMA, the slope of the primary HMA is flat or declining (≤-17°), and volatility/volume is low. The signal appears as a blue circle below the price bar, offset by 0.72 * ATR(5).
Exit Rules: Exit a buy position on a sell signal and vice versa. Combine with other tools (e.g., support/resistance, RSI) for additional confirmation. Always apply proper risk management.
Recommended Usage
The "Adaptive HMA Signals" indicator is optimized for daily charts but can be adapted to other timeframes (e.g., 1H, 4H) with adjustments to period lengths. It performs best in trending or range-bound markets with clear reversal points. Traders should:
Backtest the indicator on their chosen asset and timeframe to validate signal reliability.
Combine with other technical tools (e.g., trendlines, Fibonacci retracements) for stronger trade setups.
Adjust minLength, maxLength, minorMin, and minorMax based on market volatility and timeframe.
Use the Charger input to toggle between volatility (ATR) and volume (RSI) adaptation for optimal performance in specific market conditions.
Customization Options
Source: Choose the price source (default: close).
Show Signals: Toggle visibility of buy/sell signals (default: true).
Charger: Select adaptation trigger—Volatility (ATR-based) or Volume (RSI-based) (default: Volatility).
Main HMA Periods: Set minimum (default: 144) and maximum (default: 200) periods for the primary HMA.
Minor HMA Periods: Set minimum (default: 55) and maximum (default: 89) periods for the minor HMA.
Slope Period: Fixed at 34 bars for slope calculation, adjustable via code if needed.
Why Use This Indicator?
The "Adaptive HMA Signals" indicator combines the responsiveness of HMAs with dynamic adaptation to market conditions, offering a robust tool for identifying trend reversals. Its clear visual signals, customizable periods, and adaptive logic make it versatile for various markets and trading styles. Whether you’re a beginner or an experienced trader, this indicator enhances your ability to time entries and exits with precision.
Tips for Users
Test the indicator thoroughly on your chosen market and timeframe to optimize settings (e.g., adjust period lengths for non-daily charts).
Use in conjunction with price action or other indicators (e.g., RSI, MACD) for stronger trade confirmation.
Monitor volatility/volume conditions to ensure the Charger setting aligns with market dynamics.
Ensure your chart timeframe aligns with the selected period lengths for accurate signal generation.
Apply strict risk management to protect against false signals in choppy markets.
Happy trading with the Adaptive HMA Signals indicator! Share your feedback and strategies in the TradingView community!
MAs+Engulfing O caminho das Criptos
This indicator overlays multiple moving averages (EMAs 20/50/100/200 and SMA 200) and highlights bullish/bearish engulfing candles by dynamically coloring the candle body. When a bullish engulfing is detected, the candle appears as a strong dark green; for bearish engulfing, a more vivid red. Normal candles keep classic lime/red colors. Visual alerts and bar coloring make price-action patterns instantly visible.
Includes built-in alert conditions for both patterns, supporting both trading automation and education. The tool upgrades trend-following setups by combining structure with automatic price action insights.
Este indicador combina médias móveis (EMAs de 20/50/100/200 e SMA 200) com detecção de engolfo de alta/baixa, colorindo o candle automaticamente: engolfo de alta com verde escuro, engolfo de baixa com vermelho destacado. Inclui alertas automáticos para ambos os padrões, perfeito para análise visual, estratégia, ou ensino.