US Equity
Shorting
Introduction
In a regular long position, you buy shares and then sell them later to close the trade. In a short position, you borrow shares, sell them, and then buy the shares back later to close the trade. Short positions let you profit when a security's price decreases while you have the position open, but there are risks involved. For example, you can get a margin call and you may incur borrowing costs.
Short Availability
To short sell, you need to borrow the shares from another investor or your brokerage. The short availability is the number of shares available for you to borrow. If you don't model the short availability in backtests, you could have a strategy that performs well in backtesting but doesn't trade in live mode because the shares aren't available to borrow. If you assume your short orders are successful in backtests, your backtesting results likely won't represent the true performance of actually trading the strategy during the backtest period. To get the short availability in your algorithms, use the US Equities Short Availability dataset. For more information about this topic, see Short Availability.
Borrowing Costs
In live trading, you usually pay a fee to borrow shares that you use to open a short position. Part of the fee goes to the investor that provided you with the opportunity to short with their shares. The borrowing rate is set by your live brokerage. In backtests, LEAN doesn't currently model borrowing costs, but we have an open GitHub Issue to add the functionality. Subscribe to GitHub Issue #4563 to track the feature progress.
Examples
The following examples demonstrate some common practices for shorting US Equities:
Example 1: Shorting Penny Stock Top Gainers
The following algorithm adds a universe of penny stocks that have experienced the greatest upward momentum over the last two trading days. For each asset in the universe, it monitors an hourly Bollinger Bands indicator. When an asset breaks above the upper band, it shorts the asset. It exits each position when the asset drops 10% from the entry price or when the price reverts back below the middle Bollinger Band.
using QuantConnect.Data.Shortable; public class USEquityShortingExampleAlgorithm : QCAlgorithm { private Dictionary<Symbol, RateOfChange> _rocBySymbol = new(); private Dictionary<BollingerBands, Security> _securityByBB = new(); public override void Initialize() { SetStartDate(2021, 1, 1); SetEndDate(2022, 1, 1); // Enable automatic indicator warm-up. Settings.AutomaticIndicatorWarmUp = true; // Add a universe of low-cap penny stocks that have high upward momentum. AddUniverse(SelectAssets); } private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamentals) { // Select all the penny stocks that have a momentum score. var pennyStocks = new List<Symbol>(); foreach (var f in fundamentals) { // Create a RateOfChange indicator for each asset. if (!_rocBySymbol.ContainsKey(f.Symbol)) { _rocBySymbol[f.Symbol] = new RateOfChange(1); } // Update the indicator and ensure it's ready. if (!_rocBySymbol[f.Symbol].Update(f.EndTime, f.Price)) { continue; } // Filter out assets above $5M market cap or $1 share price. if (f.MarketCap > 5e6 || f.Price > 1) { continue; } pennyStocks.Add(f.Symbol); } // Select the 20 penny stocks with the greatest momentum. return pennyStocks.OrderByDescending(symbol => _rocBySymbol[symbol].Current.Value).Take(20); } public override void OnSecuritiesChanged(SecurityChanges changes) { // Iterate through the assets that entered the universe. foreach (var security in changes.AddedSecurities) { // Set the shortable provider of the asset to ensure realistic short availability. security.SetShortableProvider(new InteractiveBrokersShortableProvider()); // Create a BollingerBand indicator for this asset. var bb = BB(security.Symbol, 20, 2, resolution: Resolution.Hour); _securityByBB[bb] = security; // Attach an event handler to the indicator to track its updates. bb.Updated += OnBBUpdated; (security as dynamic).Bbands = bb; } // Liquidate and remove the indicator updates for assets that leave the universe. foreach (var security in changes.RemovedSecurities) { var bb = (security as dynamic).Bbands; DeregisterIndicator(bb); _securityByBB.Remove(bb); Liquidate(security.Symbol); } } private void OnBBUpdated(object indicator, IndicatorDataPoint _) { var bb = (indicator as BollingerBands); // When the indicator updates, ensure it's ready. if (!bb.IsReady) { return; } var security = _securityByBB[bb]; // Short the asset if the price is above the upper band. if (!security.Holdings.Invested && security.Price > bb.UpperBand.Current.Value) { // Get the number of shares avaiable for shorting. var shortableQuantity = security.ShortableProvider.ShortableQuantity(security.Symbol, Time); if (shortableQuantity == null) { return; } // Calculate the target quantity. var quantity = Math.Max(CalculateOrderQuantity(security.Symbol, -0.1m), -(int)shortableQuantity); if (quantity != 0) { // Short the asset. MarketOrder(security.Symbol, quantity); // Place a take-profit order 10% below the current price. LimitOrder(security.Symbol, -quantity, 0.9m * security.BidPrice); } } // If the limit order isn't hit before the price drops below the middle band, // exit the position and cancel the limit order. else if (security.Holdings.Invested && security.Price < bb.MiddleBand.Current.Value) { Liquidate(security.Symbol); } } }
class USEquityShortingExampleAlgorithm(QCAlgorithm): _roc_by_symbol = {} _security_by_bb = {} def initialize(self) -> None: self.set_start_date(2021, 1, 1) self.set_end_date(2022, 1, 1) # Enable automatic indicator warm-up. self.settings.automatic_indicator_warm_up = True # Add a universe of low-cap penny stocks that have high upward momentum. self.add_universe(self._select_assets) def _select_assets(self, fundamentals: List[Fundamental]) -> List[Symbol]: # Select all the penny stocks that have a momentum score. penny_stocks = [] for f in fundamentals: # Create a RateOfChange indicator for each asset. if f.symbol not in self._roc_by_symbol: self._roc_by_symbol[f.symbol] = RateOfChange(1) # Update the indicator and ensure it's ready. if not self._roc_by_symbol[f.symbol].update(f.end_time, f.price): continue # Filter out assets above $5M market cap or $1 share price. if f.market_cap > 5e6 or f.price > 1: continue penny_stocks.append(f.symbol) # Select the 20 penny stocks with the greatest momentum. return sorted(penny_stocks, key=lambda symbol: self._roc_by_symbol[symbol].current.value)[-20:] def on_securities_changed(self, changes: SecurityChanges) -> None: # Iterate through the assets that entered the universe. for security in changes.added_securities: # Set the shortable provider of the asset to ensure realistic short availability. security.set_shortable_provider(InteractiveBrokersShortableProvider()) # Create a BollingerBand indicator for this asset. security.bbands = self.bb(security.symbol, 20, 2, resolution=Resolution.HOUR) self._security_by_bb[security.bbands] = security # Attach an event handler to the indicator to track its updates. security.bbands.updated += self.on_bb_updated # Liquidate and remove the indicator updates for assets that leave the universe. for security in changes.removed_securities: self.deregister_indicator(security.bbands) self._security_by_bb.pop(security.bbands, None) self.liquidate(security.symbol) def on_bb_updated(self, bb: object, _: IndicatorDataPoint) -> None: # When the indicator updates, ensure it's ready. if not bb.is_ready: return security = self._security_by_bb[bb] # Short the asset if the price is above the upper band. if not security.holdings.invested and security.price > bb.upper_band.current.value: # Get the number of shares avaiable for shorting. shortable_quantity = security.shortable_provider.shortable_quantity(security.symbol, self.time) if not shortable_quantity: return # Calculate the target quantity. quantity = max(self.calculate_order_quantity(security.symbol, -0.1), -shortable_quantity) if quantity: # Short the asset. self.market_order(security.symbol, quantity) # Place a take-profit order 10% below the current price. self.limit_order(security.symbol, -quantity, 0.9 * security.bid_price) # If the limit order isn't hit before the price drops below the middle band, # exit the position and cancel the limit order. elif security.holdings.invested and security.price < bb.middle_band.current.value: self.liquidate(security.symbol)