Financial Industry Regulatory Authority
Regulation SHO
Introduction
The FINRA Reg SHO Daily Short Sale Volume dataset by FINRA (Financial Industry Regulatory Authority) tracks off-exchange short sale volume for US Equities. The data covers around 9,000 US Equities per day, starts in August 2018, and is delivered on a daily frequency. This dataset is created by aggregating the daily short sale volume that broker-dealers report to FINRA's trade reporting facilities under Regulation SHO.
For more information about the Regulation SHO dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
FINRA (the Financial Industry Regulatory Authority) was created in 2007, when NASD merged with the member regulation arm of the New York Stock Exchange. Its mission is to protect investors and keep the markets honest. FINRA oversees nearly every broker-dealer that does business with the public in the United States, and it publishes the short sale and off-exchange trading data that those firms are required to report. This information is made freely available to researchers, investors, and anyone who wants a clearer view of market activity.
Getting Started
The following snippet demonstrates how to request data from the FINRA Reg SHO Daily Short Sale Volume dataset:
self.symbol = self.add_equity("AAPL", Resolution.DAILY).symbol
self.short_volume_symbol = self.add_data(FINRAShortSaleVolume, self.symbol).symbol _symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
_shortVolumeSymbol = AddData<FINRAShortSaleVolume>(_symbol).Symbol;
Requesting Data
To add FINRA Reg SHO Daily Short Sale Volume data to your algorithm, call the AddDataadd_data method. The dataset is linked to US Equities, so pass the equity Symbol you want short sale volume for. Save a reference to the dataset Symbol so you can access the data later in your algorithm.
class FINRAShortSaleVolumeDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2019, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
self.symbol = self.add_equity("AAPL", Resolution.DAILY).symbol
self.short_volume_symbol = self.add_data(FINRAShortSaleVolume, self.symbol).symbol public class FINRAShortSaleVolumeDataAlgorithm : QCAlgorithm
{
private Symbol _symbol, _shortVolumeSymbol;
public override void Initialize()
{
SetStartDate(2019, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
_symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
_shortVolumeSymbol = AddData<FINRAShortSaleVolume>(_symbol).Symbol;
}
}
Accessing Data
To get the current FINRA Reg SHO Daily Short Sale Volume data, index the current Slice with the dataset Symbol. Slice objects deliver unique events to your algorithm as they happen, but the Slice may not contain data for your dataset at every time step. To avoid issues, check if the Slice contains the data you want before you index it.
def on_data(self, slice: Slice) -> None:
if slice.contains_key(self.short_volume_symbol):
data_point = slice[self.short_volume_symbol]
self.log(f"{self.short_volume_symbol} short volume at {slice.time}: {data_point.short_volume}") public override void OnData(Slice slice)
{
if (slice.ContainsKey(_shortVolumeSymbol))
{
var dataPoint = slice[_shortVolumeSymbol];
Log($"{_shortVolumeSymbol} short volume at {slice.Time}: {dataPoint.ShortVolume}");
}
}
To iterate through all of the dataset objects in the current Slice, call the Get method.
def on_data(self, slice: Slice) -> None:
for dataset_symbol, data_point in slice.get(FINRAShortSaleVolume).items():
self.log(f"{dataset_symbol} short volume at {slice.time}: {data_point.short_volume}") public override void OnData(Slice slice)
{
foreach (var kvp in slice.Get<FINRAShortSaleVolume>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} short volume at {slice.Time}: {dataPoint.ShortVolume}");
}
}
Historical Data
To get historical FINRA Reg SHO Daily Short Sale Volume data, call the History method with the dataset Symbol. If there is no data in the period you request, the history result is empty.
# DataFrame history_df = self.history(self.short_volume_symbol, 100, Resolution.DAILY) # Dataset objects history_bars = self.history[FINRAShortSaleVolume](self.short_volume_symbol, 100, Resolution.DAILY)
var history = History<FINRAShortSaleVolume>(_shortVolumeSymbol, 100, Resolution.Daily);
For more information about historical data, see History Requests.
Universe Selection
To select a dynamic universe of US Equities based on FINRA Reg SHO Daily Short Sale Volume data, call the AddUniverseadd_universe method with the FINRAShortSaleVolumeUniverse class and a selection function.
def initialize(self) -> None:
self._universe = self.add_universe(FINRAShortSaleVolumeUniverse, self.universe_selection)
def universe_selection(self, alt_coarse: List[FINRAShortSaleVolumeUniverse]) -> List[Symbol]:
return [d.symbol for d in alt_coarse \
if d.short_volume_ratio is not None \
and d.short_volume_ratio > 0.5] private Universe _universe;
public override void Initialize()
{
_universe = AddUniverse<FINRAShortSaleVolumeUniverse>(altCoarse =>
{
return from d in altCoarse.OfType<FINRAShortSaleVolumeUniverse>()
where d.ShortVolumeRatio > 0.5m
select d.Symbol;
});
}
Remove Subscriptions
To remove a subscription, call the RemoveSecurity method.
self.remove_security(self.short_volume_symbol)
RemoveSecurity(_shortVolumeSymbol);
If you subscribe to FINRA Reg SHO Daily Short Sale Volume data for assets in a dynamic universe, remove the dataset subscription when the asset leaves your universe. To view a common design pattern, see Track Security Changes.
Example Applications
The FINRA Reg SHO Daily Short Sale Volume dataset lets you see how much of a stock's off-exchange flow is short selling. Examples include:
- Fading a stock once its short volume climbs to an unusually large share of total reported volume.
- Following persistent short pressure when the short volume ratio stays elevated for several days in a row.
- Ranking the universe by short volume ratio and rotating toward the most heavily shorted names.
- Watching short-exempt volume for early signs of aggressive selling around large orders and index events.
Classic Algorithm Example
The following example algorithm subscribes to FINRA Reg SHO Daily Short Sale Volume data for a US Equity and trades it on the daily short volume ratio:
from AlgorithmImports import *
class FINRAShortSaleVolumeDataAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
self.short_volume_symbol = self.add_data(FINRAShortSaleVolume, aapl).symbol
history = self.history(FINRAShortSaleVolume, self.short_volume_symbol, 60, Resolution.DAILY)
self.debug(f"We got {len(history)} items from our history request")
def on_data(self, slice):
points = slice.get(FINRAShortSaleVolume)
for point in points.values():
# Go short when off-exchange short volume is more than half of total reported volume
if point.short_volume_ratio is not None and point.short_volume_ratio > 0.5:
self.set_holdings(point.symbol.underlying, -1)
# Go long otherwise
else:
self.set_holdings(point.symbol.underlying, 1) public class FINRAShortSaleVolumeDataAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2019, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
var aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
var shortVolumeSymbol = AddData<FINRAShortSaleVolume>(aapl).Symbol;
var history = History<FINRAShortSaleVolume>(shortVolumeSymbol, 60, Resolution.Daily);
Debug($"We got {history.Count()} items from our history request");
}
public override void OnData(Slice slice)
{
var points = slice.Get<FINRAShortSaleVolume>();
foreach (var point in points.Values)
{
// Go short when off-exchange short volume is more than half of total reported volume
if (point.ShortVolumeRatio > 0.5m)
{
SetHoldings(point.Symbol.Underlying, -1);
}
// Go long otherwise
else
{
SetHoldings(point.Symbol.Underlying, 1);
}
}
}
}
Framework Algorithm Example
The following example algorithm uses the Algorithm Framework to trade a manually selected universe from FINRA Reg SHO Daily Short Sale Volume data. An alpha model subscribes each security to the dataset and emits insights from the daily short volume ratio:
from AlgorithmImports import *
class FINRAShortSaleVolumeFrameworkAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2019, 1, 1)
self.set_end_date(2020, 6, 1)
self.set_cash(100000)
self.universe_settings.resolution = Resolution.DAILY
symbols = [Symbol.create("AAPL", SecurityType.EQUITY, Market.USA)]
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.add_alpha(FINRAShortPressureAlphaModel())
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
class FINRAShortPressureAlphaModel(AlphaModel):
def __init__(self, threshold: float = 0.5) -> None:
self._threshold = threshold
self._symbols_by_security = {}
def update(self, algorithm: QCAlgorithm, data: Slice) -> List[Insight]:
insights = []
for security_symbol, short_volume_symbol in self._symbols_by_security.items():
if short_volume_symbol not in data:
continue
ratio = data[short_volume_symbol].short_volume_ratio
if ratio is None:
continue
direction = InsightDirection.DOWN if ratio > self._threshold else InsightDirection.UP
insights.append(Insight.price(security_symbol, timedelta(days=7), direction))
return insights
def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
for security in changes.added_securities:
symbol = security.symbol
short_volume_symbol = algorithm.add_data(FINRAShortSaleVolume, symbol).symbol
self._symbols_by_security[symbol] = short_volume_symbol
history = algorithm.history(FINRAShortSaleVolume, short_volume_symbol, 30, Resolution.DAILY)
algorithm.debug(f"Got {len(history)} historical short sale volume rows for {symbol}")
for security in changes.removed_securities:
short_volume_symbol = self._symbols_by_security.pop(security.symbol, None)
if short_volume_symbol is not None:
algorithm.remove_security(short_volume_symbol) public class FINRAShortSaleVolumeFrameworkAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2019, 1, 1);
SetEndDate(2020, 6, 1);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
var symbols = new[] { QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA) };
SetUniverseSelection(new ManualUniverseSelectionModel(symbols));
AddAlpha(new FINRAShortPressureAlphaModel());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
}
}
public class FINRAShortPressureAlphaModel : AlphaModel
{
private readonly decimal _threshold;
private readonly Dictionary<Symbol, Symbol> _symbolsBySecurity = new();
public FINRAShortPressureAlphaModel(decimal threshold = 0.5m)
{
_threshold = threshold;
}
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
foreach (var kvp in _symbolsBySecurity)
{
var securitySymbol = kvp.Key;
var shortVolumeSymbol = kvp.Value;
if (!data.ContainsKey(shortVolumeSymbol))
{
continue;
}
var ratio = data.Get<FINRAShortSaleVolume>(shortVolumeSymbol).ShortVolumeRatio;
if (ratio == null)
{
continue;
}
var direction = ratio > _threshold ? InsightDirection.Down : InsightDirection.Up;
yield return Insight.Price(securitySymbol, TimeSpan.FromDays(7), direction);
}
}
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
var symbol = security.Symbol;
var shortVolumeSymbol = algorithm.AddData<FINRAShortSaleVolume>(symbol).Symbol;
_symbolsBySecurity[symbol] = shortVolumeSymbol;
var history = algorithm.History<FINRAShortSaleVolume>(shortVolumeSymbol, 30, Resolution.Daily);
algorithm.Debug($"Got {history.Count()} historical short sale volume rows for {symbol}");
}
foreach (var security in changes.RemovedSecurities)
{
if (_symbolsBySecurity.Remove(security.Symbol, out var shortVolumeSymbol))
{
algorithm.RemoveSecurity(shortVolumeSymbol);
}
}
}
}
Data Point Attributes
The FINRA Reg SHO Daily Short Sale Volume dataset provides FINRAShortSaleVolume and FINRAShortSaleVolumeUniverse objects.
FINRAShortSaleVolume
FINRAShortSaleVolume objects have the following attributes:
FINRAShortSaleVolumeUniverse
FINRAShortSaleVolumeUniverse objects have the following attributes: