diff --git a/03 Writing Algorithms/03 Securities/99 Asset Classes/01 US Equity/07 Shorting/99 Examples.html b/03 Writing Algorithms/03 Securities/99 Asset Classes/01 US Equity/07 Shorting/99 Examples.html new file mode 100644 index 0000000000..0da10875e5 --- /dev/null +++ b/03 Writing Algorithms/03 Securities/99 Asset Classes/01 US Equity/07 Shorting/99 Examples.html @@ -0,0 +1,127 @@ +

Example 1: Short Penny Stock Gainers

+

This example builds a universe of low-cap penny stocks (below $1) that showed the greatest gain on the previous day. It uses an intraday Bollinger Band indicator to time entry and exit.

+
+
// Dictionary to cache last price for gain calculation
+private Dictionary<Symbol, decimal> _symbolLastPrices = new();
+
+public override void Initialize()
+{
+    // Add universe filtering function to obtain the low-cap penny stocks top gainers
+    AddUniverse(Selection)
+}
+
+private IEnumerable<Symbol> Selection(IEnumerable<Fundamental> fundamental)
+{
+    // Select the small-cap penny stocks with a price less than $1.
+    var filtered = fundamental.Where(x => x.MarketCap <= 5e6 && x.Price <= 1).ToList();
+    
+    var pctGain = new Dictionary<Symbol, decimal>();
+    // Iterate filtered stocks to calculate best gainers
+    foreach (var f in filtered)
+    {
+        // If not in dictionary, save last price and exit
+        if (!_symbolLastPrices.TryGetValue(f.Symbol, out var lastPrice))
+        {
+            lastPrice = _symbolLastPrices[f.Symbol] = f.Price;
+            continue;
+        }
+
+        // Cache the 1-day percentage change
+        var pctChg = (f.Price - lastPrice) / lastPrice;
+        pctGain.Add(f.Symbol, pctChg);
+
+        // Update last price
+        _symbolLastPrices[f.Symbol] = f.Price;
+    }
+
+    // Return Symbols of the top 20 gainers
+    return pctGain.OrderByDescending(x => x.Value)
+        .Take(20)
+        .Select(x => x.Key);
+}
+
+public override void OnSecuritiesChanged(SecurityChanges changes)
+{
+    // Iterate new universe members to setup the indicators
+    foreach (var added in changes.AddedSecurities)
+    {
+        var security = added as dynamic;
+        // Create an automatically updated BollingerBand indicator
+        security.BBands = BB(added.Symbol, 20, Resolution.Minute);
+        // Add a handler for trading
+        security.BBands.Updated += OnBBUpdate;
+    }
+}
+
+private void OnBBUpdate(object sender, IndicatorDataPoint point)
+{
+    var price = Securities[point.Symbol].Price;
+    // Check if the stock is invested
+    if (!Portfolio[point.Symbol].Invested)
+    {
+        // Trade if short sell criteria met
+        if (price > sender.UpperBand.Current.Value)
+        {
+            SetHoldings(point.Symbol, -0.05m);
+        }
+    }
+    else
+    {
+        // Exit if invested and price drop below middle band
+        if (price < sender.MiddleBand.Current.Value)
+        {
+            Liquidate(point.Symbol)
+        }
+    }
+}
+
def initialize(self) -> None:
+    self.symbol_last_prices = None
+    # Add universe filtering function to obtain the low-cap penny stocks top gainers
+    self.add_universe(self.selection)
+
+def selection(self, fundamental: List[Fundamental]) -> List[Symbol]:
+    symbols = []
+    market_caps = []
+    prices = []
+    # Iterate stocks to get market cap and price
+    for f in fundamental:
+        symbols.append(f.symbol)
+        market_caps.append(f.market_cap)
+        prices.append(f.price)
+    df = pd.DataFrame({"Symbol": symbols, "MarketCap": market_caps, "Price": prices}).set_index("Symbol")
+    
+    # Filter the stocks
+    df = df[(df["MarketCap"] <= 5e6) & (df["Price"] <= 1)]
+
+    # Calculate the percentage change
+    top_gainers = []
+    if self.symbol_last_prices:
+        pct_chg = (df["Price"] - self.symbol_last_prices["Price"]) / self.symbol_last_prices["Price"]
+        # Get the top 20 gainers
+        top_gainers = list(pct_chg.nlargest(20).index)
+
+    # Update last price
+    self.symbol_last_prices = df
+    
+    return top_gainers
+	
+def on_securities_changed(self, changes: SecurityChanges) -> None:
+    # Iterate new members to create an automatically updated BollingerBand indicator
+    for added in changes.added_securities:
+        # Create a BollingerBand indicator
+        added._bbands = self.bb(added.symbol, 20, Resolution.MINUTE)
+        # Add a handler for trading
+        added._bbands.updated += self.on_bb_updated
+
+def on_bb_updated(self, sender: object, point: IndicatorDataPoint) -> None:
+    price = self.securities[point.symbol].price
+    # Check if the stock is invested
+    if not self.portfolio[point.symbol].invested:
+        # Trade if short sell criteria met
+        if price < sender.upper_band.current.value:
+            self.set_holdings(point.symbol, -0.05)
+    else:
+        # Exit if invested and price drop below middle band
+        price < sender.middle_band.current.value:
+            self.liquidate(point.symbol)
+