Financial Industry Regulatory Authority
Regulation SHO
Introduction
The Regulation SHO dataset by the Financial Industry Regulatory Authority (FINRA) tracks the daily off-exchange short sale volume of US Equities. The data covers 22,073 US Equities, starts in August 2018, and is delivered on a daily frequency. This dataset is created by processing the short sale volume that broker-dealers report to FINRA under Regulation SHO.
For more information about the Regulation SHO dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
The Financial Industry Regulatory Authority (FINRA) is a not-for-profit organization that oversees broker-dealers in the United States, under the supervision of the SEC. It was formed in 2007 when NASD merged with the member regulation arm of the New York Stock Exchange. Its mission is to protect investors and safeguard market integrity.
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. 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 Getget 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 Historyhistory 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;
});
}
For more information about dynamic universes, see Universes.
Remove Subscriptions
To remove a subscription, call the RemoveSecurityremove_security 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 Regulation SHO dataset enables you to monitor short selling in off-exchange trading. Examples include the following strategies:
- Selling stocks when short volume becomes an unusually large share of their total volume
- Ranking stocks by the share of their volume that is sold short
- Following stocks where short selling stays high for several days in a row
Classic Algorithm Example
The following example algorithm short sells AAPL when its short volume ratio is above 0.5. Otherwise, it buys AAPL.
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 emits a down insight for AAPL when its short volume ratio is above 0.5. Otherwise, it emits an up insight.
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: