The classical RSI is computed on bar-to-bar price changes. The Market Structure RSI takes a different approach: instead of feeding RSI with raw price returns, it feeds it with a series that counts how many recent swing highs price has broken above (positive contribution) and how many recent swing lows price has broken below (negative contribution). The RSI formula is then applied to that running count.
The result is an oscillator that does not respond to every tick of price action, but to the structural events of the market — the moments when price actually breaks meaningful pivots. When it pushes towards 100 the market has been making higher highs by repeatedly violating swing highs; when it sinks towards 0 the market has been taking out swing lows. Around 50 the structural picture is balanced.
A 3-bar pivot is the simplest possible structural marker:
Each new pivot is appended to a list with its price and bar index. The result is a chronological catalogue of recent structural turning points, growing one entry at a time.
On every bar the indicator scans the most recent pivots from newest to oldest:
A single bar can therefore break several pivots in cascade — a strong impulsive move that takes out three previous pivot highs in one go contributes +3 to the count, while a quiet bar in the middle of a range contributes nothing.
The cumulative break-count series is fed into a standard RSI of length rsiLength. RSI takes the role it was designed for — measuring the relative weight of positive vs. negative changes over a window — but it now operates on structural events rather than on raw price changes. Optional moving-average smoothing on top of the RSI provides a slower confirmation line.
The Market Structure RSI is read like a classical RSI but with a structural meaning attached to each level:
Because the input is a count of structural events rather than continuous price, the oscillator tends to be less noisy than a standard RSI in choppy markets: as long as no pivots are broken, the count series stays flat and the RSI reverts towards 50.
//------------------------------------------------------------//
//PRC_Market Structure RSI
//version = 0
//27.05.24
//Iván González @ www.prorealcode.com
//Sharing ProRealTime knowledge
//------------------------------------------------------------//
//-----Inputs-------------------------------------------------//
rsiLength=20
OBlvl=80
OSlvl=20
strCloseType=0
maBool=1
maSelection=1
maLength=8
color=1
signals=0
//------------------------------------------------------------//
//-----Close Type---------------------------------------------//
if strCloseType then
closeTypeBull=close[1]
closeTypeBear=close[1]
else
closeTypeBull=high[1]
closeTypeBear=low[1]
endif
//-----Get Pivots---------------------------------------------//
pH=high[3]<high[2] and high[2]>high[1]
pL=low[3]>low[2] and low[2]<low[1]
if pH then
$highprice[z+1]=high[2]
$highidx[z+1]=barindex[2]
z=z+1
endif
if pL then
$lowprice[t+1]=low[2]
$lowidx[t+1]=barindex[2]
t=t+1
endif
//------------------------------------------------------------//
//-----Get Source for RSI calculation-------------------------//
$count[0]=0
if isset($highprice[z]) then
for i=z downto 0 do
if closeTypeBull>$highprice[i] then
$highprice[i]=exp(10)
$count[r+1]=$count[r]+1
r=r+1
else
break
endif
next
endif
if isset($lowprice[t]) then
for j=t downto 0 do
if closeTypeBear<$lowprice[j] then
$lowprice[j]=0
$count[r+1]=$count[r]-1
r=r+1
else
break
endif
next
endif
//------------------------------------------------------------//
//-----Get total array count----------------------------------//
total=0
for k=0 to r do
total=total+$count[r]
next
//------------------------------------------------------------//
//-----RSI total----------------------------------------------//
rsitotal=rsi[rsilength](total)
//------------------------------------------------------------//
//-----RSI Moving Average-------------------------------------//
if maBool then
rsmMATotal=average[maLength,maSelection](rsitotal)
else
rsmMATotal=undefined
endif
//------------------------------------------------------------//
//-----Plot configuration-------------------------------------//
if color then
if rsitotal > 50 then
rc=0
gc=255
else
rc=255
gc=0
endif
colorbetween(rsitotal,50,rc,gc,0,50)
endif
//------------------------------------------------------------//
//-----Draw Signals-------------------------------------------//
if signals then
if rsiTotal crosses under OBlvl then
drawpoint(barindex,OBlvl,2)coloured("red")
elsif rsiTotal crosses over OSlvl then
drawpoint(barindex,OSlvl,2)coloured("green")
endif
endif
//------------------------------------------------------------//
return rsitotal, rsmMATotal coloured("blue")style(line,4), OBlvl as "Overbought Level"style(dottedline2), OSlvl as "Oversold level"style(dottedline2)
You must be logged in to post a comment.
I don't know... I've check it and works fine. myrsi, ignored, ignored, ignored=CALL "PRC_Market Structure RSI" [20] if not longonmarket and myrsi crosses over 50 then buy 1 contract at market endif if longonmarket and myrsi crosses under 80 then sell at market endif graph myrsi
There was indeed a bug that only showed up on high-priced instruments such as Wall Street, Nasdaq, Bitcoin or Berkshire-A. On those, the oscillator stayed pinned near the upper band instead of fluctuating.
Root cause: whenever a pivot was broken, the code marked it as “consumed” by overwriting its stored price with exp(10) (≈ 22 026), the idea being that no price could ever trade above that value, so the pivot would never be counted again. That assumption holds for stocks priced in tens or hundreds, but breaks down on assets quoted above ~22 000 — there, the condition closeTypeBull > exp(10) keeps evaluating to true bar after bar, so the same pivots are recounted indefinitely and the running count series saturates the RSI.
I’ve rewritten the pivot-break detection so it no longer depends on the magnitude of the price: broken pivots are popped out of the stack instead of marked with a “magic number”. I also removed a typo in the count-accumulation loop that was making the issue worse.
Updated code (v1):
//------------------------------------------------------------//
//PRC_Market Structure RSI
//version = 1
//05.05.26
//Iván González @ www.prorealcode.com
//Sharing ProRealTime knowledge
//
//v1: Bug fix for high-priced instruments (DJI, BTC, Berkshire,
// Nasdaq…). Replaced the exp(10) "deleted-pivot" marker —
// which fails when price > ~22 000 — with proper pop logic,
// and fixed a total-accumulation typo.
//------------------------------------------------------------//
//-----Inputs-------------------------------------------------//
rsiLength=20
OBlvl=80
OSlvl=20
strCloseType=0
maBool=1
maSelection=1
maLength=8
color=1
signals=0
//------------------------------------------------------------//
//-----Close Type---------------------------------------------//
if strCloseType then
closeTypeBull=close[1]
closeTypeBear=close[1]
else
closeTypeBull=high[1]
closeTypeBear=low[1]
endif
//------------------------------------------------------------//
//-----Get Pivots---------------------------------------------//
pH=high[3]<high[2] and high[2]>high[1]
pL=low[3]>low[2] and low[2]<low[1]
if pH then
z=z+1
$highprice[z]=high[2]
endif
if pL then
t=t+1
$lowprice[t]=low[2]
endif
//------------------------------------------------------------//
//-----Cascade-break detection (price-magnitude independent)--//
while z>0 and closeTypeBull>$highprice[z] do
total=total+1
z=z-1
wend
while t>0 and closeTypeBear<$lowprice[t] do
total=total-1
t=t-1
wend
//------------------------------------------------------------//
//-----RSI total----------------------------------------------//
rsitotal=rsi[rsiLength](total)
//------------------------------------------------------------//
//-----RSI Moving Average-------------------------------------//
if maBool then
rsmMATotal=average[maLength,maSelection](rsitotal)
else
rsmMATotal=undefined
endif
//------------------------------------------------------------//
//-----Plot configuration-------------------------------------//
if color then
if rsitotal > 50 then
rc=0
gc=255
else
rc=255
gc=0
endif
colorbetween(rsitotal,50,rc,gc,0,50)
endif
//------------------------------------------------------------//
//-----Draw Signals-------------------------------------------//
if signals then
if rsiTotal crosses under OBlvl then
drawpoint(barindex,OBlvl,2)coloured("red")
elsif rsiTotal crosses over OSlvl then
drawpoint(barindex,OSlvl,2)coloured("green")
endif
endif
//------------------------------------------------------------//
return rsitotal, rsmMATotal coloured("blue")style(line,4), OBlvl as "Overbought Level"style(dottedline2), OSlvl as "Oversold level"style(dottedline2)
If you use one of the indicator as part of a strategy, the values you obtained after the call in the strategy do not equal the values of the indicator itself.
Example :
indicator1, indicator2, ignored, ignored = CALL "PRC_Market Structure RSI"[20, 80, 20, 0, 1, 1, 8, 1, 0]
Graph indicator1 coloured (255,0,0)
If I compare the value of the indicator in the indicator panel and the value of the indicator plotted as part of the strategy, the values are different
Would you know why ?