Can anyone tell me how to scan for stocks that have traded within a 20% range over the past year? For example, today's price is 30. 20% of 30 is 6. So a stock that has traded between 24 and 36 over the last year would meet the criteria. Looking for stocks under $50/share. Thanks for reading...

i figured my range differently, the total from lowest to highest, not just from midlevel to highest.

so you will want to use 40% to get the equivelent of the 20% in your example.

-----------------------

check if past prices stayed within a % range of the current price

here are several versions

these studies calculate a total $ range that is some % of the close.

the default is 20%. this range is centered on the close price.

the upper limit is close + 10%. the lower limit is close - 10%.

when i want a column or scan study, i usually start with a lower study, and get it to work. then remove most of the output code to make a column/scan study. since this applied to prices, i started with an upper study.

---------------------------

test study - upper

draw lines for close, upper and lower limits.

a vertical line for the start of the period.

default data is days , and 252 bars , which is a year.

several labels display some $ and % stats.

the last 3 labels show if price stayed within the limits or went outside them.

set the percent to a big number to test if price stayed within the desired range.

Ruby:

```
# price_in_rng_past_xbars_upper_0
# price_in_rng_past_xmonths_0_lower
# test , upper study
# check if a stock has stayed within a price range, over a time period
declare upper;
def na = double.nan;
input range_percent = 20.0;
# 1 year of data
input time_period = AggregationPeriod.day;
input quantity_of_time_periods = 252;
def rnghi = highest(high(period = time_period), quantity_of_time_periods);
def rnglo = lowest(low(period = time_period), quantity_of_time_periods);
def rng_first = (!isnan(close[-(quantity_of_time_periods-1)]) and isnan(close[-(quantity_of_time_periods-0)]));
#----------------------------------------
# save the current close price, on all bars
# https://usethinkscript.com/threads/current-price-line-indicator-for-thinkorswim.8793/
# Line At Price Mobius
# Alternative to using the HighestAll() function
def barsBack = 1000;
def c = if !IsNaN(close) and IsNaN(close[-1])
then close
else c[1];
def lastcls = if isNaN(close[-barsBack])
then c[-barsBack]
else Double.NaN;
input show_close_line = yes;
plot line = if show_close_line then lastcls else na;
line.SetLineWeight(1);
line.SetDefaultColor(Color.LIME);
line.SetStyle(Curve.MEDIUM_DASH);
#-----------------------------------------
# compare current close price to price highest and lowest
def range1 = round(lastcls * range_percent/100, 2);
def top = round(lastcls + (range1/2),2);
def bot = round(lastcls - (range1/2),2);
#---------------------
## for testing in a lower study
#input months_back = 12;
#input agg1 = AggregationPeriod.month;
#def rnghi = highest(high(period = agg1), months_back);
#def rnglo = lowest(low(period = agg1), months_back);
#--------------------
# show a vertical line, x days back. start of data range
input show_vertical_line = yes;
addverticalline(show_vertical_line and rng_first," RANGE START", color.yellow);
# = 1 if price was out of range
def above_rng = (rnghi > top);
def below_rng = (rnglo < bot);
# = 1 if price stayed in the range
def within_rng = (!above_rng and !below_rng);
addlabel(1, "range : " + aspercent(range_percent/100), color.yellow);
addlabel(1, " of " + asdollars(close), color.yellow);
addlabel(1, "range : " + asdollars(range1), color.yellow);
addlabel(1, " ", color.black);
addlabel(1, " +- range : " + asdollars(range1/2), color.yellow);
addlabel(1, " ", color.black);
addlabel(1, "range top " + top, color.yellow);
addlabel(1, "range bottom " + bot, color.yellow);
addlabel(1, " ", color.black);
addlabel(1, "Above" , ( if above_rng then color.red else color.gray));
addlabel(1, "Within range" , ( if within_rng then color.green else color.gray));
addlabel(1, "Below" , ( if below_rng then color.red else color.gray));
input show_range_lines = yes;
plot ztop = top;
ztop.SetDefaultColor(Color.gray);
ztop.SetHiding(!show_range_lines);
plot zbot = bot;
zbot.SetDefaultColor(Color.gray);
zbot.SetHiding(!show_range_lines);
#
```

chart set to day

when prices for the past 252 days are within the % high and low limits, the 'within range' label is green, and the above and below labels are gray.

when prices for the past 252 days are higher or lower than the % high and low limits, the 'within range' label is gray, and the above label and/or the below label are red.

---------------------------

test study - lower

Ruby:

```
# price_in_rng_past_xbars_lower_0
# test , lower study
# check if a stock has stayed within a price range, over a time period
# show a value of 1 if price stayed within the range, over the past x days
declare lower;
def na = double.nan;
def bn = barnumber();
input range_percent = 20.0;
# 1 year of data
input time_period = AggregationPeriod.day;
input quantity_of_time_periods = 252;
def rnghi = highest(high(period = time_period), quantity_of_time_periods);
def rnglo = lowest(low(period = time_period), quantity_of_time_periods);
def rng_first = (!isnan(close[-(quantity_of_time_periods-1)]) and isnan(close[-(quantity_of_time_periods-0)]));
#----------------------------------------
# save the current close price, on all bars
# https://usethinkscript.com/threads/current-price-line-indicator-for-thinkorswim.8793/
# Line At Price Mobius
# Alternative to using the HighestAll() function
def barsBack = 1000;
def c = if !IsNaN(close) and IsNaN(close[-1])
then close
else c[1];
def lastcls = if isNaN(close[-barsBack])
then c[-barsBack]
else Double.NaN;
input show_close_line = no;
plot line = if show_close_line then lastcls else na;
line.SetLineWeight(1);
line.SetDefaultColor(Color.LIME);
line.SetStyle(Curve.MEDIUM_DASH);
#-----------------------------------------
# compare current close price to price highest and lowest
def range1 = round(lastcls * range_percent/100, 2);
def top = round(lastcls + (range1/2),2);
def bot = round(lastcls - (range1/2),2);
#---------------------
## for testing in a lower study
#input months_back = 12;
#input agg1 = AggregationPeriod.month;
#def rnghi = highest(high(period = agg1), months_back);
#def rnglo = lowest(low(period = agg1), months_back);
#--------------------
# show a vertical line, x days back. start of data range
input show_vertical_line = yes;
addverticalline(show_vertical_line and rng_first," RANGE START", color.yellow);
# = 1 if price was out of range
def above_rng = (rnghi > top);
def below_rng = (rnglo < bot);
# = 1 if price stayed in the range
def within_rng = (!above_rng and !below_rng);
addlabel(1, "range : " + aspercent(range_percent/100), color.yellow);
addlabel(1, " of " + asdollars(close), color.yellow);
addlabel(1, "range : " + asdollars(range1), color.yellow);
addlabel(1, " ", color.black);
addlabel(1, " +- range : " + asdollars(range1/2), color.yellow);
addlabel(1, " ", color.black);
addlabel(1, "range top " + top, color.yellow);
addlabel(1, "range bottom " + bot, color.yellow);
addlabel(1, " ", color.black);
addlabel(1, "Above" , ( if above_rng then color.red else color.gray));
addlabel(1, "Within range" , ( if within_rng then color.green else color.gray));
addlabel(1, "Below" , ( if below_rng then color.red else color.gray));
input show_range_lines = no;
plot ztop = top;
ztop.SetDefaultColor(Color.gray);
ztop.SetHiding(!show_range_lines);
plot zbot = bot;
zbot.SetDefaultColor(Color.gray);
zbot.SetHiding(!show_range_lines);
plot z = within_rng;
z.SetDefaultColor(Color.cyan);
plot w = 0;
w.SetDefaultColor(Color.gray);
# count bars after being within the range
def after = if bn == 1 then 0 else if within_rng then 0 else after[1] + 1;
addlabel(1, after + " bars since being within range", color.yellow);
#
```

currently within range, 0 bars since

out of range, for past 101 bars

---------------------------

test study - column

zinrng_xbars

http://tos.mx/0g2CoLN
set time to day

shows x days since price was within a range, for a year

for some reason, the background colors didn't work, so i left them disabled. maybe i am overlooking something simple...

n/a's were green, others not colored.

Ruby:

```
# zinrng_xbars
# test , column study
# check if a stock has stayed within a price range, over a time period
# show a number of days since price stayed within the range, over the past 252 days
#declare lower;
def na = double.nan;
def bn = barnumber();
input range_percent = 20.0;
# 1 year of data , 252 days
input quantity_of_time_periods = 252;
def rnghi = highest(high, quantity_of_time_periods);
def rnglo = lowest(low, quantity_of_time_periods);
#def rng_first = (!isnan(close[-(quantity_of_time_periods-1)]) and isnan(close[-(quantity_of_time_periods-0)]));
#----------------------------------------
# save the current close price, on all bars
# https://usethinkscript.com/threads/current-price-line-indicator-for-thinkorswim.8793/
# Line At Price Mobius
# Alternative to using the HighestAll() function
#def barsBack = 1000;
#def c = if !IsNaN(close) and IsNaN(close[-1])
# then close
# else c[1];
#def lastcls3 = if isNaN(close[-barsBack])
# then c[-barsBack]
# else Double.NaN;
#input show_close_line = no;
#plot line = if show_close_line then lastcls else na;
#line.SetLineWeight(1);
#line.SetDefaultColor(Color.LIME);
#line.SetStyle(Curve.MEDIUM_DASH);
# wasn't sure if alt priceline code would work in a scan, so changed it to highestall()
def lastcls2 = highestall(if isnan(close[-1]) then close else 0);
def lastcls = if isnan(lastcls2) then 0 else lastcls2;
#-----------------------------------------
# compare current close price to price highest and lowest
def range1 = round(lastcls * range_percent/100, 2);
def top = round(lastcls + (range1/2),2);
def bot = round(lastcls - (range1/2),2);
#---------------------
## for testing in a lower study
#input months_back = 12;
#input agg1 = AggregationPeriod.month;
#def rnghi = highest(high(period = agg1), months_back);
#def rnglo = lowest(low(period = agg1), months_back);
#--------------------
# show a vertical line, x days back. start of data range
#input show_vertical_line = yes;
#addverticalline(show_vertical_line and rng_first," RANGE START", color.yellow);
# = 1 if price was out of range
def above_rng = (rnghi > top);
def below_rng = (rnglo < bot);
# = 1 if price stayed in the range
def within_rng = (!above_rng and !below_rng);
#addlabel(1, "range : " + aspercent(range_percent/100), color.yellow);
#addlabel(1, " of " + asdollars(close), color.yellow);
#addlabel(1, "range : " + asdollars(range1), color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, " +- range : " + asdollars(range1/2), color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, "range top " + top, color.yellow);
#addlabel(1, "range bottom " + bot, color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, "Above" , ( if above_rng then color.red else color.gray));
#ddlabel(1, "Within range" , ( if within_rng then color.green else color.gray));
#addlabel(1, "Below" , ( if below_rng then color.red else color.gray));
#input show_range_lines = no;
#plot ztop = top;
#ztop.SetDefaultColor(Color.gray);
#ztop.SetHiding(!show_range_lines);
#plot zbot = bot;
#zbot.SetDefaultColor(Color.gray);
#zbot.SetHiding(!show_range_lines);
#plot z = within_rng;
#z.SetDefaultColor(Color.cyan);
# count bars after being within the range
def after = if bn == 1 then 0 else if within_rng then 0 else after[1] + 1;
#addlabel(1, after + " bars since being within range", color.yellow);
plot z = after;
#AssignBackgroundColor(color.magenta);
#assignbackgroundcolor( if z < 1 then color.green else if z > 60 then color.yellow else color.magenta);
#
```

---------------------------

this is a modified lower study, untested

test study - scan

set time to day

finds when price has been within a range, for a year

Ruby:

```
# price_in_rng_past_xbars_lower_0_scan
# test , lower study-scan
# check if a stock has stayed within a price range, over a time period
# show a value of 1 if price stayed within the range, over the past x days
# set time period to day
declare lower;
def na = double.nan;
def bn = barnumber();
input range_percent = 20.0;
# 1 year of data
#input time_period = AggregationPeriod.day;
input quantity_of_time_periods = 252;
#def rnghi = highest(high(period = time_period), quantity_of_time_periods);
#def rnglo = lowest(low(period = time_period), quantity_of_time_periods);
def rnghi = highest(high, quantity_of_time_periods);
def rnglo = lowest(low, quantity_of_time_periods);
def rng_first = (!isnan(close[-(quantity_of_time_periods-1)]) and isnan(close[-(quantity_of_time_periods-0)]));
#----------------------------------------
# save the current close price, on all bars
# https://usethinkscript.com/threads/current-price-line-indicator-for-thinkorswim.8793/
# Line At Price Mobius
# Alternative to using the HighestAll() function
#def barsBack = 1000;
#def c = if !IsNaN(close) and IsNaN(close[-1])
# then close
# else c[1];
#def lastcls = if isNaN(close[-barsBack])
# then c[-barsBack]
# else Double.NaN;
#input show_close_line = no;
#plot line = if show_close_line then lastcls else na;
#line.SetLineWeight(1);
#line.SetDefaultColor(Color.LIME);
#line.SetStyle(Curve.MEDIUM_DASH);
# wasn't sure if alt priceline code would work in a scan, so changed it to highestall()
def lastcls = highestall(if isnan(close[-1]) then close else 0);
#-----------------------------------------
# compare current close price to price highest and lowest
def range1 = round(lastcls * range_percent/100, 2);
def top = round(lastcls + (range1/2),2);
def bot = round(lastcls - (range1/2),2);
#---------------------
## for testing in a lower study
#input months_back = 12;
#input agg1 = AggregationPeriod.month;
#def rnghi = highest(high(period = agg1), months_back);
#def rnglo = lowest(low(period = agg1), months_back);
#--------------------
# show a vertical line, x days back. start of data range
#input show_vertical_line = yes;
#addverticalline(show_vertical_line and rng_first," RANGE START", color.yellow);
# = 1 if price was out of range
def above_rng = (rnghi > top);
def below_rng = (rnglo < bot);
# = 1 if price stayed in the range
def within_rng = (!above_rng and !below_rng);
#addlabel(1, "range : " + aspercent(range_percent/100), color.yellow);
#addlabel(1, " of " + asdollars(close), color.yellow);
#addlabel(1, "range : " + asdollars(range1), color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, " +- range : " + asdollars(range1/2), color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, "range top " + top, color.yellow);
#addlabel(1, "range bottom " + bot, color.yellow);
#addlabel(1, " ", color.black);
#addlabel(1, "Above" , ( if above_rng then color.red else color.gray));
#addlabel(1, "Within range" , ( if within_rng then color.green else color.gray));
#addlabel(1, "Below" , ( if below_rng then color.red else color.gray));
#input show_range_lines = no;
#plot ztop = top;
#ztop.SetDefaultColor(Color.gray);
#ztop.SetHiding(!show_range_lines);
#plot zbot = bot;
#zbot.SetDefaultColor(Color.gray);
#zbot.SetHiding(!show_range_lines);
plot z = within_rng;
#z.SetDefaultColor(Color.cyan);
# count bars after being within the range
#def after = if bn == 1 then 0 else if within_rng then 0 else after[1] + 1;
#addlabel(1, after + " bars since being within range", color.yellow);
#
```