First, if you don't know what RVOL is: it is used to compare the accumulated volume at a specific point in time
on the day of interest to the average acuumulated volume by the same point over the past 20 days.
Why not just make an SMA of volume and compare your current volume to that?
RVOL indicates changes in participation minute by minute. For example: Comparing 5 minutes into the
open when accumulated volume is 100K on an average day, and on the day in question it is 1M (RVOL would be 10).
RVOL would give you a better idea of a change in the participation character of the stock in question.
If the stock normally does 5M in average volume in a day, the 900K change by that minute might not be picked up
as significant with an SMA because it is still below average. By the end of the day, volume might be 15M or 20M.
For any given minute in a day, if RVOL is elevated (>1.0 means higher than average, >3.0 means there is an
abnormal amount of volume), the stock is probably being traded by more participants, which in turn
might cause it to move more than average (up or down), or range aggressively.
SMB capital, a propietary trading firm located in uptown Manhattan, who's main speaker is Mike Bellafiore, author of
the trading classics "one good trade" and "the playbook", uses the term "in play" to describe this...
(if you have ever watched more than one of their videos on youtube, you'll understand that last sentence, it wasn't
an advertising plug for them). When combined with a catalyst like news, its a good benchmark to let you know the
stock is something you should spend time watching due to the increased volitility that usually comes with the
Summary of the code:
-Create a relative volume calculation based on a symbol and date of interest as inputs. Relative volume
needs to be instantiated as an object first, and then .calculate(date, "symbol") is called on that object
(see the bottom 3 lines for an example)
-Look back through 20 days of minute data
-Make a rolling sum of the volume data minute to minute (accumulated the volume)
-Make a dataframe of the accumulated volumes over the 20 days, the last column to be made
(the day we are interested in) is removed from the dataframe and appended to the output dataframe as 'vol_today'.
-Average accumulated volumes for each row (row = minute() from timestamps), add into a new
column using [dataframe].mean(axis = 1).
-Remove and appended the column to the output RVOL dataframe as 'avg_vol'.
-Once that is done, vol_today is divided by avg_vol to give RVOL by minute
-You could change the code to only copy the 'vol_today' if you wanted to continue to roll data into the
dataframe over more than 1 day.
-I am no expert programmer. There are likely issues with the code, I have run into a few. This is the cleanest
most elegant code I could hack together to make this indicator, sorry. You are welcome to do what you want
with it and share it. I will change it as I come across issues, but it is up to you to adapt it to your needs.
***updated to be able to use on local jupyter notebook with something like a .csv file, got rid of NaN values by
filling in all values with the rolling sums, added a line to be able to use pre/post market, convert times from
UTC to Eastern US
import numpy as np
import pandas as pd
from datetime import time, timedelta, date
self.rvol = 
#use this function to create dates from timestamps
timer = pd.to_datetime(timestamp, unit ='s') - pd.Timedelta(hours = 5)
def calculate(self, tdate, symb):
self.separated_dates =  #timestamp date handler
self.separated_times =  #timestamp time handler
#20 trading days is standard for RVOL calculation, which is 4 - 5 day weeks when you remove weekends
self.trading_date = tdate #trading date we are interested in, last day in data
self.rvol_start = self.trading_date - timedelta(days=20) #rvol starting date
self.symb = symb #symbol that is passed in
#get minute pricing for the symbol and dates of interest
pricing = get_pricing(self.symb, start_date=str(self.rvol_start), end_date=str(self.trading_date), frequency='minute')
#converts UTC to US/Eastern
pricing.index = pricing.index - pd.Timedelta(hours=5)
#-----separate dates and times from timestamps into their own arrays----------#
for x in pricing.index:
mydates = x.date()
if mydates not in self.separated_dates:
#uncomment the below line if you are using data with pre/post market
#time_range = pd.date_range(start = str(self.separated_dates[-1]) + ' ' + str(time(4,0)), end = str(self.separated_dates[-1]) + ' ' + str(time(20,0)), freq = "T")
time_range = pd.date_range(start = str(self.separated_dates[-1]) + ' ' + str(time(9,30)), end = str(self.separated_dates[-1]) + ' ' + str(time(16,0)), freq = "T")
for y in time_range:
mytimes = y.time()
if mytimes not in self.separated_times:
#-----Rolling volume accumulation--------------------------------------------#
#initialize dataframe used to store accumulated volume values
#if the day used to build this is a holiday, you may have an issue FYI, I didnt test
rvol_helper = pd.DataFrame(index = self.separated_times, columns = self.separated_dates)
#make a rolling sum of the volume by minute for each date
for x in range(len(self.separated_dates)):
currDate = self.separated_dates[x] #date we are working on
minutes =  #helper array
rvolx = 0 #used to accumulate volume
#loop through the pricing data, sum the volumes for the specific day
#append to the minutes array, which is then used to build the rvol_helper day by day
for y in range(len(self.separated_times)):
time_now = self.separated_times[y]
date_check = pd.Timestamp.combine(currDate, time_now)
rvolInt = pricing['volume'].loc[date_check]
rvolInt = rvolx
rvolx += rvolInt
rvol_helper.at[time_now, currDate] = rvolx
#------Parse data to dataframes for handling--------------------------------#
#create RVOL dataframe
self.rvol = pd.DataFrame(index = self.separated_times, columns = ('vol_today', 'avg_vol', 'rvol'))
#remove day of interest volume, add to RVOL dataframe as 'vol_today'
self.rvol['vol_today'] = rvol_helper.pop(rvol_helper.columns[-1])
#average the 19 days of volume by minute (across rows)
rvol_helper['avg_vol'] = rvol_helper.mean(axis=1)
#remove the column of average volumes per minute, append to RVOL as 'avg_vol'
self.rvol['avg_vol'] = rvol_helper.pop(rvol_helper.columns[-1])
#calculate RVOL for the day using today volume and average volume
for x in self.rvol.index:
volTod = self.rvol['vol_today'].loc[x]
avgVol = self.rvol['avg_vol'].loc[x]
if volTod > 0 and avgVol > 0:
self.rvol.at[x, 'rvol'] = volTod/avgVol
self.rvol.at[x, 'rvol'] = 0
#---------variables to call the class/method------------#
dadate = date(2019,12,24)
dajob = RelativeVolume()
outputs = dajob.calculate(dadate, 'MBOT') #good symbol and date example to see why massive RVOL matters